diff --git a/public/app.js b/public/app.js index 0b19aed..2c8f977 100644 --- a/public/app.js +++ b/public/app.js @@ -778,9 +778,13 @@ async function toggleStatusPanel() { return; } panel.style.display = 'block'; - // Show webhooks section for admin users + // Show webhooks section for admin users (collapsed by default) if (webhooksSection && isAdmin) { webhooksSection.style.display = ''; + // Ensure webhooks section starts collapsed + webhookSectionExpanded = false; + document.getElementById('webhooks-content').style.display = 'none'; + document.getElementById('webhooks-toggle').classList.remove('expanded'); await fetchWebhookStatus(); } await refreshStatusPanel(); @@ -859,6 +863,26 @@ function renderStatusPanel(data, panel) { html += ``; + // Webhook metrics card (admin only) + if (isAdmin && data.webhooks) { + const wh = data.webhooks; + const sonarrEnabled = wh.sonarr?.enabled ? '●' : '○'; + const radarrEnabled = wh.radarr?.enabled ? '●' : '○'; + const sonarrEvents = wh.sonarr?.eventsReceived || 0; + const radarrEvents = wh.radarr?.eventsReceived || 0; + const sonarrPolls = wh.sonarr?.pollsSkipped || 0; + const radarrPolls = wh.radarr?.pollsSkipped || 0; + + html += ` +
+
Webhooks
+
Sonarr${sonarrEnabled} ${wh.sonarr?.enabled ? 'Enabled' : 'Disabled'}
+
Radarr${radarrEnabled} ${wh.radarr?.enabled ? 'Enabled' : 'Disabled'}
+
EventsS:${sonarrEvents} R:${radarrEvents}
+
Polls skippedS:${sonarrPolls} R:${radarrPolls}
+
`; + } + // Poll timings card const lp = data.polling.lastPoll; if (lp) { diff --git a/server/routes/dashboard.js b/server/routes/dashboard.js index e72fa29..ee1ae1c 100644 --- a/server/routes/dashboard.js +++ b/server/routes/dashboard.js @@ -782,6 +782,35 @@ router.get('/status', requireAuth, (req, res) => { const cacheStats = cache.getStats(); const uptime = process.uptime(); + // Get webhook metrics + const { getGlobalWebhookMetrics } = require('../utils/cache'); + const webhookMetrics = getGlobalWebhookMetrics(); + + // Find Sonarr and Radarr metrics from instances + const sonarrMetrics = {}; + const radarrMetrics = {}; + for (const [url, metrics] of Object.entries(webhookMetrics.instances || {})) { + if (url.includes('sonarr')) { + sonarrMetrics[url] = metrics; + } else if (url.includes('radarr')) { + radarrMetrics[url] = metrics; + } + } + + // Aggregate metrics for each service + const aggregateMetrics = (metricsMap) => { + const values = Object.values(metricsMap); + if (values.length === 0) return 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) + }; + }; + res.json({ server: { uptimeSeconds: Math.floor(uptime), @@ -796,7 +825,11 @@ router.get('/status', requireAuth, (req, res) => { lastPoll: getLastPollTimings() }, cache: cacheStats, - clients: getActiveClients() + clients: getActiveClients(), + webhooks: { + sonarr: aggregateMetrics(sonarrMetrics), + radarr: aggregateMetrics(radarrMetrics) + } }); } catch (err) { res.status(500).json({ error: 'Failed to get status', details: err.message });