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.
64 lines
1.8 KiB
TypeScript
64 lines
1.8 KiB
TypeScript
import alchemy from "alchemy";
|
|
import { Queue, TanStackStart } from "alchemy/cloudflare";
|
|
import { config } from "dotenv";
|
|
|
|
config({ path: "./.env" });
|
|
config({ path: "../env/.env" });
|
|
|
|
const app = await alchemy("kk");
|
|
|
|
/** Throws at deploy time if a required variable is missing from the environment. */
|
|
function getEnvVar(name: string): string {
|
|
const value = process.env[name];
|
|
if (!value) {
|
|
throw new Error(`Missing required environment variable: ${name}`);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
const emailQueue = await Queue("email-queue", {
|
|
name: "kk-email-queue",
|
|
});
|
|
|
|
export const web = await TanStackStart("web", {
|
|
cwd: "../../apps/web",
|
|
bindings: {
|
|
// Core
|
|
DATABASE_URL: getEnvVar("DATABASE_URL"),
|
|
CORS_ORIGIN: getEnvVar("CORS_ORIGIN"),
|
|
BETTER_AUTH_SECRET: getEnvVar("BETTER_AUTH_SECRET"),
|
|
BETTER_AUTH_URL: getEnvVar("BETTER_AUTH_URL"),
|
|
// Email (SMTP)
|
|
SMTP_HOST: getEnvVar("SMTP_HOST"),
|
|
SMTP_PORT: getEnvVar("SMTP_PORT"),
|
|
SMTP_USER: getEnvVar("SMTP_USER"),
|
|
SMTP_PASS: getEnvVar("SMTP_PASS"),
|
|
SMTP_FROM: getEnvVar("SMTP_FROM"),
|
|
// Payments (Mollie)
|
|
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"],
|
|
});
|
|
|
|
console.log(`Web -> ${web.url}`);
|
|
|
|
await app.finalize();
|