import { NextRequest, NextResponse } from "next/server"; import { getSessionUser, canMutate } from "@/lib/session"; import { dismissTenantRequest, getTenantRequestById } from "@/lib/db"; import { safeError } from "@/lib/errors"; /** * POST /api/onboarding/[id]/dismiss * * Customer-side acknowledgement of a rejected or cancelled request * (Bug 13). Sets `dismissed_at = now()` so the row stops appearing * in the dashboard's `listActiveTenantRequestsByOrgId` query. The * row itself is preserved for audit. * * Authorization mirrors the GET / DELETE / PATCH endpoints on this * resource: customer owners (or platform staff) of the row's org. * * Idempotent: dismissing an already-dismissed request returns 200 * with no change. We refuse to dismiss non-terminal rows (pending, * approved, provisioning, active) — those are still actionable, and * "hiding" them would stash live state from the customer. */ export async function POST( _req: NextRequest, { params }: { params: Promise<{ id: string }> } ) { const user = await getSessionUser(); if (!user) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } if (!canMutate(user)) { return NextResponse.json({ error: "Forbidden" }, { status: 403 }); } const { id } = await params; const tr = await getTenantRequestById(id); if (!tr) { return NextResponse.json({ error: "Not found" }, { status: 404 }); } if (!user.isPlatform && tr.zitadelOrgId !== user.orgId) { return NextResponse.json({ error: "Not found" }, { status: 404 }); } if (tr.status !== "rejected" && tr.status !== "cancelled") { return NextResponse.json( { error: "Only rejected or cancelled requests can be dismissed. Active requests stay visible.", code: "not_dismissable", currentStatus: tr.status, }, { status: 409 } ); } try { await dismissTenantRequest(id); return NextResponse.json({ message: "Dismissed.", id }); } catch (e: any) { console.error("Failed to dismiss request:", e); return NextResponse.json( { error: safeError(e, "Failed to dismiss request") }, { status: 500 } ); } }