fix(observability,webhooks,i18n): timestamped logs, dedupe webhook retries, default non-de locales to English
- New custom server.js (replaces react-router-serve): ISO timestamps on all console.* output and on access logs, and skip successful /healthz polls so real traffic stays visible. - New ProcessedWebhook table + dedupe helper keyed on X-Shopify-Webhook-Id; stops Shopify retries from triggering a second invoice email when the original delivery exceeded the 5s ack timeout. - orders/create + orders/fulfilled now respond 200 immediately and run the PDF/email work in the background so we stay under that timeout. - pickLanguage(): non-German locales (it, fr, es, ...) now default to English instead of falling back to German. Empty/unknown still maps to 'de' so the per-shop defaultLanguage chain keeps working. - Tests for pickLanguage and dedupe via node --test + tsx.
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Fire-and-forget runner for webhook side-effects.
|
||||
*
|
||||
* Shopify expects a 200 response within ~5 seconds, otherwise it considers
|
||||
* the delivery failed and retries it. Heavy automation work (PDF render,
|
||||
* Shopify Files upload, SMTP send) routinely exceeded that budget, which
|
||||
* caused duplicate invoice emails before we added the dedupe table.
|
||||
*
|
||||
* Returning the response immediately and letting the work finish in the
|
||||
* background keeps Shopify happy. Combined with the dedupe table this is
|
||||
* defence-in-depth: dedupe ensures *correctness* even if a retry sneaks
|
||||
* through, while async processing makes retries unlikely in the first
|
||||
* place.
|
||||
*
|
||||
* Errors are caught and logged \u2014 they cannot reach a dispatcher because
|
||||
* the HTTP response is already gone.
|
||||
*/
|
||||
export function runWebhookInBackground(
|
||||
description: string,
|
||||
work: () => Promise<unknown>,
|
||||
): void {
|
||||
// `void` so we don't accidentally `await` the floating promise; the
|
||||
// node event loop keeps the task alive until it settles.
|
||||
void work().catch((err) => {
|
||||
console.error(`background webhook task '${description}' failed:`, err);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user