← back to Purelymail Fleet Viewer

run-receive-test.js

72 lines

#!/usr/bin/env node
/* Purelymail 211-domain receive test.
 * Sends one tagged probe to each info@<domain> via George's /api/send (over tailnet),
 * ~3s apart, logs every send, updates the viewer's status file live, then waits
 * 4 min for catch-all forwarding before exiting.
 *
 *   GEORGE_ADMIN_PASS=... node run-receive-test.js
 */
'use strict';
const fs = require('fs');
const os = require('os');
const path = require('path');

const HEALTH = path.join(os.homedir(), 'Projects/email-deliverability-agent/output/domain-health.json');
const STATUS = path.join(__dirname, 'data/remediation-status.json');
const SENDS  = path.join(__dirname, 'data/receive-test-sends.jsonl');
const RUNID_F= path.join(__dirname, 'data/receive-test-runid.txt');
const GEORGE = 'http://100.107.67.67:9850/api/send';
const PASS = process.env.GEORGE_ADMIN_PASS;
if (!PASS) { console.error('GEORGE_ADMIN_PASS not set'); process.exit(1); }
const AUTH = 'Basic ' + Buffer.from('admin:' + PASS).toString('base64');

const runId = 'PMRCV-' + new Date().toISOString().replace(/[-:T.]/g, '').slice(0, 14);
const domains = JSON.parse(fs.readFileSync(HEALTH, 'utf8')).domains.map(d => d.domain);
fs.writeFileSync(SENDS, '');
fs.writeFileSync(RUNID_F, runId);

function patch(fields) {
  try {
    const s = JSON.parse(fs.readFileSync(STATUS, 'utf8'));
    Object.assign(s.receiveTest, fields);
    s.receiveTest.runId = runId;
    s.updatedAt = new Date().toISOString();
    fs.writeFileSync(STATUS, JSON.stringify(s, null, 2));
  } catch (e) { /* non-fatal */ }
}
const sleep = ms => new Promise(r => setTimeout(r, ms));

(async () => {
  let sent = 0, okCount = 0;
  for (const domain of domains) {
    const subject = `PMRCV ${runId} ${domain}`;
    const body = `<p>Purelymail receive probe. run=${runId} domain=${domain}. If this forwarded into the unified Gmail, ${domain}'s catch-all is working.</p>`;
    let result;
    try {
      const res = await fetch(GEORGE, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'Authorization': AUTH },
        // Send from agentabrams (NOT steve-office) — the catch-all forwards into
        // steve-office's inbox, so sending from a different account avoids Gmail's
        // same-Message-ID dedup that silently swallowed the v1 probes.
        body: JSON.stringify({ account: 'agentabrams', to: 'info@' + domain, subject, body }),
      });
      const j = await res.json();
      result = { domain, ok: !!j.success, messageId: j.messageId || null, error: j.error || null };
    } catch (e) {
      result = { domain, ok: false, messageId: null, error: String(e.message || e) };
    }
    if (result.ok) okCount++;
    fs.appendFileSync(SENDS, JSON.stringify(result) + '\n');
    sent++;
    if (sent % 5 === 0 || sent === domains.length) {
      patch({ done: sent, note: `sending — ${sent}/${domains.length} probes out (${okCount} accepted)` });
    }
    await sleep(3000);
  }
  patch({ done: sent, note: `${sent} probes sent (${okCount} accepted) — waiting 4 min for catch-all forwarding` });
  await sleep(240000);
  patch({ done: sent, note: `${sent} sent — forwarding window elapsed, ready for Gmail tabulation`, sendPhaseComplete: true });
  console.log(`receive-test send phase complete: ${sent} sent, ${okCount} accepted, runId=${runId}`);
})();