deploy
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import "../globals.css";
|
||||
import { PageLoadTracker } from "../PageLoadTracker.jsx";
|
||||
import { baseMetadata } from "../seo.js";
|
||||
|
||||
export const metadata = baseMetadata;
|
||||
@@ -6,7 +7,10 @@ export const metadata = baseMetadata;
|
||||
export default function EnglishLayout({ children }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>{children}</body>
|
||||
<body>
|
||||
<PageLoadTracker locale="en" />
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
38
frontend/app/PageLoadTracker.jsx
Normal file
38
frontend/app/PageLoadTracker.jsx
Normal file
@@ -0,0 +1,38 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
|
||||
export function PageLoadTracker({ locale }) {
|
||||
useEffect(() => {
|
||||
const payload = {
|
||||
path: window.location.pathname,
|
||||
locale,
|
||||
referrer: document.referrer || null,
|
||||
viewport: {
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
devicePixelRatio: window.devicePixelRatio || 1,
|
||||
},
|
||||
metadata: {
|
||||
language: navigator.language || null,
|
||||
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || null,
|
||||
},
|
||||
};
|
||||
const body = JSON.stringify(payload);
|
||||
|
||||
if (navigator.sendBeacon) {
|
||||
const blob = new Blob([body], { type: "application/json" });
|
||||
navigator.sendBeacon("/api/page-load", blob);
|
||||
return;
|
||||
}
|
||||
|
||||
fetch("/api/page-load", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body,
|
||||
keepalive: true,
|
||||
}).catch(() => {});
|
||||
}, [locale]);
|
||||
|
||||
return null;
|
||||
}
|
||||
71
frontend/app/api/page-load/route.js
Normal file
71
frontend/app/api/page-load/route.js
Normal file
@@ -0,0 +1,71 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import pg from "pg";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
export const runtime = "nodejs";
|
||||
|
||||
const { Pool } = pg;
|
||||
|
||||
function pool() {
|
||||
if (!process.env.DATABASE_URL) {
|
||||
return null;
|
||||
}
|
||||
globalThis.__robertsearesPageLoadPool ||= new Pool({
|
||||
connectionString: process.env.DATABASE_URL,
|
||||
max: 2,
|
||||
idleTimeoutMillis: 30_000,
|
||||
connectionTimeoutMillis: 10_000,
|
||||
});
|
||||
return globalThis.__robertsearesPageLoadPool;
|
||||
}
|
||||
|
||||
function text(value, maxLength) {
|
||||
if (typeof value !== "string") {
|
||||
return null;
|
||||
}
|
||||
const trimmed = value.trim();
|
||||
return trimmed ? trimmed.slice(0, maxLength) : null;
|
||||
}
|
||||
|
||||
function object(value) {
|
||||
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
||||
}
|
||||
|
||||
export async function POST(request) {
|
||||
const database = pool();
|
||||
if (!database) {
|
||||
return new NextResponse(null, { status: 202 });
|
||||
}
|
||||
|
||||
try {
|
||||
const body = await request.json();
|
||||
const path = text(body.path, 512) || "/";
|
||||
const locale = text(body.locale, 16);
|
||||
const referrer = text(body.referrer, 2048);
|
||||
const userAgent = text(request.headers.get("user-agent"), 2048);
|
||||
const viewport = object(body.viewport);
|
||||
const metadata = {
|
||||
...object(body.metadata),
|
||||
forwardedFor: text(request.headers.get("x-forwarded-for"), 2048),
|
||||
country:
|
||||
text(request.headers.get("cf-ipcountry"), 16)
|
||||
|| text(request.headers.get("x-vercel-ip-country"), 16)
|
||||
|| text(request.headers.get("x-country-code"), 16),
|
||||
};
|
||||
|
||||
await database.query(
|
||||
`INSERT INTO robertseares_page_loads
|
||||
(path, locale, referrer, user_agent, viewport, metadata)
|
||||
VALUES ($1, $2, $3, $4, $5::jsonb, $6::jsonb)`,
|
||||
[path, locale, referrer, userAgent, JSON.stringify(viewport), JSON.stringify(metadata)],
|
||||
);
|
||||
} catch (error) {
|
||||
console.warn("page load tracking failed", error);
|
||||
}
|
||||
|
||||
return new NextResponse(null, { status: 204 });
|
||||
}
|
||||
|
||||
export function GET() {
|
||||
return NextResponse.json({ ok: true });
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import "../globals.css";
|
||||
import { PageLoadTracker } from "../PageLoadTracker.jsx";
|
||||
import { baseMetadata } from "../seo.js";
|
||||
|
||||
export const metadata = {
|
||||
@@ -16,7 +17,10 @@ export const metadata = {
|
||||
export default function PortugueseLayout({ children }) {
|
||||
return (
|
||||
<html lang="pt-BR">
|
||||
<body>{children}</body>
|
||||
<body>
|
||||
<PageLoadTracker locale="pt" />
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user