#!/usr/bin/env node 'use strict'; // alert.js — send one alert email, reusing the server's own SMTP settings. // // Exists because backup-to-drive.sh used to report failure only into // ~/Library/Logs/checklist-drive-backup.log, where nobody was ever going to look. That is // how the Drive copies went ten weeks stale in July 2026, and then — despite the script // written to prevent exactly that — another nine days in September 2026. // // Usage: node scripts/alert.js "" "" // Exit 0 if the mail went out, 1 otherwise. Callers should NOT fail on a failed alert: // the thing being reported matters more than the reporting of it. const fs = require('fs'); const path = require('path'); const ROOT = path.resolve(__dirname, '..'); const ENV_FILE = path.join(ROOT, 'server', '.env'); // Minimal .env reader — the server uses dotenv, but this script has to run from a plain // LaunchAgent with no cwd guarantees and no node_modules of its own. function readEnv(file) { const out = {}; let text; try { text = fs.readFileSync(file, 'utf8'); } catch { return out; } for (const raw of text.split('\n')) { const line = raw.trim(); if (!line || line.startsWith('#')) continue; const eq = line.indexOf('='); if (eq < 0) continue; let v = line.slice(eq + 1).trim(); if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) v = v.slice(1, -1); out[line.slice(0, eq).trim()] = v; } return out; } async function main() { const subject = process.argv[2]; const body = process.argv[3] || ''; if (!subject) { console.error('alert.js: no subject given'); process.exit(1); } const env = readEnv(ENV_FILE); const to = env.REPORT_EMAIL; if (!to) { console.error(`alert.js: no REPORT_EMAIL in ${ENV_FILE}`); process.exit(1); } if (!env.SMTP_HOST || !env.SMTP_USER || !env.SMTP_PASS) { console.error(`alert.js: SMTP is not configured in ${ENV_FILE}`); process.exit(1); } // nodemailer lives in server/node_modules; this script has none of its own. let nodemailer; try { nodemailer = require(path.join(ROOT, 'server', 'node_modules', 'nodemailer')); } catch { console.error('alert.js: nodemailer not installed — run `npm install` in server/'); process.exit(1); } const transport = nodemailer.createTransport({ host: env.SMTP_HOST, port: Number(env.SMTP_PORT || 587), secure: String(env.SMTP_SECURE || 'false') === 'true', auth: { user: env.SMTP_USER, pass: env.SMTP_PASS }, }); await transport.sendMail({ from: env.SMTP_FROM || env.SMTP_USER, to, subject, text: `${body}\n\n-- \nSent by scripts/alert.js on ${require('os').hostname()} at ${new Date().toISOString()}\n`, }); console.log(`alert.js: sent "${subject}" to ${to}`); } main().catch(err => { console.error('alert.js:', err && err.message ? err.message : err); process.exit(1); });