Refactor monolithic modules and harden booking, email, and auth infrastructure.

Split oversized frontend API client, email service, and admin/booking pages into focused modules while preserving import surfaces, and add Redis-backed queues, stale booking cleanup, stronger auth, and scale deployment configs.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Michilis
2026-06-25 07:12:59 +00:00
co-authored by Cursor
parent f0e2de2834
commit 613bd7be1d
75 changed files with 7702 additions and 5580 deletions
+3
View File
@@ -12,8 +12,11 @@ const dbType = process.env.DB_TYPE || 'sqlite';
let db: ReturnType<typeof drizzleSqlite> | ReturnType<typeof drizzlePg>;
if (dbType === 'postgres') {
// Cap connections per instance so that, when running multiple replicas,
// DB_POOL_MAX * replicas stays below the Postgres max_connections limit.
const pool = new pg.Pool({
connectionString: process.env.DATABASE_URL || 'postgresql://localhost:5432/spanglish',
max: Number(process.env.DB_POOL_MAX || 10),
});
db = drizzlePg(pool, { schema });
} else {
+24
View File
@@ -432,6 +432,18 @@ async function migrate() {
)
`);
await (db as any).run(sql`
CREATE TABLE IF NOT EXISTS email_queue (
id TEXT PRIMARY KEY,
params TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'pending',
attempts INTEGER NOT NULL DEFAULT 0,
last_error TEXT,
created_at TEXT NOT NULL,
processed_at TEXT
)
`);
// Site settings table
await (db as any).run(sql`
CREATE TABLE IF NOT EXISTS site_settings (
@@ -899,6 +911,18 @@ async function migrate() {
)
`);
await (db as any).execute(sql`
CREATE TABLE IF NOT EXISTS email_queue (
id UUID PRIMARY KEY,
params TEXT NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
attempts INTEGER NOT NULL DEFAULT 0,
last_error TEXT,
created_at TIMESTAMP NOT NULL,
processed_at TIMESTAMP
)
`);
// Site settings table
await (db as any).execute(sql`
CREATE TABLE IF NOT EXISTS site_settings (
+25
View File
@@ -273,6 +273,18 @@ export const sqliteEmailSettings = sqliteTable('email_settings', {
updatedAt: text('updated_at').notNull(),
});
// Durable email queue. Jobs survive process restarts; a startup recovery step
// resets any 'processing' rows back to 'pending'.
export const sqliteEmailQueue = sqliteTable('email_queue', {
id: text('id').primaryKey(),
params: text('params').notNull(), // JSON-encoded TemplateEmailJobParams
status: text('status', { enum: ['pending', 'processing', 'sent', 'failed'] }).notNull().default('pending'),
attempts: integer('attempts').notNull().default(0),
lastError: text('last_error'),
createdAt: text('created_at').notNull(),
processedAt: text('processed_at'),
});
// Legal Pages table for admin-editable legal content
export const sqliteLegalPages = sqliteTable('legal_pages', {
id: text('id').primaryKey(),
@@ -608,6 +620,18 @@ export const pgEmailSettings = pgTable('email_settings', {
updatedAt: timestamp('updated_at').notNull(),
});
// Durable email queue. Jobs survive process restarts; a startup recovery step
// resets any 'processing' rows back to 'pending'.
export const pgEmailQueue = pgTable('email_queue', {
id: uuid('id').primaryKey(),
params: pgText('params').notNull(), // JSON-encoded TemplateEmailJobParams
status: varchar('status', { length: 20 }).notNull().default('pending'),
attempts: pgInteger('attempts').notNull().default(0),
lastError: pgText('last_error'),
createdAt: timestamp('created_at').notNull(),
processedAt: timestamp('processed_at'),
});
// Legal Pages table for admin-editable legal content
export const pgLegalPages = pgTable('legal_pages', {
id: uuid('id').primaryKey(),
@@ -695,6 +719,7 @@ export const auditLogs = dbType === 'postgres' ? pgAuditLogs : sqliteAuditLogs;
export const emailTemplates = dbType === 'postgres' ? pgEmailTemplates : sqliteEmailTemplates;
export const emailLogs = dbType === 'postgres' ? pgEmailLogs : sqliteEmailLogs;
export const emailSettings = dbType === 'postgres' ? pgEmailSettings : sqliteEmailSettings;
export const emailQueue = dbType === 'postgres' ? pgEmailQueue : sqliteEmailQueue;
export const paymentOptions = dbType === 'postgres' ? pgPaymentOptions : sqlitePaymentOptions;
export const eventPaymentOverrides = dbType === 'postgres' ? pgEventPaymentOverrides : sqliteEventPaymentOverrides;
export const magicLinkTokens = dbType === 'postgres' ? pgMagicLinkTokens : sqliteMagicLinkTokens;