import { NextResponse } from "next/server"; import { requirePlatformRole } from "@/lib/session"; import { getTenant, deleteTenant } from "@/lib/k8s"; import { markTenantRequestDeletedByTenantName, removeAllAssignmentsForTenant, recordTenantDeleted, } from "@/lib/db"; import { safeError } from "@/lib/errors"; /** * POST /api/admin/tenants/[name]/delete * Delete a PiecedTenant CR. The operator handles cleanup * (namespace, vault, litellm team, etc.). * * Slice 6: also cascades the tenant_user_assignments rows so a * future tenant with the same name (won't happen given UUID-suffix * naming, but defense in depth) doesn't inherit stale assignments. * * Also marks the associated tenant_request as "deleted" so the * customer can re-submit the onboarding wizard. */ export async function POST( _request: Request, { params }: { params: Promise<{ name: string }> } ) { try { await requirePlatformRole(); } catch { return NextResponse.json({ error: "Forbidden" }, { status: 403 }); } const { name } = await params; const tenant = await getTenant(name); if (!tenant) { return NextResponse.json({ error: "Tenant not found" }, { status: 404 }); } try { await deleteTenant(name); // Best-effort DB cleanups. Both errors are logged but not surfaced — // the K8s deletion has already started, and the row state is just // for portal display. await markTenantRequestDeletedByTenantName(name).catch((e) => console.error("Failed to mark tenant request deleted:", e) ); await removeAllAssignmentsForTenant(name).catch((e) => console.error("Failed to clean up tenant assignments:", e) ); // Billing — Phase 1: stamp deletion timestamp on the lifecycle // row so the final invoice covering the deletion month can // prorate correctly. Idempotent at the DB layer; a missing // lifecycle row (e.g. pre-Phase-1 tenants that haven't been // backfilled yet) makes this a no-op. await recordTenantDeleted(name).catch((e) => console.error("billing: failed to stamp tenant deletion:", e) ); return NextResponse.json({ message: "Tenant deletion initiated. The operator will clean up all resources.", }); } catch (e: any) { console.error("Failed to delete tenant:", e); return NextResponse.json( { error: safeError(e, "Failed to delete tenant") }, { status: 500 } ); } }