a21bafa041
- GET /api/status/status: admin-only, server/cache/polling/webhook metrics - GET /api/history/recent: filtered by user tag, deduplication logic - Document deduplication rules (imported suppresses failed) - Document availableForUpgrade flag - Include query parameters (days, showAll)
168 lines
6.1 KiB
JavaScript
168 lines
6.1 KiB
JavaScript
// Copyright (c) 2026 Gordon Bolton. MIT License.
|
|
const express = require('express');
|
|
const router = express.Router();
|
|
const requireAuth = require('../middleware/requireAuth');
|
|
const cache = require('../utils/cache');
|
|
const { getLastPollTimings, POLLING_ENABLED } = require('../utils/poller');
|
|
const { getSonarrInstances, getRadarrInstances } = require('../utils/config');
|
|
const { getGlobalWebhookMetrics } = require('../utils/cache');
|
|
const { checkWebhookConfigured, aggregateMetrics } = require('../services/WebhookStatus');
|
|
|
|
/**
|
|
* @openapi
|
|
* /api/status/status:
|
|
* get:
|
|
* tags: [Status]
|
|
* summary: Get server status (admin-only)
|
|
* description: |
|
|
* Admin-only endpoint returning server metrics, cache statistics, polling information,
|
|
* and webhook metrics. Used by the admin status panel to monitor sofarr health.
|
|
*
|
|
* **Authentication:** Requires valid `emby_user` cookie (admin only).
|
|
*
|
|
* **Response Structure:**
|
|
* - `server`: Uptime, Node version, memory usage
|
|
* - `polling`: Polling enabled status, interval, last poll timings
|
|
* - `cache`: Cache statistics (item count, sizes, TTLs)
|
|
* - `webhooks`: Webhook configuration and metrics for Sonarr/Radarr
|
|
*
|
|
* **Webhook Metrics:**
|
|
* - `configured`: Whether webhook is configured in Sonarr/Radarr
|
|
* - `eventsReceived`: Total webhook events received
|
|
* - `lastWebhookTimestamp`: Last webhook event time
|
|
* - `pollsSkipped`: Number of poll cycles skipped due to recent webhook activity
|
|
*
|
|
* **x-integration-notes:** This endpoint is used by the admin status panel to display:
|
|
* - Server health and resource usage
|
|
* - Polling performance and timing
|
|
* - Cache hit rates and sizes
|
|
* - Webhook activity and smart polling effectiveness
|
|
* security:
|
|
* - CookieAuth: []
|
|
* responses:
|
|
* '200':
|
|
* description: Status data
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* $ref: '#/components/schemas/StatusResponse'
|
|
* example:
|
|
* server:
|
|
* uptimeSeconds: 3600
|
|
* nodeVersion: "v22.0.0"
|
|
* memoryUsageMB: 128.5
|
|
* heapUsedMB: 64.2
|
|
* heapTotalMB: 128.0
|
|
* polling:
|
|
* enabled: true
|
|
* intervalMs: 5000
|
|
* lastPoll:
|
|
* sabnzbdQueue: 150
|
|
* sonarrQueue: 200
|
|
* cache:
|
|
* "poll:sab-queue":
|
|
* size: 2456
|
|
* items: 1
|
|
* ttlRemaining: 12000
|
|
* webhooks:
|
|
* sonarr:
|
|
* configured: true
|
|
* eventsReceived: 42
|
|
* lastWebhookTimestamp: "2026-05-21T10:00:00.000Z"
|
|
* pollsSkipped: 15
|
|
* radarr:
|
|
* configured: true
|
|
* eventsReceived: 38
|
|
* lastWebhookTimestamp: "2026-05-21T09:55:00.000Z"
|
|
* pollsSkipped: 12
|
|
* '403':
|
|
* description: Admin access required
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* $ref: '#/components/schemas/ErrorResponse'
|
|
* example:
|
|
* error: "Admin access required"
|
|
* '500':
|
|
* description: Server error
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* $ref: '#/components/schemas/ErrorResponse'
|
|
* x-code-samples:
|
|
* - lang: curl
|
|
* label: cURL
|
|
* source: |
|
|
* curl -X GET http://localhost:3001/api/status/status \
|
|
* -b cookies.txt
|
|
* - lang: JavaScript
|
|
* label: JavaScript (fetch)
|
|
* source: |
|
|
* const response = await fetch('http://localhost:3001/api/status/status', {
|
|
* method: 'GET',
|
|
* credentials: 'include'
|
|
* });
|
|
* const data = await response.json();
|
|
* console.log('Uptime:', data.server.uptimeSeconds);
|
|
*/
|
|
router.get('/', requireAuth, async (req, res) => {
|
|
try {
|
|
const user = req.user;
|
|
if (!user.isAdmin) {
|
|
return res.status(403).json({ error: 'Admin access required' });
|
|
}
|
|
|
|
const cacheStats = cache.getStats();
|
|
const uptime = process.uptime();
|
|
|
|
// Get webhook metrics
|
|
const webhookMetrics = getGlobalWebhookMetrics();
|
|
|
|
// Check webhook configuration for each service
|
|
const sonarrInstances = getSonarrInstances();
|
|
const radarrInstances = getRadarrInstances();
|
|
|
|
const sonarrWebhookConfigured = sonarrInstances.length > 0
|
|
? await checkWebhookConfigured(sonarrInstances[0], 'Sonarr')
|
|
: false;
|
|
const radarrWebhookConfigured = radarrInstances.length > 0
|
|
? await checkWebhookConfigured(radarrInstances[0], 'Radarr')
|
|
: false;
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
res.json({
|
|
server: {
|
|
uptimeSeconds: Math.floor(uptime),
|
|
nodeVersion: process.version,
|
|
memoryUsageMB: Math.round(process.memoryUsage().rss / 1024 / 1024 * 10) / 10,
|
|
heapUsedMB: Math.round(process.memoryUsage().heapUsed / 1024 / 1024 * 10) / 10,
|
|
heapTotalMB: Math.round(process.memoryUsage().heapTotal / 1024 / 1024 * 10) / 10
|
|
},
|
|
polling: {
|
|
enabled: POLLING_ENABLED,
|
|
intervalMs: POLLING_ENABLED ? require('../utils/poller').POLL_INTERVAL : 0,
|
|
lastPoll: getLastPollTimings()
|
|
},
|
|
cache: cacheStats,
|
|
webhooks: {
|
|
sonarr: aggregateMetrics(sonarrMetrics, sonarrWebhookConfigured),
|
|
radarr: aggregateMetrics(radarrMetrics, radarrWebhookConfigured)
|
|
}
|
|
});
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Failed to get status', details: err.message });
|
|
}
|
|
});
|
|
|
|
module.exports = router;
|