/** * Tableau de bord web du service sync — port 3001 */ 'use strict'; const express = require('express'); const { getPg } = require('./db'); const app = express(); const PORT = process.env.SYNC_DASHBOARD_PORT || 3001; // ── État global ────────────────────────────────────────────── const state = { running: false, progress: null, // { current, index, total, startedAt, tables } lastError: null, }; const TABLES_ORDER = [ 'referentiel', 'articles', 'fournisseurs', 'stock', 'mouvements', 'commandes', 'ranking', 'stat_dispoperm', 'phenix_quantite_conseille', 'appro' ]; const TABLES_TOTAL = TABLES_ORDER.length; // Buffer des 300 dernières lignes de log const logBuffer = []; function pushLog(msg, level = 'info') { logBuffer.push({ ts: new Date().toISOString(), msg, level }); if (logBuffer.length > 300) logBuffer.shift(); } // Surcharge console const _log = console.log.bind(console); const _err = console.error.bind(console); console.log = (...a) => { pushLog(a.join(' '), 'info'); _log(...a); }; console.error = (...a) => { pushLog(a.join(' '), 'error'); _err(...a); }; // Appelé depuis index.js avant/après chaque groupe de tables function setProgress(tableName, index) { state.progress = { current: tableName, index: index, // 1-based total: TABLES_TOTAL, pct: Math.round((index / TABLES_TOTAL) * 100), startedAt: state.progress?.startedAt || new Date().toISOString(), }; } function clearProgress() { state.progress = null; } // Fn sync enregistrée depuis index.js let syncAllFn = null; function registerSyncFn(fn) { syncAllFn = fn; } function setSyncRunning(val) { state.running = val; } function isSyncRunning() { return state.running; } // ── Routes API ─────────────────────────────────────────────── app.use(express.json()); // Statut complet (tables + progression en cours) app.get('/api/status', async (req, res) => { try { const pg = getPg(); const { rows } = await pg.query( `SELECT table_name, last_sync, rows_synced, status, error_msg FROM sync_log ORDER BY table_name` ); res.json({ running: state.running, progress: state.progress, tables: rows, }); } catch (err) { res.status(500).json({ error: err.message }); } }); // Logs en mémoire app.get('/api/logs', (req, res) => { const limit = parseInt(req.query.limit) || 150; res.json(logBuffer.slice(-limit)); }); // Prochain cron app.get('/api/next', (req, res) => { const now = new Date(); const next = new Date(now); next.setHours(6, 45, 0, 0); if (next <= now) next.setDate(next.getDate() + 1); res.json({ next: next.toISOString() }); }); // Déclencher sync manuelle app.post('/api/sync/run', async (req, res) => { if (state.running) return res.status(409).json({ error: 'Sync déjà en cours' }); if (!syncAllFn) return res.status(500).json({ error: 'syncAll non enregistré' }); res.json({ message: 'Sync lancée' }); syncAllFn(true).catch(err => console.error('Erreur sync manuelle:', err.message)); }); // ── Dashboard HTML ─────────────────────────────────────────── app.get('/', (req, res) => res.send(HTML)); function startDashboard() { app.listen(PORT, '0.0.0.0', () => { console.log(`Dashboard sync : http://0.0.0.0:${PORT}`); }); } module.exports = { startDashboard, registerSyncFn, setSyncRunning, isSyncRunning, setProgress, clearProgress }; // ── HTML inline ────────────────────────────────────────────── const HTML = ` Sync Dashboard

⚡ Sync Dashboard

Idle
Synchronisation en cours… 0%
Statut des tables
TableDernière syncLignesStatut
Chargement…
Logs
Chargement…
`;