import { NextResponse } from "next/server"; import { eventBus } from "@nexus/api/sse"; import { verifyCronSecret } from "~/lib/cron-auth.js"; export const dynamic = "force-dynamic"; export const runtime = "nodejs"; /** * GET /api/perf — Runtime performance metrics. * * Protected by CRON_SECRET via `Authorization: Bearer ` header only. * Query-string authentication is not supported (secrets must not appear in URLs). * Fails closed (401) when CRON_SECRET is not configured in the environment. * Returns Node.js memory usage, process uptime, and SSE connection count. */ export function GET(request: Request) { const deny = verifyCronSecret(request); if (deny) return deny; const mem = process.memoryUsage(); return NextResponse.json({ timestamp: new Date().toISOString(), uptime: { seconds: Math.round(process.uptime()), formatted: formatUptime(process.uptime()), }, memory: { heapUsedMB: round(mem.heapUsed / 1024 / 1024), heapTotalMB: round(mem.heapTotal / 1024 / 1024), rssMB: round(mem.rss / 1024 / 1024), externalMB: round(mem.external / 1024 / 1024), arrayBuffersMB: round(mem.arrayBuffers / 1024 / 1024), }, sse: { activeConnections: eventBus.subscriberCount, }, node: { version: process.version, platform: process.platform, arch: process.arch, }, }); } function round(n: number): number { return Math.round(n * 100) / 100; } function formatUptime(seconds: number): string { const d = Math.floor(seconds / 86400); const h = Math.floor((seconds % 86400) / 3600); const m = Math.floor((seconds % 3600) / 60); const s = Math.floor(seconds % 60); const parts: string[] = []; if (d > 0) parts.push(`${d}d`); if (h > 0) parts.push(`${h}h`); if (m > 0) parts.push(`${m}m`); parts.push(`${s}s`); return parts.join(" "); }