import { Env } from './types'; import { NotificationsHub } from './durable/notifications-hub'; import { handleRequest } from './router'; import { StorageService } from './services/storage'; import { applyCors, jsonResponse } from './utils/response'; import { runScheduledBackupIfDue } from './handlers/backup'; let dbInitialized = false; let dbInitError: string | null = null; let dbInitPromise: Promise | null = null; function normalizeRequestUrl(request: Request): Request { const url = new URL(request.url); const normalizedPathname = url.pathname.length <= 1 ? url.pathname : url.pathname.replace(/\/+$/, ''); if (normalizedPathname === url.pathname) return request; url.pathname = normalizedPathname; return new Request(url.toString(), request); } function isWorkerHandledPath(path: string): boolean { return ( path.startsWith('/api/') || path.startsWith('/identity/') || path.startsWith('/icons/') || path.startsWith('/notifications/') || path.startsWith('/.well-known/') || path === '/config' || path === '/api/config' || path === '/api/version' ); } async function maybeServeAsset(request: Request, env: Env): Promise { if (!env.ASSETS) return null; if (request.method !== 'GET' && request.method !== 'HEAD') return null; const url = new URL(request.url); if (isWorkerHandledPath(url.pathname)) return null; return env.ASSETS.fetch(request); } async function ensureDatabaseInitialized(env: Env): Promise { if (dbInitialized) return; if (!dbInitPromise) { dbInitPromise = (async () => { const storage = new StorageService(env.DB); await storage.initializeDatabase(); dbInitialized = true; dbInitError = null; })() .catch((error: unknown) => { console.error('Failed to initialize database:', error); dbInitError = error instanceof Error ? error.message : 'Unknown database initialization error'; }) .finally(() => { dbInitPromise = null; }); } await dbInitPromise; } export default { async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise { void ctx; const normalizedRequest = normalizeRequestUrl(request); const assetResponse = await maybeServeAsset(normalizedRequest, env); if (assetResponse) { return applyCors(normalizedRequest, assetResponse); } await ensureDatabaseInitialized(env); if (dbInitError) { // Log full error server-side, return generic message to client. console.error('DB init error (not forwarded to client):', dbInitError); const resp = jsonResponse( { error: 'Database not initialized', error_description: 'Database initialization failed. Check server logs for details.', ErrorModel: { Message: 'Service temporarily unavailable', Object: 'error', }, }, 500 ); return applyCors(normalizedRequest, resp); } const resp = await handleRequest(normalizedRequest, env); return applyCors(normalizedRequest, resp); }, async scheduled(controller: ScheduledController, env: Env, ctx: ExecutionContext): Promise { void controller; await ensureDatabaseInitialized(env); if (dbInitError) { console.error('Skipping scheduled backup because DB init failed:', dbInitError); return; } ctx.waitUntil(runScheduledBackupIfDue(env).catch((error) => { console.error('Scheduled backup failed:', error); })); }, }; export { NotificationsHub };