From 87f69aae8c29151ff1bb4337e3ca35a5c4ec5974 Mon Sep 17 00:00:00 2001 From: iamlegendharsh Date: Fri, 20 Mar 2026 00:12:48 +0530 Subject: [PATCH] fix: alert when no vehicle states written for an agency (fixes #31) --- src/index.js | 67 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 15 deletions(-) diff --git a/src/index.js b/src/index.js index dab0ad6..3c39916 100644 --- a/src/index.js +++ b/src/index.js @@ -3,6 +3,7 @@ const fs = require('fs'); const s3Helper = require('./s3Helper'); const interval = 15000; // ms +const ALERT_THRESHOLD_MS = 5 * 60 * 1000; // 5 minutes const configPath = process.env.ORION_CONFIG_PATH; const configJson = process.env.ORION_CONFIG_JSON; @@ -37,6 +38,9 @@ const providerNames = [ const s3Bucket = config.s3_bucket; console.log("S3 bucket: " + s3Bucket); +// Track last successful S3 write per agency +const lastSuccessfulWrite = {}; + var agenciesInfo = config.agencies.map((agencyConfig) => { const providerName = agencyConfig.provider; if (!providerNames.includes(providerName)) { @@ -52,6 +56,9 @@ var agenciesInfo = config.agencies.map((agencyConfig) => { console.log("Agency: " + agencyId + " (" + providerName + ")"); + // Initialize timestamp so alert doesn't fire immediately on startup + lastSuccessfulWrite[agencyId] = Date.now(); + return { provider: provider, id: agencyId, @@ -59,24 +66,54 @@ var agenciesInfo = config.agencies.map((agencyConfig) => { }; }); -// wait until the next multiple of 15 seconds +// Alert function — uses Slack webhook if configured, else logs to stderr +function sendAlert(agencyId) { + const message = `Orion Alert: No vehicle states written for agency "${agencyId}" in the last ${ALERT_THRESHOLD_MS / 60000} minutes. Provider may be down.`; + console.error(`ALERT: ${message}`); + + const webhookUrl = process.env.ALERT_WEBHOOK_URL; + if (webhookUrl) { + axios.post(webhookUrl, { text: `🚨 ${message}` }) + .catch((err) => console.error(`Failed to send alert webhook for ${agencyId}:`, err.message)); + } +} + +// Periodically check if any agency has stopped writing states +setInterval(() => { + const now = Date.now(); + agenciesInfo.forEach((agencyInfo) => { + const last = lastSuccessfulWrite[agencyInfo.id]; + if (!last || (now - last) > ALERT_THRESHOLD_MS) { + sendAlert(agencyInfo.id); + } + }); +}, ALERT_THRESHOLD_MS); + +// Wait until the next multiple of 15 seconds setTimeout(function() { setInterval(saveVehicles, interval); saveVehicles(); }, interval - Date.now() % interval); function saveVehicles() { - const currentTime = Date.now(); - - const promises = agenciesInfo.map((agencyInfo) => { - return agencyInfo.provider.getVehicles(agencyInfo.config) - .then((vehicles) => { - return s3Helper.writeToS3(s3Bucket, agencyInfo.id, currentTime, vehicles); - }) - .catch((err) => { - console.log(err); - }); - }); - - Promise.all(promises); -} + const currentTime = Date.now(); + + const promises = agenciesInfo.map((agencyInfo) => { + return agencyInfo.provider.getVehicles(agencyInfo.config) + .then((vehicles) => { + return s3Helper.writeToS3(s3Bucket, agencyInfo.id, currentTime, vehicles) + .then((result) => { + // ✅ Update timestamp on successful write + lastSuccessfulWrite[agencyInfo.id] = Date.now(); + return result; + }); + }) + .catch((err) => { + // Log error but don't update lastSuccessfulWrite so alert can trigger + console.error(`Error saving vehicles for agency "${agencyInfo.id}":`, err.message || err); + }); + }); + + // Return the Promise so errors are traceable + return Promise.all(promises); +} \ No newline at end of file