save ip, lat, lng of session

This commit is contained in:
Viet-Tien Ngoc 2024-07-20 11:23:34 +07:00
parent 8b108d3aae
commit d0fb1dd4dd
10 changed files with 101 additions and 12 deletions

View file

@ -0,0 +1,4 @@
-- AlterTable
ALTER TABLE "session" ADD COLUMN "ip" VARCHAR(40),
ADD COLUMN "lat" FLOAT,
ADD COLUMN "lng" FLOAT;

View file

@ -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,

View 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`);

View file

@ -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")
} }

View 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");

View file

@ -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")
} }

View file

@ -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,
};
} }

View file

@ -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')) {

View file

@ -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;
} }

View file

@ -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,
}, },
}); });
} }