[object Object]

← back to Designer Wallcoverings

auto-save: 2026-07-31T17:59:58 (1 files) — DW-Agents/vendor-command-center/server.js

6390c31a6fa2187693a453a5ef70c8deef89afc4 · 2026-07-31 18:00:04 -0700 · Steve Abrams

Files touched

Diff

commit 6390c31a6fa2187693a453a5ef70c8deef89afc4
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Fri Jul 31 18:00:04 2026 -0700

    auto-save: 2026-07-31T17:59:58 (1 files) — DW-Agents/vendor-command-center/server.js
---
 DW-Agents/vendor-command-center/server.js | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/DW-Agents/vendor-command-center/server.js b/DW-Agents/vendor-command-center/server.js
index ba0d97dc..dc4fd0de 100644
--- a/DW-Agents/vendor-command-center/server.js
+++ b/DW-Agents/vendor-command-center/server.js
@@ -180,6 +180,26 @@ app.get('/health', async (req, res) => {
 // Fleet-convention liveness probe (auth-gated services must serve unauthenticated /healthz -> 200)
 app.get('/healthz', (req, res) => res.status(200).send('ok'));
 
+// Deep health: exposes rollup-sync freshness so canaries can catch a VCC that is up
+// but whose 5-minute sync silently stopped. 503 when the last completed sync is older
+// than 3 intervals (15 min) — gate on HTTP status, details in the body.
+app.get('/healthz-deep', async (req, res) => {
+  let dbOk = true;
+  try { await pool.query('SELECT 1'); } catch (_) { dbOk = false; }
+  const sinceDoneSec = syncState.lastDone ? Math.round((Date.now() - Date.parse(syncState.lastDone)) / 1000) : null;
+  const sinceStartSec = syncState.lastStart ? Math.round((Date.now() - Date.parse(syncState.lastStart)) / 1000) : null;
+  const bootGraceSec = Math.round(process.uptime());
+  // A recently-STARTED sync still in flight isn't stale even if the last COMPLETION is old
+  const inFlight = sinceStartSec !== null && (sinceDoneSec === null || sinceStartSec < sinceDoneSec) && sinceStartSec < 900;
+  const stale = dbOk && !inFlight && (sinceDoneSec === null ? bootGraceSec > 900 : sinceDoneSec > 900);
+  res.status(!dbOk || stale ? 503 : 200).json({
+    ok: dbOk && !stale, db: dbOk, staleSync: stale, syncInFlight: inFlight,
+    sinceLastSyncSec: sinceDoneSec, lastSyncDurationMs: syncState.lastDurationMs,
+    // boolean only — pg error strings can carry connection details, and this route is unauthenticated
+    syncRuns: syncState.runs, lastSyncFailed: !!syncState.lastError, uptimeSec: bootGraceSec
+  });
+});
+
 // Apply auth to all other routes
 app.use(requireAuth);
 
@@ -2167,10 +2187,14 @@ async function computeSpecCompleteness(catTable) {
 }
 
 // --- Sync: Populate vendor_registry — CATALOG tables are source of truth ---
+// Observable sync state for /healthz-deep — pm2 "online" can't see a sync loop that
+// silently stopped firing (the executes-but-frozen failure mode the freshness canaries watch)
+const syncState = { lastStart: null, lastDone: null, lastDurationMs: null, lastError: null, runs: 0 };
 async function syncVendorCounts() {
   console.log('[Victor] Syncing vendor counts — PostgreSQL catalogs = source of truth, Shopify = published...');
   logActivity('sync', 'Sync started — scanning all vendor catalogs');
   const startTime = Date.now();
+  syncState.lastStart = new Date().toISOString();
   let updated = 0;
 
   try {
@@ -2395,10 +2419,15 @@ async function syncVendorCounts() {
     }
 
     const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
+    syncState.lastDone = new Date().toISOString();
+    syncState.lastDurationMs = Date.now() - startTime;
+    syncState.lastError = null;
+    syncState.runs++;
     console.log(`[Victor] Sync complete: ${updated} vendors updated in ${elapsed}s`);
     logActivity('sync', `Sync complete: ${updated} vendors updated in ${elapsed}s`);
     return { success: true, updated, elapsed };
   } catch (err) {
+    syncState.lastError = err.message;
     console.error('[Victor] Sync error:', err.message);
     logActivity('error', 'Sync failed: ' + err.message);
     return { success: false, error: err.message };

← 4e064b17 auto-save: 2026-07-31T16:59:39 (2 files) — shopify/scripts/a  ·  back to Designer Wallcoverings  ·  auto-save: 2026-07-31T23:01:37 (1 files) — shopify/scripts/c 2951b26a →