mirror of
https://github.com/umami-software/umami.git
synced 2026-02-18 19:45:35 +01:00
save ip, lat, lng of session
This commit is contained in:
parent
8b108d3aae
commit
d0fb1dd4dd
10 changed files with 101 additions and 12 deletions
4
db/clickhouse/migrations/04_ip_lat_lng.sql
Normal file
4
db/clickhouse/migrations/04_ip_lat_lng.sql
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "session" ADD COLUMN "ip" VARCHAR(40),
|
||||||
|
ADD COLUMN "lat" FLOAT,
|
||||||
|
ADD COLUMN "lng" FLOAT;
|
||||||
|
|
@ -16,6 +16,9 @@ CREATE TABLE umami.website_event
|
||||||
subdivision1 LowCardinality(String),
|
subdivision1 LowCardinality(String),
|
||||||
subdivision2 LowCardinality(String),
|
subdivision2 LowCardinality(String),
|
||||||
city String,
|
city String,
|
||||||
|
ip Nullable(String),
|
||||||
|
lat Nullable(Float32),
|
||||||
|
lng Nullable(Float32),
|
||||||
--pageviews
|
--pageviews
|
||||||
url_path String,
|
url_path String,
|
||||||
url_query String,
|
url_query String,
|
||||||
|
|
|
||||||
9
db/mysql/migrations/07_ip_lat_lng/migration.sql
Normal file
9
db/mysql/migrations/07_ip_lat_lng/migration.sql
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "session" ADD COLUMN "ip" VARCHAR(255),
|
||||||
|
ADD COLUMN "lat" FLOAT,
|
||||||
|
ADD COLUMN "lng" FLOAT;
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX `session_website_id_created_at_ip_idx` ON `session`(`website_id`, `created_at`, `ip`);
|
||||||
|
CREATE INDEX `session_website_id_created_at_lat_idx` ON `session`(`website_id`, `created_at`, `lat`);
|
||||||
|
CREATE INDEX `session_website_id_created_at_lng_idx` ON `session`(`website_id`, `created_at`, `lng`);
|
||||||
|
|
@ -40,6 +40,9 @@ model Session {
|
||||||
subdivision1 String? @db.Char(20)
|
subdivision1 String? @db.Char(20)
|
||||||
subdivision2 String? @db.VarChar(50)
|
subdivision2 String? @db.VarChar(50)
|
||||||
city String? @db.VarChar(50)
|
city String? @db.VarChar(50)
|
||||||
|
ip String? @db.VarChar(40)
|
||||||
|
lat Float?
|
||||||
|
lng Float?
|
||||||
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0)
|
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0)
|
||||||
|
|
||||||
websiteEvent WebsiteEvent[]
|
websiteEvent WebsiteEvent[]
|
||||||
|
|
@ -57,6 +60,9 @@ model Session {
|
||||||
@@index([websiteId, createdAt, country])
|
@@index([websiteId, createdAt, country])
|
||||||
@@index([websiteId, createdAt, subdivision1])
|
@@index([websiteId, createdAt, subdivision1])
|
||||||
@@index([websiteId, createdAt, city])
|
@@index([websiteId, createdAt, city])
|
||||||
|
@@index([websiteId, createdAt, ip])
|
||||||
|
@@index([websiteId, createdAt, lat])
|
||||||
|
@@index([websiteId, createdAt, lng])
|
||||||
@@map("session")
|
@@map("session")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
9
db/postgresql/migrations/07_ip_lat_lng/migration.sql
Normal file
9
db/postgresql/migrations/07_ip_lat_lng/migration.sql
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "session" ADD COLUMN "ip" VARCHAR(40),
|
||||||
|
ADD COLUMN "lat" FLOAT,
|
||||||
|
ADD COLUMN "lng" FLOAT;
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "session_website_id_created_at_ip_idx" ON "session"("website_id", "created_at", "ip");
|
||||||
|
CREATE INDEX "session_website_id_created_at_lat_idx" ON "session"("website_id", "created_at", "lat");
|
||||||
|
CREATE INDEX "session_website_id_created_at_lng_idx" ON "session"("website_id", "created_at", "lng");
|
||||||
|
|
@ -40,6 +40,9 @@ model Session {
|
||||||
subdivision1 String? @db.VarChar(20)
|
subdivision1 String? @db.VarChar(20)
|
||||||
subdivision2 String? @db.VarChar(50)
|
subdivision2 String? @db.VarChar(50)
|
||||||
city String? @db.VarChar(50)
|
city String? @db.VarChar(50)
|
||||||
|
ip String? @db.VarChar(40)
|
||||||
|
lat Float?
|
||||||
|
lng Float?
|
||||||
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6)
|
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6)
|
||||||
|
|
||||||
websiteEvent WebsiteEvent[]
|
websiteEvent WebsiteEvent[]
|
||||||
|
|
@ -57,6 +60,9 @@ model Session {
|
||||||
@@index([websiteId, createdAt, country])
|
@@index([websiteId, createdAt, country])
|
||||||
@@index([websiteId, createdAt, subdivision1])
|
@@index([websiteId, createdAt, subdivision1])
|
||||||
@@index([websiteId, createdAt, city])
|
@@index([websiteId, createdAt, city])
|
||||||
|
@@index([websiteId, createdAt, ip])
|
||||||
|
@@index([websiteId, createdAt, lat])
|
||||||
|
@@index([websiteId, createdAt, lng])
|
||||||
@@map("session")
|
@@map("session")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -84,11 +84,15 @@ export async function getLocation(ip: string, req: NextApiRequestCollect) {
|
||||||
const country = safeDecodeURIComponent(req.headers['cf-ipcountry']);
|
const country = safeDecodeURIComponent(req.headers['cf-ipcountry']);
|
||||||
const subdivision1 = safeDecodeURIComponent(req.headers['cf-region-code']);
|
const subdivision1 = safeDecodeURIComponent(req.headers['cf-region-code']);
|
||||||
const city = safeDecodeURIComponent(req.headers['cf-ipcity']);
|
const city = safeDecodeURIComponent(req.headers['cf-ipcity']);
|
||||||
|
const lat = safeDecodeURIComponent(req.headers['cf-iplatitude']);
|
||||||
|
const lng = safeDecodeURIComponent(req.headers['cf-iplongitude']);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
country,
|
country,
|
||||||
subdivision1: getRegionCode(country, subdivision1),
|
subdivision1: getRegionCode(country, subdivision1),
|
||||||
city,
|
city,
|
||||||
|
lat: parseFloat(lat),
|
||||||
|
lng: parseFloat(lng),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -99,11 +103,15 @@ export async function getLocation(ip: string, req: NextApiRequestCollect) {
|
||||||
const country = safeDecodeURIComponent(req.headers['x-vercel-ip-country']);
|
const country = safeDecodeURIComponent(req.headers['x-vercel-ip-country']);
|
||||||
const subdivision1 = safeDecodeURIComponent(req.headers['x-vercel-ip-country-region']);
|
const subdivision1 = safeDecodeURIComponent(req.headers['x-vercel-ip-country-region']);
|
||||||
const city = safeDecodeURIComponent(req.headers['x-vercel-ip-city']);
|
const city = safeDecodeURIComponent(req.headers['x-vercel-ip-city']);
|
||||||
|
const lat = safeDecodeURIComponent(req.headers['x-vercel-ip-latitude']);
|
||||||
|
const lng = safeDecodeURIComponent(req.headers['x-vercel-ip-longitude']);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
country,
|
country,
|
||||||
subdivision1: getRegionCode(country, subdivision1),
|
subdivision1: getRegionCode(country, subdivision1),
|
||||||
city,
|
city,
|
||||||
|
lat: parseFloat(lat),
|
||||||
|
lng: parseFloat(lng),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -121,12 +129,16 @@ export async function getLocation(ip: string, req: NextApiRequestCollect) {
|
||||||
const subdivision1 = result.subdivisions?.[0]?.iso_code;
|
const subdivision1 = result.subdivisions?.[0]?.iso_code;
|
||||||
const subdivision2 = result.subdivisions?.[1]?.names?.en;
|
const subdivision2 = result.subdivisions?.[1]?.names?.en;
|
||||||
const city = result.city?.names?.en;
|
const city = result.city?.names?.en;
|
||||||
|
const lat = result.location?.latitude;
|
||||||
|
const lng = result.location?.longitude;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
country,
|
country,
|
||||||
subdivision1: getRegionCode(country, subdivision1),
|
subdivision1: getRegionCode(country, subdivision1),
|
||||||
subdivision2,
|
subdivision2,
|
||||||
city,
|
city,
|
||||||
|
lat,
|
||||||
|
lng,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -141,9 +153,23 @@ export async function getClientInfo(req: NextApiRequestCollect) {
|
||||||
const subdivision1 = location?.subdivision1;
|
const subdivision1 = location?.subdivision1;
|
||||||
const subdivision2 = location?.subdivision2;
|
const subdivision2 = location?.subdivision2;
|
||||||
const city = location?.city;
|
const city = location?.city;
|
||||||
|
const lat = location?.lat;
|
||||||
|
const lng = location?.lng;
|
||||||
const browser = browserName(userAgent);
|
const browser = browserName(userAgent);
|
||||||
const os = detectOS(userAgent) as string;
|
const os = detectOS(userAgent) as string;
|
||||||
const device = getDevice(req.body?.payload?.screen, os);
|
const device = getDevice(req.body?.payload?.screen, os);
|
||||||
|
|
||||||
return { userAgent, browser, os, ip, country, subdivision1, subdivision2, city, device };
|
return {
|
||||||
|
userAgent,
|
||||||
|
browser,
|
||||||
|
os,
|
||||||
|
ip,
|
||||||
|
country,
|
||||||
|
subdivision1,
|
||||||
|
subdivision2,
|
||||||
|
city,
|
||||||
|
device,
|
||||||
|
lat,
|
||||||
|
lng,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,19 @@ export async function getSession(req: NextApiRequestCollect): Promise<SessionDat
|
||||||
throw new Error(`Website not found: ${websiteId}.`);
|
throw new Error(`Website not found: ${websiteId}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { userAgent, browser, os, ip, country, subdivision1, subdivision2, city, device } =
|
const {
|
||||||
await getClientInfo(req);
|
userAgent,
|
||||||
|
browser,
|
||||||
|
os,
|
||||||
|
ip,
|
||||||
|
country,
|
||||||
|
subdivision1,
|
||||||
|
subdivision2,
|
||||||
|
city,
|
||||||
|
device,
|
||||||
|
lat,
|
||||||
|
lng,
|
||||||
|
} = await getClientInfo(req);
|
||||||
|
|
||||||
const sessionId = uuid(websiteId, hostname, ip, userAgent);
|
const sessionId = uuid(websiteId, hostname, ip, userAgent);
|
||||||
const visitId = uuid(sessionId, visitSalt());
|
const visitId = uuid(sessionId, visitSalt());
|
||||||
|
|
@ -58,6 +69,9 @@ export async function getSession(req: NextApiRequestCollect): Promise<SessionDat
|
||||||
subdivision1,
|
subdivision1,
|
||||||
subdivision2,
|
subdivision2,
|
||||||
city,
|
city,
|
||||||
|
ip,
|
||||||
|
lat,
|
||||||
|
lng,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -80,6 +94,9 @@ export async function getSession(req: NextApiRequestCollect): Promise<SessionDat
|
||||||
subdivision1,
|
subdivision1,
|
||||||
subdivision2,
|
subdivision2,
|
||||||
city,
|
city,
|
||||||
|
ip,
|
||||||
|
lat,
|
||||||
|
lng,
|
||||||
});
|
});
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
if (!e.message.toLowerCase().includes('unique constraint')) {
|
if (!e.message.toLowerCase().includes('unique constraint')) {
|
||||||
|
|
|
||||||
|
|
@ -220,4 +220,7 @@ export interface SessionData {
|
||||||
subdivision1: string;
|
subdivision1: string;
|
||||||
subdivision2: string;
|
subdivision2: string;
|
||||||
city: string;
|
city: string;
|
||||||
|
ip: string;
|
||||||
|
lat: number;
|
||||||
|
lng: number;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,9 @@ export async function createSession(data: Prisma.SessionCreateInput) {
|
||||||
subdivision1,
|
subdivision1,
|
||||||
subdivision2,
|
subdivision2,
|
||||||
city,
|
city,
|
||||||
|
ip,
|
||||||
|
lat,
|
||||||
|
lng,
|
||||||
} = data;
|
} = data;
|
||||||
|
|
||||||
return prisma.client.session.create({
|
return prisma.client.session.create({
|
||||||
|
|
@ -31,6 +34,9 @@ export async function createSession(data: Prisma.SessionCreateInput) {
|
||||||
subdivision1,
|
subdivision1,
|
||||||
subdivision2,
|
subdivision2,
|
||||||
city,
|
city,
|
||||||
|
ip,
|
||||||
|
lat,
|
||||||
|
lng,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue