- Created server/utils/logCapture.js to intercept and buffer server output, stripping ANSI escape codes.
- Created server/middleware/logStreamAuth.js enforcing subnet IP filtering (LOG_ALLOW_SUBNETS), Emby session cookie, Basic Auth fallback, and X-Webhook-Secret header bypass.
- Created server/routes/debug.js with SSE streams /api/debug/server-logs, /api/debug/client-logs and batched POST /api/debug/client-logs. Exposes public configuration status at /api/debug/status.
- Integrated log capture and mounted debug routes in server/app.js and server/index.js.
- Implemented client/src/utils/clientLogCapture.js in the frontend SPA to hook console log/warn/error and flush batched console events.
- Documented all endpoints in OpenAPI server/openapi.yaml, ARCHITECTURE.md, and README.md.
- Wrote route integration tests and frontend console capture tests, with full validation in swagger-coverage.
- Add Ombi requests tab UI with movie/TV request display
- Add showAll parameter support for Ombi requests (API and SSE)
- Add Ombi webhook panel with enable/test functionality
- Add Ombi webhook status endpoint with metrics
- Add Ombi webhook test endpoint
- Change GET /api/ombi/requests to use OmbiRetriever instead of cache
- Add Ombi webhook state and API functions to frontend
- Update SSE payload to include Ombi baseUrl and requests
- Change swaggerUi.setup to pass null and fetch spec from /api/swagger.json
- Update /api/swagger.json handler to dynamically set server URL based on request
- Remove dead client-side detection script (swagger-server-detection.js)
- Server-side detection respects TRUST_PROXY for reverse proxy scenarios
- req.protocol and req.get('host') automatically use X-Forwarded headers when configured
- Fixes issue where placeholder URL was never replaced due to window.ui being unavailable
- Update openapi.yaml to use single placeholder server URL
- Add swagger-server-detection.js to auto-detect current server URL from window.location
- Configure protocol, host, and port detection based on browser connection
- Fallback to placeholder URL if detection fails
- Include detection script in both app.js and index.js Swagger UI configurations
- /api/swagger.json endpoint returns static placeholder for external consumers
- GET /health: returns uptime, no auth/rate-limit
- GET /ready: checks EMBY_URL configuration, returns 503 if not ready
- Document Docker HEALTHCHECK usage
- Import swagger-ui-express, swagger-jsdoc, yamljs in app.js and index.js
- Load server/openapi.yaml as base spec
- Configure swagger-jsdoc to merge JSDoc comments from route files
- Mount Swagger UI at /api/swagger (publicly accessible)
- Add authentication banner explaining cookie + CSRF flow
- Ensure spec loads from both createApp (tests) and index.js (production)
- Extract /status route to server/routes/status.js
- Create server/services/WebhookStatus.js with checkWebhookConfigured and aggregateMetrics
- Slim dashboard.js to pure HTTP orchestration (559→283 lines, 49.4% reduction)
- Remove /user-summary and /webhook-metrics routes from dashboard.js
- Mount status router at /api/status in server/index.js and server/app.js
- Update tests to use new /api/status/status endpoint
- Fix test expectation for speed field (number vs string)
All 571 tests passing.
- Added POST /api/webhook/sonarr and POST /api/webhook/radarr endpoints
- Implemented webhook secret validation via SOFARR_WEBHOOK_SECRET environment variable
- Added logging for all incoming webhook events using existing logToFile utility
- Returns HTTP 200 immediately to prevent webhook retries
- Mounted webhook routes before CSRF middleware (called by external services)
- Non-breaking: no changes to polling, caching, SSE, or any existing behavior
- Lays groundwork for Phase 2 (cache + SSE integration) without implementing it yet
NODE_ENV=production enabled upgrade-insecure-requests unconditionally,
which instructed browsers to upgrade HTTP subresource requests to HTTPS.
When sofarr is accessed directly over HTTP (no reverse proxy), this
silently blocks all CSS, JS, and image loads — the page renders unstyled
with no functionality.
The correct signal for 'we are behind HTTPS' is TRUST_PROXY, not
NODE_ENV. upgrade-insecure-requests is now only emitted when a
TLS-terminating reverse proxy is confirmed to be in front.
Timing bars in the status panel and any other dynamically-injected
style= attributes were being silently blocked by the Content Security
Policy. style-src only governs <style> blocks and linked stylesheets;
inline element attributes need style-src-attr separately.
Adding style-src-attr 'unsafe-inline' is the minimal fix — it only
affects attribute-level inline styles, not script execution.
Also removes the temporary debug console.log added in the previous commit.