lib/george.mjs
// George send helper. Resolves creds at runtime from the canonical secrets (never hardcoded).
import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path';
function envFrom(p){ const o={}; try{ for(const l of fs.readFileSync(p,'utf8').split('\n')){ const m=l.match(/^([A-Z0-9_]+)=(.*)$/); if(m) o[m[1]]=m[2].replace(/^["']|["']$/g,''); } }catch{} return o; }
const H=os.homedir();
const sec=envFrom(path.join(H,'Projects/secrets-manager/.env'));
const gg=envFrom(path.join(H,'Projects/george-gmail/.env'));
const GEORGE_URL=process.env.GEORGE_URL||'http://127.0.0.1:9850';
function basicAuth(){ // admin:<pass> — same resolution as dw-signup-fulfillment
let v = process.env.GEORGE_BASIC_AUTH || sec.GEORGE_AUTH || gg.GEORGE_BASIC_AUTH || (sec.GEORGE_BASIC_AUTH_PASS?('admin:'+sec.GEORGE_BASIC_AUTH_PASS):'') || '';
if(v && !v.includes(':')) v='admin:'+v;
return v ? 'Basic '+Buffer.from(v).toString('base64') : '';
}
const token = process.env.GEORGE_EXTERNAL_SEND_TOKEN || gg.GEORGE_EXTERNAL_SEND_TOKEN || sec.GEORGE_EXTERNAL_SEND_TOKEN || '';
export async function send({to,subject,html,account='steve-personal',dry=false}){
if(dry) return {dry:true,to,subject};
const r=await fetch(GEORGE_URL+'/api/send',{method:'POST',headers:{'Content-Type':'application/json','Authorization':basicAuth(),'X-Send-Approval':token},body:JSON.stringify({account,to,subject,body:html})});
const t=await r.text(); return {ok:r.ok,status:r.status,body:t.slice(0,200)};
}
export function daysToDeadline(deadline){ const d=new Date(deadline+'T23:59:59-05:00'); return Math.ceil((d-Date.now())/86400000); }