fix(orders): improve error handling for order creation and enforce email body length limit
Enhanced error messages to include specific field validation details from PocketBase, allowing callers to identify issues more easily. Additionally, implemented a safeguard to truncate the email body to a maximum of 5000 characters to prevent validation errors from surfacing as generic messages.
This commit is contained in:
parent
b20651b12d
commit
85761a8698
2 changed files with 31 additions and 3 deletions
|
|
@ -130,7 +130,27 @@ export async function POST(req: NextRequest) {
|
|||
);
|
||||
}
|
||||
|
||||
const message = error instanceof Error ? error.message : "Failed to create order";
|
||||
// PocketBase's ClientResponseError carries the real per-field
|
||||
// validation reason (e.g. "email_body: Must be no more than 5000
|
||||
// character(s).") in `.response.data`/`.data`, not `.message` —
|
||||
// `.message` is always the generic "Failed to create record."
|
||||
// Surface the field details when present so callers (n8n, the
|
||||
// agent API) don't have to go dig through PB's own request log to
|
||||
// find out which field actually failed.
|
||||
const pbData = (error as { response?: { data?: unknown }; data?: unknown })?.response?.data
|
||||
?? (error as { data?: unknown })?.data;
|
||||
const fieldDetails =
|
||||
pbData && typeof pbData === "object" && "data" in (pbData as Record<string, unknown>)
|
||||
? (pbData as { data?: Record<string, { message?: string }>; message?: string }).data
|
||||
: undefined;
|
||||
|
||||
const baseMessage = error instanceof Error ? error.message : "Failed to create order";
|
||||
const message =
|
||||
fieldDetails && Object.keys(fieldDetails).length > 0
|
||||
? `${baseMessage}: ${Object.entries(fieldDetails)
|
||||
.map(([field, info]) => `${field} - ${info?.message ?? "invalid"}`)
|
||||
.join("; ")}`
|
||||
: baseMessage;
|
||||
const status = (error as Error & { status?: number }).status || 500;
|
||||
// 404 from getOne propagates with status === 404 if PB throws it.
|
||||
// Otherwise we bucket by message: customer-not-found / company-not-found
|
||||
|
|
|
|||
|
|
@ -19,6 +19,14 @@ const QUOTE_COLLECTION = "quotes";
|
|||
const QUOTE_ITEMS_COLLECTION = "quote_items";
|
||||
const ORDER_COUNTERS_COLLECTION = "order_counters";
|
||||
|
||||
// PB schema caps `orders.email_body` at 5000 chars. Long reply threads
|
||||
// (quoted history, signatures, legal disclaimers) routinely exceed
|
||||
// that, and PB's validation error only surfaces in `err.data`, not
|
||||
// `err.message` — so an oversized body used to bubble up as an opaque
|
||||
// "Failed to create record." Truncate defensively at the write site
|
||||
// instead of trusting every caller to pre-trim.
|
||||
const ORDER_EMAIL_BODY_MAX_LENGTH = 5000;
|
||||
|
||||
export function getOrderItemsCollectionName() {
|
||||
return process.env.ORDER_ITEMS_COLLECTION || "order_items";
|
||||
}
|
||||
|
|
@ -2123,7 +2131,7 @@ export async function createOrder(input: CreateOrderInput): Promise<CreateOrderR
|
|||
// source PO email. Trimmed; empty / undefined collapses to "".
|
||||
email_sender: input.email_sender?.trim() || "",
|
||||
email_subject: input.email_subject?.trim() || "",
|
||||
email_body: input.email_body?.trim() || "",
|
||||
email_body: (input.email_body?.trim() || "").slice(0, ORDER_EMAIL_BODY_MAX_LENGTH),
|
||||
})) as Order;
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
|
|
@ -3731,7 +3739,7 @@ export async function updateOrder(
|
|||
const cleaned: Record<string, unknown> = {};
|
||||
for (const [k, v] of Object.entries(patch)) {
|
||||
if ((ORDER_PATCHABLE_FIELDS as readonly string[]).includes(k)) {
|
||||
cleaned[k] = v;
|
||||
cleaned[k] = k === "email_body" && typeof v === "string" ? v.slice(0, ORDER_EMAIL_BODY_MAX_LENGTH) : v;
|
||||
} else {
|
||||
rejected.push(k);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue