← back to Wallpaper Tour

preflight.mjs

67 lines

#!/usr/bin/env node
// Pre-flight: HEAD each https://<name>.com with 8s timeout, keep live ones.
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const projects = fs.readFileSync(path.join(__dirname, 'projects.txt'), 'utf8')
  .split('\n').map(s => s.trim()).filter(Boolean);

const TIMEOUT_MS = 8000;

async function checkUrl(name) {
  const url = `https://${name}.com`;
  const ctrl = new AbortController();
  const timer = setTimeout(() => ctrl.abort(), TIMEOUT_MS);
  try {
    // Try HEAD first; some servers reject HEAD, fall back to GET range
    let res;
    try {
      res = await fetch(url, { method: 'HEAD', redirect: 'follow', signal: ctrl.signal });
    } catch (_) {
      res = await fetch(url, { method: 'GET', redirect: 'follow', signal: ctrl.signal, headers: { Range: 'bytes=0-1023' } });
    }
    clearTimeout(timer);
    if (res.status >= 200 && res.status < 400) {
      return { name, url: res.url || url, status: res.status, live: true };
    }
    // 405 (method not allowed) usually means HEAD-rejected — try GET
    if (res.status === 405) {
      const ctrl2 = new AbortController();
      const t2 = setTimeout(() => ctrl2.abort(), TIMEOUT_MS);
      try {
        const r2 = await fetch(url, { method: 'GET', redirect: 'follow', signal: ctrl2.signal });
        clearTimeout(t2);
        if (r2.status >= 200 && r2.status < 400) return { name, url: r2.url || url, status: r2.status, live: true };
        return { name, url, status: r2.status, live: false };
      } catch (e) {
        clearTimeout(t2);
        return { name, url, status: 0, live: false, error: e.message };
      }
    }
    return { name, url, status: res.status, live: false };
  } catch (e) {
    clearTimeout(timer);
    return { name, url, status: 0, live: false, error: e.message };
  }
}

const results = [];
// Sequential to be gentle (Steve says load avg ~500)
let i = 0;
for (const name of projects) {
  i++;
  process.stdout.write(`[${i}/${projects.length}] ${name} ... `);
  const r = await checkUrl(name);
  results.push(r);
  console.log(r.live ? `LIVE (${r.status})` : `DEAD (${r.status}${r.error ? ' ' + r.error : ''})`);
}

const live = results.filter(r => r.live);
fs.writeFileSync(path.join(__dirname, 'live-urls.txt'), live.map(r => `${r.name}\t${r.url}`).join('\n') + '\n');
fs.writeFileSync(path.join(__dirname, 'preflight-results.json'), JSON.stringify(results, null, 2));

console.log(`\nLIVE: ${live.length}/${results.length}`);
console.log(`Wrote ${path.join(__dirname, 'live-urls.txt')}`);