/**
* 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', 'statopca', '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();
// Calcule le prochain 06:45 heure Europe/Paris
const parisOffset = (() => {
const u = new Date(now.toLocaleString('en-US', { timeZone: 'UTC' }));
const p = new Date(now.toLocaleString('en-US', { timeZone: 'Europe/Paris' }));
return (p - u) / 3600000; // heures
})();
const next = new Date(now);
next.setHours(6 - parisOffset, 45, 0, 0); // 06:45 Paris → heure UTC serveur
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
| Table | Dernière sync | Lignes | Statut |
| Chargement… |
`;