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),
subdivision2 LowCardinality(String),
city String,
ip Nullable(String),
lat Nullable(Float32),
lng Nullable(Float32),
--pageviews
url_path 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)
subdivision2 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)
websiteEvent WebsiteEvent[]
@ -57,6 +60,9 @@ model Session {
@@index([websiteId, createdAt, country])
@@index([websiteId, createdAt, subdivision1])
@@index([websiteId, createdAt, city])
@@index([websiteId, createdAt, ip])
@@index([websiteId, createdAt, lat])
@@index([websiteId, createdAt, lng])
@@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)
subdivision2 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)
websiteEvent WebsiteEvent[]
@ -57,6 +60,9 @@ model Session {
@@index([websiteId, createdAt, country])
@@index([websiteId, createdAt, subdivision1])
@@index([websiteId, createdAt, city])
@@index([websiteId, createdAt, ip])
@@index([websiteId, createdAt, lat])
@@index([websiteId, createdAt, lng])
@@map("session")
}

View file

@ -84,11 +84,15 @@ export async function getLocation(ip: string, req: NextApiRequestCollect) {
const country = safeDecodeURIComponent(req.headers['cf-ipcountry']);
const subdivision1 = safeDecodeURIComponent(req.headers['cf-region-code']);
const city = safeDecodeURIComponent(req.headers['cf-ipcity']);
const lat = safeDecodeURIComponent(req.headers['cf-iplatitude']);
const lng = safeDecodeURIComponent(req.headers['cf-iplongitude']);
return {
country,
subdivision1: getRegionCode(country, subdivision1),
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 subdivision1 = safeDecodeURIComponent(req.headers['x-vercel-ip-country-region']);
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 {
country,
subdivision1: getRegionCode(country, subdivision1),
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 subdivision2 = result.subdivisions?.[1]?.names?.en;
const city = result.city?.names?.en;
const lat = result.location?.latitude;
const lng = result.location?.longitude;
return {
country,
subdivision1: getRegionCode(country, subdivision1),
subdivision2,
city,
lat,
lng,
};
}
@ -141,9 +153,23 @@ export async function getClientInfo(req: NextApiRequestCollect) {
const subdivision1 = location?.subdivision1;
const subdivision2 = location?.subdivision2;
const city = location?.city;
const lat = location?.lat;
const lng = location?.lng;
const browser = browserName(userAgent);
const os = detectOS(userAgent) as string;
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}.`);
}
const { userAgent, browser, os, ip, country, subdivision1, subdivision2, city, device } =
await getClientInfo(req);
const {
userAgent,
browser,
os,
ip,
country,
subdivision1,
subdivision2,
city,
device,
lat,
lng,
} = await getClientInfo(req);
const sessionId = uuid(websiteId, hostname, ip, userAgent);
const visitId = uuid(sessionId, visitSalt());
@ -58,6 +69,9 @@ export async function getSession(req: NextApiRequestCollect): Promise<SessionDat
subdivision1,
subdivision2,
city,
ip,
lat,
lng,
};
}
@ -80,6 +94,9 @@ export async function getSession(req: NextApiRequestCollect): Promise<SessionDat
subdivision1,
subdivision2,
city,
ip,
lat,
lng,
});
} catch (e: any) {
if (!e.message.toLowerCase().includes('unique constraint')) {

View file

@ -220,4 +220,7 @@ export interface SessionData {
subdivision1: string;
subdivision2: string;
city: string;
ip: string;
lat: number;
lng: number;
}

View file

@ -15,6 +15,9 @@ export async function createSession(data: Prisma.SessionCreateInput) {
subdivision1,
subdivision2,
city,
ip,
lat,
lng,
} = data;
return prisma.client.session.create({
@ -31,6 +34,9 @@ export async function createSession(data: Prisma.SessionCreateInput) {
subdivision1,
subdivision2,
city,
ip,
lat,
lng,
},
});
}