Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 52 additions & 15 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)) {
Expand All @@ -52,31 +56,64 @@ 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,
config: 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);
}