import { NextResponse } from "next/server"; import { requirePlatformRole } from "@/lib/session"; import { getTenantRequestById, updateTenantRequestStatus } from "@/lib/db"; import { createTenant } from "@/lib/k8s"; /** * POST /api/admin/requests/[id]/approve * Approve a tenant request: create the PiecedTenant CR and update status. * Also supports re-approving a previously rejected request. */ export async function POST( request: Request, { params }: { params: Promise<{ id: string }> } ) { try { await requirePlatformRole(); } catch { return NextResponse.json({ error: "Forbidden" }, { status: 403 }); } const { id } = await params; const body = await request.json().catch(() => ({})); const adminNotes = body.adminNotes as string | undefined; const tenantRequest = await getTenantRequestById(id); if (!tenantRequest) { return NextResponse.json( { error: "Request not found" }, { status: 404 } ); } if (tenantRequest.status !== "pending" && tenantRequest.status !== "rejected") { return NextResponse.json( { error: `Request is already ${tenantRequest.status}` }, { status: 400 } ); } // Derive tenant name from company name: lowercase, alphanumeric + hyphens const tenantName = tenantRequest.companyName .toLowerCase() .replace(/[^a-z0-9]+/g, "-") .replace(/^-|-$/g, "") .slice(0, 63) || `tenant-${tenantRequest.id.slice(0, 8)}`; try { // Create the PiecedTenant CR await createTenant( tenantName, { displayName: tenantRequest.companyName, agentName: tenantRequest.agentName, packages: tenantRequest.packages, workspaceFiles: tenantRequest.soulMd ? { "SOUL.md": tenantRequest.soulMd } : undefined, }, { "pieced.ch/zitadel-org-id": tenantRequest.zitadelOrgId, } ); // Update request status const updated = await updateTenantRequestStatus(id, "provisioning", { adminNotes, tenantName, }); return NextResponse.json({ message: "Tenant approved and provisioning started.", request: updated, tenantName, }); } catch (e: any) { console.error("Failed to create tenant:", e); return NextResponse.json( { error: `Failed to create tenant: ${e.message}` }, { status: 500 } ); } }