import { NextResponse } from "next/server"; import { requirePlatformRole } from "@/lib/session"; import { getTenantRequestById, updateTenantRequestStatus } from "@/lib/db"; import { setTenantAnnotation } from "@/lib/k8s"; import { sendRejectionEmail, sendResumeRejectionEmail } from "@/lib/email"; /** * POST /api/admin/requests/[id]/reject * Reject a tenant request and notify the customer. * * For resume requests (Bug 37a): also clears the * `pieced.ch/resume-request-pending` annotation on the tenant CR. * The operator's 60-day TTL then resumes counting from the original * suspendedAt — rejection doesn't reset it. The customer can submit * a fresh resume request later if circumstances change, but that * starts a new pending row and re-stamps the annotation. */ 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") { return NextResponse.json( { error: `Request is already ${tenantRequest.status}` }, { status: 400 } ); } const updated = await updateTenantRequestStatus(id, "rejected", { adminNotes, }); // Resume rejection: clear the annotation so the operator's TTL // resumes. Best-effort — failure is logged, not propagated. if ( tenantRequest.requestType === "resume" && tenantRequest.tenantName ) { try { await setTenantAnnotation( tenantRequest.tenantName, "pieced.ch/resume-request-pending", null ); } catch (e) { console.warn( "post-reject annotation clear failed; operator's TTL will pause until annotation removed by admin", e ); } } // Notify customer. Resume requests get a different email — the // tenant already exists; copy needs to mention "stays suspended" and // the 60-day retention deadline. Provision rejections use the // original onboarding-rejection wording. if (tenantRequest.requestType === "resume") { await sendResumeRejectionEmail( tenantRequest.contactEmail, tenantRequest.contactName, tenantRequest.companyName, adminNotes ); } else { await sendRejectionEmail( tenantRequest.contactEmail, tenantRequest.contactName, tenantRequest.companyName, adminNotes ); } return NextResponse.json({ message: "Request rejected.", request: updated, }); }