Files
sofarr/server/services/WebhookStatus.js
T
gronod 9491948ec9
CI / Security audit (push) Successful in 1m45s
Build and Push Docker Image / build (push) Successful in 1m43s
Licence Check / Licence compatibility and copyright header verification (push) Has been cancelled
CI / Tests & coverage (push) Has been cancelled
CI / Swagger Validation & Coverage (push) Has been cancelled
fix: resolve Ombi webhook race condition and add Ombi webhook status metrics to status panel
2026-05-24 10:22:47 +01:00

75 lines
2.5 KiB
JavaScript

// Copyright (c) 2026 Gordon Bolton. MIT License.
const axios = require('axios');
const { getSonarrInstances, getRadarrInstances } = require('../utils/config');
/**
* Check if Sofarr webhook is configured in a Sonarr/Radarr instance.
* @param {Object} instance - The Sonarr/Radarr instance config
* @param {string} type - 'Sonarr' or 'Radarr'
* @returns {Promise<boolean>} true if webhook is configured
*/
async function checkWebhookConfigured(instance, type) {
try {
const response = await axios.get(`${instance.url}/api/v3/notification`, {
headers: { 'X-Api-Key': instance.apiKey },
timeout: 5000
});
const notifications = response.data || [];
return notifications.some(n => n.name === 'Sofarr' && n.implementation === 'Webhook');
} catch (err) {
console.log(`[WebhookStatus] Failed to check ${type} webhook config: ${err.message}`);
return false;
}
}
/**
* Aggregate webhook metrics for a service type.
* @param {Object} metricsMap - Map of instance URLs to their metrics
* @param {boolean} configured - Whether the service is configured
* @returns {Object|null} Aggregated metrics or null if not configured
*/
function aggregateMetrics(metricsMap, configured) {
const values = Object.values(metricsMap);
if (values.length === 0) {
// Return default metrics if configured but no events yet
return configured ? {
enabled: true,
eventsReceived: 0,
pollsSkipped: 0,
lastEvent: null
} : null;
}
return {
enabled: true,
eventsReceived: values.reduce((sum, m) => sum + (m.eventsReceived || 0), 0),
pollsSkipped: values.reduce((sum, m) => sum + (m.pollsSkipped || 0), 0),
lastEvent: values.reduce((latest, m) => {
return m.lastWebhookTimestamp > latest ? m.lastWebhookTimestamp : latest;
}, 0)
};
}
/**
* Check if Sofarr webhook is configured in an Ombi instance.
* @param {Object} instance - The Ombi instance config
* @returns {Promise<boolean>} true if webhook is configured
*/
async function checkOmbiWebhookConfigured(instance) {
try {
const response = await axios.get(`${instance.url}/api/v1/Settings/notifications/webhook`, {
headers: { 'ApiKey': instance.apiKey },
timeout: 5000
});
return !!(response.data && response.data.enabled);
} catch (err) {
console.log(`[WebhookStatus] Failed to check Ombi webhook config: ${err.message}`);
return false;
}
}
module.exports = {
checkWebhookConfigured,
checkOmbiWebhookConfigured,
aggregateMetrics
};