UNPKG

@safaricom-mxl/nextjs

Version:

MXL Javascript RUM agent for nextjs

106 lines (103 loc) 3.45 kB
// src/create-next-route-handler.ts import { createHash } from "crypto"; import { NextResponse } from "next/server.js"; // src/constants.ts var SCRIPT_URL = "https://mxl-scripts.apps.ocpthikadev01.safaricom.net/v1"; var DEFAULT_API_URL = "https://mxl-server.apps.ocpthikadev01.safaricom.net"; var SCRIPT_PATH = "/mxl.js"; // src/create-next-route-handler.ts function getClientHeaders(req) { const headers = new Headers(); const ip = req.headers.get("cf-connecting-ip") ?? req.headers.get("x-forwarded-for")?.split(",")[0] ?? req.headers.get("x-vercel-forwarded-for"); headers.set("Content-Type", "application/json"); headers.set("mxl-client-id", req.headers.get("mxl-client-id") ?? ""); const origin = req.headers.get("origin") ?? (() => { const url = new URL(req.url); return `${url.protocol}//${url.host}`; })(); headers.set("origin", origin); headers.set("User-Agent", req.headers.get("user-agent") ?? ""); if (ip) { headers.set("mxl-client-ip", ip); } return headers; } async function handleApiRoute(req, apiUrl, apiPath) { const headers = getClientHeaders(req); try { const res = await fetch(`${apiUrl}${apiPath}`, { method: req.method, headers, body: req.method === "POST" ? JSON.stringify(await req.json()) : void 0 }); if (res.headers.get("content-type")?.includes("application/json")) { return NextResponse.json(await res.json(), { status: res.status }); } return NextResponse.json(await res.text(), { status: res.status }); } catch (e) { return NextResponse.json( { error: "Failed to proxy request", message: e instanceof Error ? e.message : String(e) }, { status: 500 } ); } } async function handleScriptProxyRoute(req) { const url = new URL(req.url); const pathname = url.pathname; if (!pathname.endsWith(SCRIPT_PATH)) { return NextResponse.json({ error: "Not found" }, { status: 404 }); } let scriptUrl = `${SCRIPT_URL}${SCRIPT_PATH}`; if (url.searchParams.size > 0) { scriptUrl += `?${url.searchParams.toString()}`; } try { const res = await fetch(scriptUrl, { // @ts-expect-error next: { revalidate: 86400 } }); const text = await res.text(); const etag = `"${createHash("md5").update(scriptUrl + text).digest("hex")}"`; return new NextResponse(text, { headers: { "Content-Type": "text/javascript", "Cache-Control": "public, max-age=86400, stale-while-revalidate=86400", ETag: etag } }); } catch (e) { return NextResponse.json( { error: "Failed to fetch script", message: e instanceof Error ? e.message : String(e) }, { status: 500 } ); } } function createRouteHandler(options) { const apiUrl = options?.apiUrl ?? DEFAULT_API_URL; const handler = async function handler2(req) { const url = new URL(req.url); const pathname = url.pathname; const method = req.method; if (method === "GET" && pathname.endsWith(SCRIPT_PATH)) { return handleScriptProxyRoute(req); } const apiPathMatch = pathname.indexOf("/track"); if (apiPathMatch === -1) { return NextResponse.json({ error: "Not found" }, { status: 404 }); } const apiPath = pathname.substring(apiPathMatch); return handleApiRoute(req, apiUrl, apiPath); }; handler.GET = handler; handler.POST = handler; return handler; } export { createRouteHandler };