[object Object]

← back to Beverlyhillsvideos

poster: add read-only BHV token-health watchdog (Cody FIX-FIRST #2)

e7027024d3ba43c344d5d65eca6268e5e184ec94 · 2026-08-06 16:25:38 -0700 · Steve Abrams

Files touched

Diff

commit e7027024d3ba43c344d5d65eca6268e5e184ec94
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Aug 6 16:25:38 2026 -0700

    poster: add read-only BHV token-health watchdog (Cody FIX-FIRST #2)
---
 social/poster/token-health.mjs | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/social/poster/token-health.mjs b/social/poster/token-health.mjs
new file mode 100644
index 0000000..3728f55
--- /dev/null
+++ b/social/poster/token-health.mjs
@@ -0,0 +1,36 @@
+#!/usr/bin/env node
+// token-health.mjs — READ-ONLY check that the BHV Graph API token is alive + not near
+// expiry. The 6-hour poster fails SILENTLY if the token dies (launchd doesn't alert),
+// so this is the missing watchdog (Cody FIX-FIRST #2). Exit 0 healthy, 1 dead/expiring.
+// Wire to a launchd job + George email on non-zero (gated install — see pending-approval).
+import fs from 'node:fs';
+import os from 'node:os';
+import path from 'node:path';
+
+const env = fs.readFileSync(path.join(os.homedir(), 'Projects/secrets-manager/.env'), 'utf8');
+const g = (k) => { let v = null; for (const l of env.split('\n')) { const m = l.match(/^([A-Z0-9_]+)=(.*)$/); if (m && m[1] === k) v = m[2]; } return v; };
+const tok = g('BHV_IG_ACCESS_TOKEN'), ig = g('BHV_IG_USER_ID'), ver = g('BHV_IG_GRAPH_VERSION') || 'v21.0';
+
+const WARN_DAYS = 10; // warn if expiry within this window
+
+(async () => {
+  if (!tok || !ig) { console.log('DEAD: missing BHV_IG token/user id'); process.exit(1); }
+  try {
+    // 1) can the token still read the IG account?
+    const r = await fetch(`https://graph.facebook.com/${ver}/${ig}?fields=username&access_token=${tok}`);
+    const j = await r.json();
+    if (j.error) { console.log(`DEAD: ${JSON.stringify(j.error).slice(0, 160)}`); process.exit(1); }
+    // 2) how long until it expires?
+    const d = await (await fetch(`https://graph.facebook.com/${ver}/debug_token?input_token=${tok}&access_token=${tok}`)).json();
+    const exp = d?.data?.expires_at;
+    if (exp && exp > 0) {
+      const days = Math.round((exp * 1000 - Date.now()) / 864e5);
+      if (days <= 0) { console.log('DEAD: token expired'); process.exit(1); }
+      if (days <= WARN_DAYS) { console.log(`EXPIRING: @${j.username} token expires in ${days}d`); process.exit(1); }
+      console.log(`HEALTHY: @${j.username} token OK, expires in ${days}d`);
+    } else {
+      console.log(`HEALTHY: @${j.username} token OK (no expiry / long-lived)`);
+    }
+    process.exit(0);
+  } catch (e) { console.log(`DEAD: ${e.message}`); process.exit(1); }
+})();

← 6e16b66 poster: rankByLRU ignores dry-run entries (Cody FIX-FIRST)  ·  back to Beverlyhillsvideos  ·  auto-data-snapshot: 2026-08-06T16:33:58 (2 data files) — soc e150ee7 →