b41c1d2501
CI / Architecture Guardrails (push) Successful in 2m38s
CI / Assistant Split Regression (push) Successful in 3m33s
CI / Typecheck (push) Successful in 3m51s
CI / Lint (push) Successful in 5m2s
CI / E2E Tests (push) Has been cancelled
CI / Fresh-Linux Docker Deploy (push) Has been cancelled
CI / Release Images (push) Has been cancelled
CI / Build (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
rename(phase 1): CapaKraken → Nexus across code, UI, docs, CI (#61) Co-authored-by: Hartmut Nörenberg <hn@hartmut-noerenberg.com> Co-committed-by: Hartmut Nörenberg <hn@hartmut-noerenberg.com>
62 lines
1.8 KiB
TypeScript
62 lines
1.8 KiB
TypeScript
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 <secret>` 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(" ");
|
|
}
|