← back to Professional Directory

agents/enrich-agent/server.js

69 lines

#!/usr/bin/env node
/**
 * Enrich agent — HTTP control plane for geocoding + future enrichers.
 *
 *   GET /health           — { ok, agent, geocode_pending, enrichers }
 *   POST /run/geocode     — async-fire geocode.js with optional { limit }
 */
const fs = require('node:fs');
const path = require('path');
const { spawn } = require('node:child_process');
require('dotenv').config({ path: path.resolve(__dirname, '../../.env') });
const express = require('express');
const { pool, query } = require('../shared/db');

const PORT = Number(process.env.PORT_ENRICH || process.env.PORT || 9872);
const ENRICHERS_DIR = path.join(__dirname);
const app = express();
app.use(express.json());

// LAN/tailnet auth gate (skip /health for watchdog probes)
const BASIC_AUTH = process.env.BASIC_AUTH || 'admin:DWSecure2024!';
const AUTH_HEADER = 'Basic ' + Buffer.from(BASIC_AUTH).toString('base64');
app.use((req, res, next) => {
  if (req.path === '/health' || req.path === '/healthz') return next();
  if (req.get('authorization') === AUTH_HEADER) return next();
  res.set('WWW-Authenticate', 'Basic realm="mac2-pm2"');
  res.status(401).send('auth required');
});

function listEnrichers() {
  return fs.readdirSync(ENRICHERS_DIR)
    .filter(f => f.endsWith('.js') && f !== 'server.js')
    .map(f => f.replace(/\.js$/, ''));
}

app.get('/health', async (_req, res) => {
  try {
    const r = await query(`
      SELECT
        (SELECT COUNT(*) FROM organizations WHERE lat IS NULL AND address IS NOT NULL) AS geocode_pending,
        (SELECT COUNT(*) FROM organizations WHERE lat IS NOT NULL) AS geocoded
    `, []);
    res.json({ ok: true, agent: 'pd-enrich', enrichers: listEnrichers(), ...r.rows[0] });
  } catch (e) {
    res.status(500).json({ ok: false, error: e.message });
  }
});

app.post('/run/:enricher', (req, res) => {
  const id = String(req.params.enricher);
  if (!listEnrichers().includes(id)) {
    return res.status(404).json({ error: `unknown enricher '${id}'`, available: listEnrichers() });
  }
  const env = { ...process.env };
  if (req.body?.limit) env.LIMIT = String(req.body.limit);
  const child = spawn('node', [path.join(ENRICHERS_DIR, `${id}.js`)], {
    cwd: path.resolve(__dirname, '../..'),
    env,
    detached: true,
    stdio: 'ignore',
  });
  child.unref();
  res.json({ ok: true, started: id, pid: child.pid });
});

app.listen(PORT, '0.0.0.0', () => {
  console.log(`[pd-enrich] listening on http://127.0.0.1:${PORT}`);
});