import { NextResponse } from "next/server"; /** * Canonical API error contract for `app/api/**`. * * Response body shape: `{ error: { code, message }, details? }`. * * Codes are kept intentionally small. Add a new code only when an existing * one cannot describe the failure; route handlers should not invent codes * inline (use `errorJson(code, …)` here so the union stays the source of * truth). */ export type ApiErrorCode = | "db_unavailable" | "unauthorized" | "forbidden" | "not_found" | "validation_error" | "invalid_json" | "payload_too_large" | "rate_limited" | "server_misconfigured" | "mail_failed" | "internal_error" | "conflict"; export interface ApiErrorBody { error: { code: ApiErrorCode; message: string }; details?: unknown; } interface ErrorOpts { details?: unknown; headers?: HeadersInit; } export function errorJson( code: ApiErrorCode, message: string, status: number, opts: ErrorOpts = {}, ): NextResponse { const body: ApiErrorBody = { error: { code, message } }; if (opts.details !== undefined) { body.details = opts.details; } return NextResponse.json(body, { status, headers: opts.headers }); } export function dbUnavailable(): NextResponse { return errorJson( "db_unavailable", "Database is not configured (CLOUDRON_POSTGRESQL_URL).", 503, ); } export function unauthorized(message = "Unauthorized"): NextResponse { return errorJson("unauthorized", message, 401); } export function notFound(message = "Not found"): NextResponse { return errorJson("not_found", message, 404); } export function forbidden(message = "Forbidden"): NextResponse { return errorJson("forbidden", message, 403); } export function conflict(message = "Conflict"): NextResponse { return errorJson("conflict", message, 409); } export function rateLimited(retryAfterMs: number): NextResponse { const retryAfterSec = Math.max(1, Math.ceil(retryAfterMs / 1000)); return errorJson("rate_limited", "Too many requests", 429, { details: { retryAfterMs }, headers: { "retry-after": String(retryAfterSec) }, }); } export function serverMisconfigured( message = "Server misconfiguration", ): NextResponse { return errorJson("server_misconfigured", message, 500); } export function internalError( message = "Internal server error", ): NextResponse { return errorJson("internal_error", message, 500); }