/** * Idle-suspend sweep for Path B dev containers. * * POST /api/admin/path-b/idle-sweep[?minutes=30] * Headers: Authorization: Bearer * * Suspends every running dev container whose `last_active_at` is older * than `minutes` (default 30). Idempotent — re-runs harmlessly. * * Wire this to a cron (every 5 min) once the frontend is stable. * Crontab: "[asterisk]/5 * * * *" running: * curl -fsS -X POST -H "Authorization: Bearer $SECRET" \ * https://vibnai.com/api/admin/path-b/idle-sweep * * Saves money (suspended containers don't bill compute) without * destroying state — the workspace volume + cache volume persist, and * the next shell.exec call resumes the service in <5s. */ import { NextResponse } from "next/server"; import { suspendIdleContainers } from "@/lib/dev-container"; import { timingSafeStringEq } from "@/lib/server/timing-safe"; export async function POST(request: Request) { const expected = process.env.NEXTAUTH_SECRET ?? ""; if (!expected) { return NextResponse.json( { error: "NEXTAUTH_SECRET not configured" }, { status: 503 }, ); } const auth = request.headers.get("authorization") ?? ""; const bearer = auth.toLowerCase().startsWith("bearer ") ? auth.slice(7).trim() : ""; if (!bearer || !timingSafeStringEq(expected, bearer)) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } const url = new URL(request.url); const minStr = url.searchParams.get("minutes"); const minutes = minStr && Number.isFinite(Number(minStr)) ? Math.max(5, Number(minStr)) : 30; const result = await suspendIdleContainers(minutes); return NextResponse.json({ result, idleMinutes: minutes }); }