← back to Allnewsdaily

server.js

95 lines

const express = require('express');
const fs = require('fs');
const path = require('path');
const { spawn } = require('child_process');

const app = express();
const PORT = process.env.PORT || 9788;

const OUTLETS_PATH = path.join(__dirname, 'data', 'outlets.json');
const LIVE_STATUS_PATH = path.join(__dirname, 'data', 'live-status.json');

function loadOutlets() {
  try {
    return JSON.parse(fs.readFileSync(OUTLETS_PATH, 'utf8'));
  } catch (e) {
    console.error('[outlets] load failed', e.message);
    return [];
  }
}

function loadLiveStatus() {
  try {
    if (!fs.existsSync(LIVE_STATUS_PATH)) return {};
    return JSON.parse(fs.readFileSync(LIVE_STATUS_PATH, 'utf8'));
  } catch (e) {
    return {};
  }
}

function mergeOutlets() {
  const outlets = loadOutlets();
  const status = loadLiveStatus();
  return outlets.map(o => {
    const s = status[o.id];
    return {
      ...o,
      isLive: s ? !!s.isLive : false,
      liveVideoId: s && s.videoId ? s.videoId : null,
      lastCheck: s ? s.checkedAt : null
    };
  });
}

app.use(express.json());
app.use('/static', express.static(path.join(__dirname, 'public')));

app.get('/', (req, res) => {
  res.sendFile(path.join(__dirname, 'public', 'index.html'));
});

app.get('/api/outlets', (req, res) => {
  res.json({ outlets: mergeOutlets(), updatedAt: new Date().toISOString() });
});

app.get('/api/health', (req, res) => {
  const outlets = loadOutlets();
  const status = loadLiveStatus();
  const liveCount = Object.values(status).filter(s => s && s.isLive).length;
  res.json({
    ok: true,
    outlets: outlets.length,
    liveOutlets: liveCount,
    lastStatusFile: fs.existsSync(LIVE_STATUS_PATH)
      ? fs.statSync(LIVE_STATUS_PATH).mtime
      : null
  });
});

app.get('/api/regions', (req, res) => {
  const outlets = loadOutlets();
  const regions = [...new Set(outlets.map(o => o.region))].sort();
  const countries = [...new Set(outlets.map(o => o.country))].sort();
  const langs = [...new Set(outlets.map(o => o.lang))].sort();
  const categories = [...new Set(outlets.map(o => o.category))].sort();
  res.json({ regions, countries, langs, categories });
});

function runChecker() {
  const proc = spawn('node', [path.join(__dirname, 'scripts', 'check-live.js')], {
    stdio: ['ignore', 'pipe', 'pipe']
  });
  proc.stdout.on('data', d => process.stdout.write('[checker] ' + d));
  proc.stderr.on('data', d => process.stderr.write('[checker] ' + d));
  proc.on('exit', code => console.log('[checker] exit ' + code));
}

const POLL_INTERVAL_MS = parseInt(process.env.POLL_INTERVAL_MS || '90000', 10);

app.listen(PORT, () => {
  console.log(`allnewsdaily listening on http://0.0.0.0:${PORT}`);
  console.log(`outlets loaded: ${loadOutlets().length}`);
  runChecker();
  setInterval(runChecker, POLL_INTERVAL_MS);
});