← back to Doing Viewer
server-mirror.js
56 lines
#!/usr/bin/env node
// server-mirror.js (runs on KAMATERA — always-on public face)
// Serves the board purely from data/snapshot.json pushed by Mac2. No ticket log,
// no Ollama, no lib.js. If Mac2 stops syncing, it keeps serving the last snapshot
// with a staleness flag the UI surfaces. Same front-end as the primary.
const http = require('http');
const fs = require('fs');
const path = require('path');
const PORT = process.env.PORT || 9802;
const SNAP = path.join(__dirname, 'data', 'snapshot.json');
const INDEX = fs.readFileSync(path.join(__dirname, 'public', 'index.html'), 'utf8');
const AUTH_USER = process.env.BASIC_USER || 'admin';
const AUTH_PASS = process.env.BASIC_PASS || 'DW2024!';
function authed(req) {
const h = req.headers['authorization'] || '';
if (!h.startsWith('Basic ')) return false;
const [u, p] = Buffer.from(h.slice(6), 'base64').toString().split(':');
return u === AUTH_USER && p === AUTH_PASS;
}
function readSnap() {
try { return JSON.parse(fs.readFileSync(SNAP, 'utf8')); }
catch { return { syncedAt: null, source: 'mac2', count: 0, items: [] }; }
}
http.createServer((req, res) => {
// health check is open (no auth) so deploy.sh smoke test + monitors work
if (req.url === '/api/healthz') {
const s = readSnap();
const ageMin = s.syncedAt ? (Date.now() - new Date(s.syncedAt).getTime()) / 60000 : null;
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ ok: true, mirror: true, count: s.count, syncedAt: s.syncedAt, syncAgeMin: ageMin }));
return;
}
if (!authed(req)) {
res.writeHead(401, { 'WWW-Authenticate': 'Basic realm="doing-viewer (mirror)"' });
res.end('auth required'); return;
}
if (req.url === '/api/doing') {
const s = readSnap();
res.writeHead(200, { 'Content-Type': 'application/json', 'Cache-Control': 'no-store' });
res.end(JSON.stringify({
now: new Date().toISOString(), count: s.count, pending: 0,
mirror: true, canAct: false, syncedAt: s.syncedAt, items: s.items,
}));
return;
}
if (req.url === '/' || req.url === '/index.html') {
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.end(INDEX); return;
}
res.writeHead(404); res.end('not found');
}).listen(PORT, () => console.log(`doing-viewer MIRROR on :${PORT}`));