72 lines
1.9 KiB
TypeScript
72 lines
1.9 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import { createServiceClient } from "@/lib/supabase";
|
|
import { anonymizeIp, isBot, parseDevice, parseBrowser, parseOs } from "@/lib/analytics";
|
|
|
|
export const runtime = "nodejs";
|
|
export const dynamic = "force-dynamic";
|
|
|
|
interface TrackBody {
|
|
path: string;
|
|
session_id: string;
|
|
referrer?: string;
|
|
view_id?: string;
|
|
duration_ms?: number;
|
|
}
|
|
|
|
export async function POST(req: NextRequest) {
|
|
try {
|
|
const body: TrackBody = await req.json();
|
|
|
|
if (!body.path || !body.session_id) {
|
|
return NextResponse.json({ ok: false }, { status: 400 });
|
|
}
|
|
|
|
const ua = req.headers.get("user-agent") ?? "";
|
|
|
|
if (isBot(ua)) {
|
|
return NextResponse.json({ ok: true, bot: true });
|
|
}
|
|
|
|
const db = createServiceClient();
|
|
|
|
if (body.view_id && body.duration_ms !== undefined) {
|
|
await db
|
|
.from("page_views")
|
|
.update({ duration_ms: body.duration_ms })
|
|
.eq("id", body.view_id)
|
|
.is("duration_ms", null);
|
|
return NextResponse.json({ ok: true });
|
|
}
|
|
|
|
const rawIp =
|
|
req.headers.get("x-forwarded-for")?.split(",")[0].trim() ??
|
|
req.headers.get("x-real-ip") ??
|
|
"0.0.0.0";
|
|
|
|
const { data, error } = await db
|
|
.from("page_views")
|
|
.insert({
|
|
path: body.path,
|
|
session_id: body.session_id,
|
|
referrer: body.referrer ?? null,
|
|
ip_anon: anonymizeIp(rawIp),
|
|
device_type: parseDevice(ua),
|
|
browser: parseBrowser(ua),
|
|
os: parseOs(ua),
|
|
is_bot: false,
|
|
})
|
|
.select("id")
|
|
.single();
|
|
|
|
if (error) {
|
|
console.error("[analytics/track] DB error:", error);
|
|
return NextResponse.json({ ok: false }, { status: 500 });
|
|
}
|
|
|
|
return NextResponse.json({ ok: true, view_id: data.id });
|
|
} catch (err) {
|
|
console.error("[analytics/track] Error:", err);
|
|
return NextResponse.json({ ok: false }, { status: 500 });
|
|
}
|
|
}
|