← 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
M DW-Agents/vendor-command-center/server.js
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 →