import { NextRequest, NextResponse } from "next/server"; import { env } from "@/lib/env"; /** * Smart Proxy for Umami Analytics. * * This Route Handler receives tracking events from the browser, * injects the secret UMAMI_WEBSITE_ID, and forwards them to the * internal Umami API endpoint. * * This ensures: * 1. The Website ID is NOT leaked to the client bundle. * 2. The Umami API endpoint is hidden behind our domain. * 3. We have full control over the tracking data. */ export async function POST(request: NextRequest) { try { const body = await request.json(); const { type, payload } = body; // Inject the secret websiteId from server config const websiteId = env.UMAMI_WEBSITE_ID || env.NEXT_PUBLIC_UMAMI_WEBSITE_ID; if (!websiteId) { console.warn( "Umami tracking received but no Website ID configured on server", ); return NextResponse.json({ status: "ignored" }, { status: 200 }); } // Prepare the enhanced payload with the secret ID const enhancedPayload = { ...payload, website: websiteId, }; const umamiEndpoint = env.UMAMI_API_ENDPOINT; // Log the event (debug only) if (process.env.NODE_ENV === "development") { console.debug("Forwarding analytics event", { type, url: payload.url, website: websiteId.slice(0, 8) + "...", }); } const response = await fetch(`${umamiEndpoint}/api/send`, { method: "POST", headers: { "Content-Type": "application/json", "User-Agent": request.headers.get("user-agent") || "Mintel-Smart-Proxy", "X-Forwarded-For": request.headers.get("x-forwarded-for") || "", }, body: JSON.stringify({ type, payload: enhancedPayload }), }); if (!response.ok) { const errorText = await response.text(); console.error("Umami API responded with error", { status: response.status, error: errorText.slice(0, 100), }); return new NextResponse(errorText, { status: response.status }); } return NextResponse.json({ status: "ok" }); } catch (error) { console.error("Failed to proxy analytics request", { error: (error as Error).message, }); return NextResponse.json( { error: "Internal Server Error" }, { status: 500 }, ); } }