import { Router, Request, Response } from 'express'; import { etl } from '../config'; import { runSync, SyncResult } from '../services/etlSync'; const router = Router(); type SyncState = | { status: 'idle' } | { status: 'running'; mode: string; startedAt: string; currentMonth?: string } | { status: 'done'; result: SyncResult; finishedAt: string } | { status: 'error'; error: string; finishedAt: string }; let syncState: SyncState = { status: 'idle' }; function auth(req: Request, res: Response): boolean { const header = req.headers.authorization; if (etl.secret && header !== `Bearer ${etl.secret}`) { res.status(401).json({ error: 'Unauthorized' }); return false; } return true; } // POST /api/etl/sync?mode=full|incremental — fires and returns immediately router.post('/sync', (req: Request, res: Response) => { if (!auth(req, res)) return; if (syncState.status === 'running') { res.status(409).json({ error: 'Sync already running', state: syncState }); return; } const mode = (req.query.mode as string) === 'full' ? 'full' : 'incremental'; syncState = { status: 'running', mode, startedAt: new Date().toISOString() }; res.json({ accepted: true, mode, message: 'Sync started — poll GET /api/etl/status for progress' }); console.log(`\nETL sync started (${mode})...`); runSync(mode, (month) => { if (syncState.status === 'running') syncState = { ...syncState, currentMonth: month }; }) .then(result => { console.log(`ETL sync complete: ${result.transactionsFetched} transactions → ${result.recordsWritten} records in ${result.duration}`); syncState = { status: 'done', result, finishedAt: new Date().toISOString() }; }) .catch(err => { console.error('ETL sync failed:', (err as Error).message); syncState = { status: 'error', error: (err as Error).message, finishedAt: new Date().toISOString() }; }); }); // GET /api/etl/status router.get('/status', (req: Request, res: Response) => { if (!auth(req, res)) return; res.json(syncState); }); export default router;