← back to Zendesk Chat Analyzer

server.js

54 lines

#!/usr/bin/env node
// Minimal static server for the DW Chat Analyzer + /api/refresh (re-runs pull.py).
// No external deps. Token stays server-side (pull.py reads secrets .env).
const http = require('http'), fs = require('fs'), path = require('path');
const { execFile } = require('child_process');
const ROOT = path.join(__dirname, 'public');
const PORT = process.env.PORT || 9856;
const TYPES = { '.html':'text/html', '.js':'text/javascript', '.json':'application/json', '.css':'text/css' };

http.createServer((req, res) => {
  if (req.method === 'GET' && req.url.startsWith('/api/recent')) {
    const hours = (new URL(req.url, 'http://x').searchParams.get('hours')) || '8';
    execFile('/usr/bin/python3', [path.join(__dirname, 'recent.py')],
      { env: { ...process.env, HOURS: String(hours) }, maxBuffer: 8 * 1024 * 1024 },
      (err, so, se) => {
        res.writeHead(err ? 500 : 200, { 'content-type': 'application/json' });
        res.end(err ? JSON.stringify({ error: (se||'').trim() || String(err), chats: [] }) : (so || '{"chats":[]}'));
      });
    return;
  }
  if (req.method === 'POST' && req.url === '/api/refresh') {
    execFile('/usr/bin/python3', [path.join(__dirname, 'pull.py')],
      { env: { ...process.env, START: '1770000000', MAX_PAGES: '40' }, maxBuffer: 8 * 1024 * 1024 },
      (err, so, se) => {
        res.writeHead(err ? 500 : 200, { 'content-type': 'application/json' });
        res.end(JSON.stringify({ ok: !err, out: (so||'').trim(), err: (se||'').trim() }));
      });
    return;
  }
  let p = decodeURIComponent(req.url.split('?')[0]);
  if (p === '/') p = '/index.html';
  const fp = path.join(ROOT, path.normalize(p).replace(/^(\.\.[/\\])+/, ''));
  if (!fp.startsWith(ROOT)) { res.writeHead(403); return res.end(); }
  fs.readFile(fp, (e, buf) => {
    if (e) { res.writeHead(404); return res.end('not found'); }
    res.writeHead(200, { 'content-type': TYPES[path.extname(fp)] || 'application/octet-stream' });
    res.end(buf);
  });
}).listen(PORT, '127.0.0.1', () => console.log('DW Chat Analyzer → http://127.0.0.1:' + PORT + ' (localhost-only; public access via nginx+auth)'));

// Keep the dashboard data fresh: refresh data.json on boot (if stale) + every REFRESH_HOURS.
const REFRESH_HOURS = Number(process.env.REFRESH_HOURS || 6);
function refreshData(reason) {
  execFile('/usr/bin/python3', [path.join(__dirname, 'pull.py')],
    { env: { ...process.env, START: '1760000000', MAX_PAGES: '80' }, maxBuffer: 32 * 1024 * 1024 },
    (err, so) => console.log(`[auto-refresh ${reason}] ` + (err ? 'FAILED: ' + err : (so || '').trim())));
}
try {
  const dj = path.join(ROOT, 'data.json');
  const stale = !fs.existsSync(dj) || (Date.now() - fs.statSync(dj).mtimeMs) > REFRESH_HOURS * 3600 * 1000;
  if (stale) setTimeout(() => refreshData('boot'), 4000);
} catch (e) {}
setInterval(() => refreshData('interval'), REFRESH_HOURS * 3600 * 1000);