diff --git a/autopost/server.js b/autopost/server.js index 06f84e9..8c17185 100644 --- a/autopost/server.js +++ b/autopost/server.js @@ -9,12 +9,13 @@ const { exec } = require('child_process'); const os = require('os'); const config = require('./config'); const db = require('./db'); - +const chokidar = require('chokidar'); db.testConnection(); // vérification au démarrage const app = express(); const port = config.port; +const background_color = (config?.background_color ?? '').trim() || 'slate-900'; // Middleware pour parser les formulaires POST app.use(express.urlencoded({ extended: true })); @@ -57,7 +58,7 @@ autopostRouter.get('/login', (req, res) => { - +

Authentification

@@ -100,6 +101,109 @@ function checkAuth(req, res, next) { } autopostRouter.use(checkAuth); +// ---- Long-polling refresh (robuste) ---- +let lpVersion = 1; +const lpWaiters = new Set(); +const LP_TIMEOUT_MS = 25000; + +function lpNotify(source, filePath) { + lpVersion += 1; + console.log('[LP] notify', { version: lpVersion, source, file: filePath }); + for (const waiter of lpWaiters) { + clearTimeout(waiter.timer); + try { waiter.res.json({ version: lpVersion }); } catch (_) {} + } + lpWaiters.clear(); +} + +// Endpoint long-polling +autopostRouter.get('/updates', (req, res) => { + const since = parseInt(req.query.since, 10) || 0; + + if (lpVersion > since) { + return res.json({ version: lpVersion }); + } + + const waiter = { + res, + timer: setTimeout(() => { + lpWaiters.delete(waiter); + res.json({ version: lpVersion }); // heartbeat/timeout + }, LP_TIMEOUT_MS) + }; + + lpWaiters.add(waiter); + + req.on('close', () => { + clearTimeout(waiter.timer); + lpWaiters.delete(waiter); + }); +}); + +// Watcher fichiers (JSON / BDINFO) + fallback scan +const infoDir = path.resolve(config.infodirectory); +const watchPatterns = [ + path.join(infoDir, '*.json'), + path.join(infoDir, '*.JSON'), + path.join(infoDir, '*.bdinfo.txt'), + path.join(infoDir, '*.BDINFO.TXT') +]; + +const lpWatcher = chokidar.watch(watchPatterns, { + ignoreInitial: true, + awaitWriteFinish: { stabilityThreshold: 1200, pollInterval: 250 }, + usePolling: true, // important pour NFS/SMB/Docker + interval: 1000, + depth: 0, + ignorePermissionErrors: true, + persistent: true +}); + +lpWatcher.on('add', (filePath) => lpNotify('add', filePath)); +lpWatcher.on('change', (filePath) => lpNotify('change', filePath)); +lpWatcher.on('ready', () => console.log('[LP] watcher ready on', watchPatterns)); +lpWatcher.on('error', (err) => console.error('[LP] watcher error:', err)); + +// Fallback scan (toutes les 2s) : signature (nb fichiers + dernier mtime) +const fsp = fs.promises; // réutilise ton import fs existant +let lastSig = null; + +async function computeSignature() { + try { + const names = await fsp.readdir(infoDir); + const files = names.filter(n => /\.json$/i.test(n) || /\.bdinfo\.txt$/i.test(n)); + let latest = 0; + await Promise.all(files.map(async (n) => { + try { + const st = await fsp.stat(path.join(infoDir, n)); + if (st.mtimeMs > latest) latest = st.mtimeMs; + } catch (_) {} + })); + return files.length + ':' + Math.floor(latest); + } catch { + return '0:0'; + } +} + +async function periodicScan() { + try { + const sig = await computeSignature(); + if (lastSig === null) { + lastSig = sig; // baseline + } else if (sig !== lastSig) { + lastSig = sig; + lpNotify('scan', infoDir); + } + } catch (e) { + console.error('[LP] periodic scan error:', e.message || e); + } finally { + setTimeout(periodicScan, 2000); + } +} +periodicScan(); + + + // --------------------------- PAGE PRINCIPALE ----------------------------- autopostRouter.get('/', async (req, res) => { const limit = 100; // enregistrements par page @@ -107,6 +211,7 @@ autopostRouter.get('/', async (req, res) => { const offset = (page - 1) * limit; try { + const [stats] = await db.query(` SELECT COUNT(*) AS total, @@ -116,7 +221,7 @@ autopostRouter.get('/', async (req, res) => { SUM(status = 3) AS deja, SUM(status = 4) AS encours FROM \`${config.DB_TABLE}\` - `); + `); // Récupérer le nombre total d'enregistrements const [countResult] = await db.query(`SELECT COUNT(*) as total FROM \`${config.DB_TABLE}\``); const totalRecords = countResult[0].total; @@ -133,11 +238,11 @@ autopostRouter.get('/', async (req, res) => { Suivi Autopost ${config.name} - - + + - +
@@ -272,7 +377,7 @@ autopostRouter.get('/', async (req, res) => { `; - + rows.forEach(row => { let statusText = ''; let statusClass = ''; @@ -325,8 +430,8 @@ autopostRouter.get('/', async (req, res) => { `; }); - - + + html += ` @@ -445,6 +550,9 @@ autopostRouter.get('/', async (req, res) => { + + +