feat(email): route all email sends through Cloudflare Queue
Introduces a CF Queue binding (kk-email-queue) to decouple email delivery from request handlers, preventing slow responses and providing automatic retries. All send*Email calls now go through the queue when the binding is available, with direct-send fallbacks for local dev. Reminder fan-outs mark DB rows optimistically before enqueueing to prevent re-enqueue on subsequent cron ticks.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import alchemy from "alchemy";
|
||||
import { TanStackStart } from "alchemy/cloudflare";
|
||||
import { Queue, TanStackStart } from "alchemy/cloudflare";
|
||||
import { config } from "dotenv";
|
||||
|
||||
config({ path: "./.env" });
|
||||
@@ -16,6 +16,10 @@ function getEnvVar(name: string): string {
|
||||
return value;
|
||||
}
|
||||
|
||||
const emailQueue = await Queue("email-queue", {
|
||||
name: "kk-email-queue",
|
||||
});
|
||||
|
||||
export const web = await TanStackStart("web", {
|
||||
cwd: "../../apps/web",
|
||||
bindings: {
|
||||
@@ -34,7 +38,21 @@ export const web = await TanStackStart("web", {
|
||||
MOLLIE_API_KEY: getEnvVar("MOLLIE_API_KEY"),
|
||||
// Cron secret for protected scheduled endpoints
|
||||
CRON_SECRET: getEnvVar("CRON_SECRET"),
|
||||
// Queue binding for async email sends
|
||||
EMAIL_QUEUE: emailQueue,
|
||||
},
|
||||
// Queue consumer: the worker's queue() handler processes EmailMessage batches
|
||||
eventSources: [
|
||||
{
|
||||
queue: emailQueue,
|
||||
settings: {
|
||||
batchSize: 10,
|
||||
maxRetries: 3,
|
||||
retryDelay: 60, // seconds before retrying a failed message
|
||||
maxWaitTimeMs: 1000,
|
||||
},
|
||||
},
|
||||
],
|
||||
// Fire every hour so reminder checks can run at 19:00 on 2026-03-15 (24h) and 18:00 on 2026-03-16 (1h)
|
||||
crons: ["0 * * * *"],
|
||||
domains: ["kunstenkamp.be", "www.kunstenkamp.be"],
|
||||
|
||||
Reference in New Issue
Block a user