← back to Nationalrealestate

src/jobs/refresh_all.ts

38 lines

/**
 * Monthly refresh orchestrator (launchd com.steve.usre-refresh):
 * zillow -> redfin -> fhfa -> acs -> derive -> score -> alerts.
 * Every job is an idempotent upsert, so re-running any month is safe; jobs run
 * as child processes because each script owns its pool lifecycle (pool.end()).
 * A failed job logs + continues (per-source failure shows red in ingest_runs;
 * one dead source must not starve the others).
 */
import { execFileSync } from 'node:child_process';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';

const __dirname = dirname(fileURLToPath(import.meta.url));
const ROOT = join(__dirname, '..', '..');

const JOBS: Array<[string, string]> = [
  ['zillow', 'src/ingest/zillow_research.ts'],
  ['redfin', 'src/ingest/redfin_tracker.ts'],
  ['fhfa', 'src/ingest/fhfa_hpi.ts'],
  ['acs', 'src/ingest/census_acs.ts'],
  ['derive', 'src/ingest/derive_metrics.ts'],
  ['score', 'src/score/opportunity.ts'],
  ['alerts', 'src/jobs/alerts_check.ts'],
];

let failed = 0;
for (const [name, script] of JOBS) {
  console.log(`\n[refresh] ── ${name} ──`);
  try {
    execFileSync('npx', ['tsx', script], { cwd: ROOT, stdio: 'inherit', timeout: 45 * 60_000 });
  } catch (e: any) {
    failed++;
    console.error(`[refresh] ${name} FAILED: ${e.message}`);
  }
}
console.log(`\n[refresh] complete, ${failed} job(s) failed`);
process.exit(failed > 0 ? 1 : 0);