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
+121
View File
@@ -0,0 +1,121 @@
// Pub/Sub abstraction with two implementations:
// - memory: in-process EventEmitter (single instance only)
// - redis: PUBLISH / SUBSCRIBE so a message published on one instance reaches
// subscribers on every instance
//
// Messages are JSON-serialized. Selection happens once based on REDIS_URL.
import { EventEmitter } from 'events';
import { getRedis, getSubscriber, isRedisEnabled } from '../redis.js';
export type PubSubHandler = (message: any) => void;
export interface PubSub {
readonly backend: 'memory' | 'redis';
publish(channel: string, message: any): Promise<void>;
// Returns an unsubscribe function for this specific handler.
subscribe(channel: string, handler: PubSubHandler): Promise<() => void>;
}
// ==================== Memory implementation ====================
class MemoryPubSub implements PubSub {
readonly backend = 'memory' as const;
private emitter = new EventEmitter();
constructor() {
// SSE fan-out can attach many listeners to the same channel; lift the cap.
this.emitter.setMaxListeners(0);
}
async publish(channel: string, message: any): Promise<void> {
this.emitter.emit(channel, message);
}
async subscribe(channel: string, handler: PubSubHandler): Promise<() => void> {
this.emitter.on(channel, handler);
return () => this.emitter.off(channel, handler);
}
}
// ==================== Redis implementation ====================
class RedisPubSub implements PubSub {
readonly backend = 'redis' as const;
// Per-channel handler sets so a single Redis subscription fans out locally.
private handlers = new Map<string, Set<PubSubHandler>>();
private wired = false;
private ensureWired(): void {
if (this.wired) return;
const sub = getSubscriber();
if (!sub) return;
this.wired = true;
sub.on('message', (channel: string, payload: string) => {
const set = this.handlers.get(channel);
if (!set || set.size === 0) return;
let parsed: any = payload;
try {
parsed = JSON.parse(payload);
} catch {
// Leave as raw string if it was not JSON.
}
for (const handler of set) {
try {
handler(parsed);
} catch (err: any) {
console.error('[pubsub] handler error:', err?.message || err);
}
}
});
}
async publish(channel: string, message: any): Promise<void> {
const redis = getRedis();
if (!redis) return;
try {
await redis.publish(channel, JSON.stringify(message));
} catch (err: any) {
console.error('[pubsub] publish error:', err?.message || err);
}
}
async subscribe(channel: string, handler: PubSubHandler): Promise<() => void> {
this.ensureWired();
const sub = getSubscriber();
if (!sub) return () => undefined;
let set = this.handlers.get(channel);
if (!set) {
set = new Set();
this.handlers.set(channel, set);
try {
await sub.subscribe(channel);
} catch (err: any) {
console.error('[pubsub] subscribe error:', err?.message || err);
}
}
set.add(handler);
return () => {
const current = this.handlers.get(channel);
if (!current) return;
current.delete(handler);
if (current.size === 0) {
this.handlers.delete(channel);
sub.unsubscribe(channel).catch(() => undefined);
}
};
}
}
// ==================== Selection ====================
let instance: PubSub | null = null;
export function getPubSub(): PubSub {
if (!instance) {
instance = isRedisEnabled() ? new RedisPubSub() : new MemoryPubSub();
}
return instance;
}