← back to Re Coverage Dashboard
server.js
139 lines
#!/usr/bin/env node
/**
* RE Contact-Coverage dashboard — liquid-fill gauges showing how the $0/local
* agent contact-discovery is filling in phone / firm / site / agent-site coverage
* across the RE builds (usre + CRCP), plus a wins/milestone feed.
*
* Zero deps: shells to psql for usre counts, reads CRCP data/agent-sites.json.
* Read-only. Milestones appended to data/milestones.jsonl as coverage crosses
* thresholds (the "track wins" ask); big ones also POST to CNCP.
*/
const http = require('http');
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
const PORT = process.env.PORT || 9792;
const ROOT = __dirname;
const CRCP_SITES = process.env.HOME + '/Projects/commercialrealestate/data/agent-sites.json';
const MILESTONES = path.join(ROOT, 'data', 'milestones.jsonl');
// Known CRCP roster size — update when the source list grows.
const CRCP_TOTAL_AGENTS = 2022;
// Minimum milestone value that also fires a CNCP win POST.
const CNCP_WIN_MIN_VALUE = 50;
fs.mkdirSync(path.join(ROOT, 'data'), { recursive: true });
function psql1(db, sql) {
try { return execSync(`psql -h /tmp ${db} -tAc ${JSON.stringify(sql)}`, { encoding: 'utf8', timeout: 8000 }).trim(); }
catch { return ''; }
}
const nums = s => s.split('|').map(x => parseInt(x, 10) || 0);
function coverage() {
// usre broker + firm
const b = nums(psql1('usre', `SELECT count(*), count(*) FILTER (WHERE firm_id IS NOT NULL), count(website_status), count(website) FROM broker`));
const f = nums(psql1('usre', `SELECT count(*), (SELECT count(url) FROM firm_site) FROM firm`));
const [bTotal, bFirm, bAttempted, bSite] = b.length === 4 ? b : [0, 0, 0, 0];
const [fTotal, fSite] = f.length === 2 ? f : [0, 0];
// CRCP agent-sites
let cAttempted = 0, cFound = 0, cPhone = 0, cTotal = CRCP_TOTAL_AGENTS;
try {
const s = JSON.parse(fs.readFileSync(CRCP_SITES, 'utf8'));
const v = Object.values(s);
cAttempted = v.length; cFound = v.filter(x => x.status === 'found').length; cPhone = v.filter(x => x.phone).length;
} catch {}
// HomesOnSpec (builder-direct spec homes — Mac2 local DB). Builder = the firm here,
// so coverage is naturally high (spec homes carry a listing URL + builder site).
const h = nums(psql1('homesonspec', `SELECT (SELECT count(*) FROM "Builder"), (SELECT count(*) FROM "Builder" WHERE "websiteUrl" IS NOT NULL AND "websiteUrl"<>''), (SELECT count(*) FROM "InventoryHome" WHERE status='PUBLISHED'), (SELECT count(*) FROM "InventoryHome" WHERE status='PUBLISHED' AND "sourceUrl" IS NOT NULL), (SELECT count(*) FROM "Community"), (SELECT count(*) FROM "Community" WHERE "salesPhone" IS NOT NULL)`));
const [hBTot, hBSite, hHomes, hHomeUrl, hCTot, hCPhone] = h.length === 6 ? h : [0, 0, 0, 0, 0, 0];
const pct = (n, d) => d > 0 ? Math.round((n / d) * 1000) / 10 : 0;
const metrics = [
{ key: 'usre_firm', group: 'usre', label: 'Agents tied to a firm', pct: pct(bFirm, bTotal), num: bFirm, den: bTotal, unit: 'agents' },
{ key: 'usre_firmsite', group: 'usre', label: 'Firms with a website', pct: pct(fSite, fTotal), num: fSite, den: fTotal, unit: 'firms' },
{ key: 'usre_swept', group: 'usre', label: 'Agent sweep progress', pct: pct(bAttempted, bTotal), num: bAttempted, den: bTotal, unit: 'agents searched' },
{ key: 'usre_agentsite', group: 'usre', label: 'Agent own-sites found', pct: pct(bSite, Math.max(bAttempted, 1)), num: bSite, den: bAttempted, unit: 'of searched' },
{ key: 'crcp_swept', group: 'CRCP', label: 'CRCP agents searched', pct: pct(cAttempted, cTotal), num: cAttempted, den: cTotal, unit: 'agents' },
{ key: 'crcp_site', group: 'CRCP', label: 'CRCP agent sites found', pct: pct(cFound, Math.max(cAttempted, 1)), num: cFound, den: cAttempted, unit: 'of searched' },
{ key: 'crcp_phone', group: 'CRCP', label: 'CRCP phones found', pct: pct(cPhone, Math.max(cAttempted, 1)), num: cPhone, den: cAttempted, unit: 'of searched' },
{ key: 'hos_buildersite', group: 'HomesOnSpec', label: 'Builders with a website', pct: pct(hBSite, hBTot), num: hBSite, den: hBTot, unit: 'builders' },
{ key: 'hos_listing', group: 'HomesOnSpec', label: 'Homes with a listing link', pct: pct(hHomeUrl, hHomes), num: hHomeUrl, den: hHomes, unit: 'homes' },
{ key: 'hos_phone', group: 'HomesOnSpec', label: 'Communities with a phone', pct: pct(hCPhone, hCTot), num: hCPhone, den: hCTot, unit: 'communities' },
];
const totals = {
sites_found: bSite + cFound,
phones_found: cPhone,
searched: bAttempted + cAttempted,
firm_linked: bFirm,
};
checkMilestones(totals);
return { updated: new Date().toISOString(), metrics, totals };
}
// "track wins": append a milestone when a running total crosses a threshold.
function readMilestones() {
try { return fs.readFileSync(MILESTONES, 'utf8').trim().split('\n').filter(Boolean).flatMap(l => { try { return [JSON.parse(l)]; } catch { return []; } }); }
catch { return []; }
}
function checkMilestones(t) {
const done = new Set(readMilestones().map(m => m.id));
const bands = { sites_found: 10, phones_found: 5, searched: 100 };
const labels = { sites_found: 'agent/firm sites found', phones_found: 'phone numbers found', searched: 'agents searched' };
const out = [];
for (const [metric, step] of Object.entries(bands)) {
const crossed = Math.floor((t[metric] || 0) / step) * step;
if (crossed >= step) {
const id = `${metric}:${crossed}`;
if (!done.has(id)) out.push({ id, ts: new Date().toISOString(), metric, value: crossed, title: `${crossed.toLocaleString()} ${labels[metric]}` });
}
}
if (out.length) {
fs.appendFileSync(MILESTONES, out.map(m => JSON.stringify(m)).join('\n') + '\n');
// big round milestones also land as a CNCP win (best-effort, non-blocking)
for (const m of out) if (m.value >= CNCP_WIN_MIN_VALUE) postWin(m).catch(() => {});
}
}
function postWin(m) {
const body = JSON.stringify({ project: 're-coverage', title: `RE contact coverage: ${m.title}`, summary: `$0/local discovery crossed ${m.title} across usre + CRCP.` });
return new Promise((res, rej) => {
const req = http.request('http://127.0.0.1:3333/api/wins', { method: 'POST', headers: { 'Content-Type': 'application/json' } }, r => { r.resume(); r.on('end', res); });
req.on('error', rej); req.write(body); req.end();
});
}
// Whole-site Basic Auth — required before any public (agentabrams.com) exposure so the
// dashboard + discovery internals aren't world-readable. /healthz stays open for the
// CF-tunnel health probe. Creds env-overridable (COV_USER/COV_PASS); house default.
const AUTH = 'Basic ' + Buffer.from((process.env.COV_USER || 'admin') + ':' + (process.env.COV_PASS || 'DW2024!')).toString('base64');
const server = http.createServer((req, res) => {
if (req.url === '/healthz') { res.end('ok'); return; }
if (req.url === '/favicon.ico') { // inline SVG favicon — no 404, no auth needed
res.setHeader('Content-Type', 'image/svg+xml');
res.end("<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><rect width='16' height='16' rx='3' fill='#0e1116'/><text x='8' y='12' font-size='11' text-anchor='middle'>🌊</text></svg>");
return;
}
if ((req.headers.authorization || '') !== AUTH) {
res.statusCode = 401; res.setHeader('WWW-Authenticate', 'Basic realm="RE Coverage", charset="UTF-8"');
res.end('Authentication required'); return;
}
if (req.url === '/api/coverage') { res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify(coverage())); return; }
if (req.url === '/api/wins') { res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({ wins: readMilestones().reverse() })); return; }
const file = req.url === '/' ? '/index.html' : req.url.split('?')[0];
const fp = path.join(ROOT, 'public', file);
if (fp.startsWith(path.join(ROOT, 'public')) && fs.existsSync(fp) && fs.statSync(fp).isFile()) {
if (file.endsWith('.html')) {
// Inject env-driven GA4 + FB-Pixel ids (per the DW build standard). Empty ids
// = the loaders no-op; set GA4_ID / FB_PIXEL_ID in the launchd plist env to arm.
let html = fs.readFileSync(fp, 'utf8');
const cfg = `<script>window.__A={ga4:${JSON.stringify(process.env.GA4_ID || '')},fb:${JSON.stringify(process.env.FB_PIXEL_ID || '')}};</script>`;
html = html.replace('</head>', cfg + '</head>');
res.setHeader('Content-Type', 'text/html'); res.end(html); return;
}
res.setHeader('Content-Type', 'text/plain');
res.end(fs.readFileSync(fp)); return;
}
res.statusCode = 404; res.end('not found');
});
server.listen(PORT, () => console.log(`RE coverage dashboard → http://127.0.0.1:${PORT}`));