Add note to reactivation request
All checks were successful
Build and Push / build (push) Successful in 1m28s

This commit is contained in:
2026-05-02 16:43:54 +02:00
parent 8273d08f15
commit 11157b872c
11 changed files with 235 additions and 11 deletions

View File

@@ -1,4 +1,5 @@
import { NextRequest, NextResponse } from "next/server";
import { z } from "zod";
import { getSessionUser, canMutate } from "@/lib/session";
import { getTenant, setTenantAnnotation } from "@/lib/k8s";
import { canUserSeeTenant } from "@/lib/visibility";
@@ -7,8 +8,26 @@ import {
getPendingResumeRequestForTenant,
getTenantRequestByTenantName,
} from "@/lib/db";
import { sendResumeRequestAdminNotificationEmail } from "@/lib/email";
import { safeError } from "@/lib/errors";
/**
* Body schema. Both fields optional; the customer can submit a
* resume request with no body at all (the JS client sends `{}`),
* or with a note explaining their reactivation rationale.
*
* Length cap mirrors `billing_notes` (2000 chars) — same lower
* bound for "free-form text we don't want abused".
*/
const bodySchema = z.object({
customerNotes: z
.string()
.trim()
.max(2000)
.optional()
.transform((v) => (v && v.length > 0 ? v : undefined)),
});
/**
* POST /api/tenants/[name]/resume-request
*
@@ -82,6 +101,18 @@ export async function POST(
);
}
// Body is optional — the customer can submit a resume request
// with no payload at all, or attach a free-form note.
const rawBody = await req.json().catch(() => ({}));
const parsed = bodySchema.safeParse(rawBody ?? {});
if (!parsed.success) {
return NextResponse.json(
{ error: "Invalid input", details: parsed.error.flatten() },
{ status: 400 }
);
}
const customerNotes = parsed.data.customerNotes;
// Already a pending request? Don't duplicate.
const existing = await getPendingResumeRequestForTenant(name);
if (existing) {
@@ -110,6 +141,7 @@ export async function POST(
contactEmail: user.email,
companyName: provision?.companyName ?? tenant.spec.displayName ?? name,
agentName: provision?.agentName ?? "Assistant",
customerNotes,
});
// Stamp the annotation so the operator pauses its TTL. If this
@@ -128,6 +160,20 @@ export async function POST(
);
}
// Notify admin distribution. Fire-and-log: failure to email
// doesn't roll back the request creation. The customer's note
// (if any) is included so admin can triage from the email
// without opening the queue.
sendResumeRequestAdminNotificationEmail({
tenantName: name,
companyName: resumeRequest.companyName,
contactName: resumeRequest.contactName,
contactEmail: resumeRequest.contactEmail,
customerNotes,
}).catch((e) =>
console.error("resume admin notification email failed:", e)
);
return NextResponse.json(
{
message: "Resume request submitted. An admin will review shortly.",