Added debug logging to trace status panel rendering:
- Log when refresh starts
- Log when data is received
- Log errors with details
Also added visible dashed border and background to #status-content
to make it obvious when the div is present but empty.
The webhooks panel was being destroyed when renderStatusPanel set
panel.innerHTML. Added a dedicated #status-content div for status
data, keeping webhooks section intact when status refreshes.
The webhooks panel was appearing separately from the status panel.
Now it's properly nested inside the status-panel div:
- Moved webhooks-section inside status-panel in HTML
- Updated CSS so nested webhooks looks like a subsection (no double borders)
- Simplified JS toggle logic - webhooks shows/hides automatically with status panel
- Admin users see webhooks inside status panel, collapsed by default
Sonarr Activity tab has 12 pages but we only fetched ~2 items.
Added pageSize=1000 to queue API and changed history default from 10 to 100.
This ensures all downloads are available for matching to SAB/qBittorrent.
Sonarr tracks the exact SAB download ID (nzo_id). Now tries to match
by downloadId first, then falls back to title matching. Also adds
debug to show if matches are via downloadId vs title, and logs
downloadIds in history to verify the link exists.
When a match is found, logs whether it came from queue or history.
When no match, shows history counts and sample titles to verify
history is being checked properly.
SAB items often persist after Sonarr has processed them.
Previously only checked the active queue, now also checks
history records so completed downloads still appear.
SAB filenames use dots (dora.the.explorer.s02e08) but Sonarr titles
use spaces (Dora the Explorer - S02E08). Now tries matching with
both formats to improve match rate.
Also logs actual Sonarr titles when no match found for debugging.
Shows exactly which SAB items match/don't match to Sonarr/Radarr:
- ✓ Sonarr match: SAB name → Sonarr name
- ✓ Radarr match: SAB name → Radarr name
- ✗ No match: SAB name (with Sonarr queue count)
This will help diagnose why Sonarr Activity Queue shows matches but Sofarr doesn't.
SABnzbd API returns labels as an array in newer versions,
but the code assumed it was a comma-separated string.
Now handles both cases to prevent 'slot.labels.split is not a function' error.
1. Fixed download client collision:
- SABnzbd client with id 'i3omb' was being overwritten by qBittorrent
- Now uses unique key ':' like the arr retrievers
2. Fixed webhook metrics showing 0:
- instanceName from webhooks is generic ('Sonarr', 'Radarr')
- Not the configured instance name ('i3omb')
- Now updates metrics for ALL instances of that type
When Sonarr and Radarr had the same instance ID (e.g., 'i3omb'),
the Radarr retriever would overwrite the Sonarr retriever in the Map.
This caused webhook refreshes to show '0 instance(s)' for Sonarr.
Now uses ':' as the unique key so both can coexist.
The status panel was showing webhooks as disabled (null) when no events
had been received yet. Now it checks Sonarr/Radarr API to see if the
Sofarr webhook notification is actually configured.
- Added checkWebhookConfigured() to verify webhook exists in Sonarr/Radarr
- Shows 'enabled: true' with 0 events when webhook is configured
- Only shows null when webhook is not configured at all
Add debug logging to trace:
- When downloads payload is built
- Data sizes from cache (SAB, qBit, Sonarr, Radarr)
- Number of downloads found and their titles
This will help diagnose why Dora downloads aren't appearing.
- Fixed webhooks section to load collapsed (content hidden, toggle arrow reset)
- Added webhook metrics card to status panel for admin users:
- Shows Sonarr/Radarr enabled/disabled status
- Shows events received and polls skipped counts
- Updated /api/dashboard/status endpoint to include webhook metrics
- Metrics are aggregated from all Sonarr/Radarr instances
- Moved webhooks-section to be inline with status-panel in HTML
- Updated toggleStatusPanel() to show/hide webhooks section for admin users
- Updated closeStatusPanel() to also hide webhooks section
- Removed webhooks visibility from showDashboard() - now tied to status panel
- Updated CSS to make webhooks section styling consistent with status panel:
- Same border, border-radius, margin, box-shadow
- Updated webhook-stats to use status-card styling (background, border)
- Webhooks metrics now display inline with status panel for admin users
The /notifications/test endpoint requires the full notification object,
not just the ID. Changed testSonarrWebhook() and testRadarrWebhook() to
send the complete notification object (sonarrSofarr/radarrSofarr).
Fixes: 400 validation error when testing webhooks
The webhook notification payload was using string 'POST' for the method
field, but Sonarr/Radarr API expects numeric values:
- 1 = POST
- 2 = PUT
Also added onManualInteractionRequired: false to match the schema.
Fixes: Radarr/Sonarr rejecting webhook configuration with validation errors
The notification routes were using process.env.SONARR_URL directly,
which is undefined when using the newer SONARR_INSTANCES JSON format.
Changes:
- Added getFirstSonarrInstance() and getFirstRadarrInstance() helpers
- Updated /notifications, /notifications/test, and /notifications/sofarr-webhook
routes to use instance config from getSonarrInstances()/getRadarrInstances()
- Returns 503 error if no instances are configured
Fixes: 'Invalid URL' errors when calling Sonarr/Radarr notification APIs
Added detailed error logging to help diagnose 500 errors when calling
Sonarr/Radarr notification APIs. Logs include:
- Error message
- Response status (if available)
- Response data (if available)
This will help identify if the issue is:
- Missing SONARR_URL/RADARR_URL or API keys
- Network connectivity issues
- Sonarr/Radarr API version incompatibility
The React build replaced the full-featured vanilla JS app with a
simpler UI, causing the dashboard to disappear and lose theming.
This commit:
- Restores original vanilla JS app with auth, themes, tabs, history
- Adds Webhooks Configuration panel for admin users
- Adds webhook status, enable/test buttons, triggers, and stats
- Uses proper CSS variables for theme support
Fixes the dashboard disappearing issue and restores all original functionality.
Webhook routes were only registered in app.js (the test factory) but
not in index.js (the production entry point). POST /api/webhook/*
was therefore falling through to the verifyCsrf middleware and being
rejected with 403 in production.
Inline onclick attribute was silently blocked by the server CSP nonce
policy. Replace with addEventListener after innerHTML is set.
chore: bump version to 1.5.0a
- 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
refactor(arr-retrievers): implement Pluggable *arr Retrieval Layer (PALDRA) (#19)
- Added abstract ArrRetriever base class and concrete PollingSonarrRetriever / PollingRadarrRetriever
- Created centralized ArrRetrieverRegistry (pure singleton, matching PDCA style)
- Refactored poller.js and historyFetcher.js to use the new pluggable registry
- 100% backward compatible: no changes to behavior, caching, SSE, performance, or APIs
This completes the PALDRA work from ticket #19 and lays the groundwork for webhook support.
- Remove instanceConfig parameter from all retriever methods (getTags, getQueue, getHistory)
- Retriever instances now use this.url, this.apiKey, this.id instead of passed parameter
- Convert ArrRetrieverRegistry from class with convenience functions to pure singleton object
- Export singleton instance directly instead of class + convenience functions
- Update poller.js and historyFetcher.js to call methods on singleton directly
- All 261 tests pass with zero behavior changes
- Create ArrRetriever abstract base class defining pluggable interface
- Implement PollingSonarrRetriever and PollingRadarrRetriever with HTTP polling
- Add ArrRetrieverRegistry for managing retriever instances
- Refactor poller.js to use retriever registry instead of direct Axios calls
- Update historyFetcher.js to use retriever registry
- Preserve all cache keys, TTLs, timing logs, SSE broadcasts, error handling
- Enable future webhook listeners without touching poller logic