chore: bump version to 1.6.0, update CHANGELOG and ARCHITECTURE docs
Build and Push Docker Image / build (push) Successful in 48s
CI / Security audit (push) Successful in 1m23s
Docs Check / Markdown lint (push) Successful in 1m10s
CI / Tests & coverage (push) Successful in 1m47s
Licence Check / Licence compatibility and copyright header verification (push) Successful in 1m18s
Docs Check / Mermaid diagram parse check (push) Successful in 1m44s

This commit is contained in:
2026-05-21 11:49:46 +01:00
parent adbb0c12c1
commit 5d0da45e10
3 changed files with 54 additions and 6 deletions
+7 -5
View File
@@ -85,7 +85,7 @@ flowchart TB
login -->|"POST /api/auth/login"| auth_r login -->|"POST /api/auth/login"| auth_r
dash -->|"GET /api/dashboard/stream (SSE)"| dash_r dash -->|"GET /api/dashboard/stream (SSE)"| dash_r
status -->|"GET /api/status/status"| stat_r status -->|"GET /api/status"| stat_r
history -->|"GET /api/history/recent"| hist_r history -->|"GET /api/history/recent"| hist_r
auth_r --> tokenstore auth_r --> tokenstore
@@ -452,10 +452,10 @@ Every `POLL_INTERVAL` ms the poller fetches all services in parallel:
| SABnzbd History | `GET /api?mode=history` | `limit=10` | | SABnzbd History | `GET /api?mode=history` | `limit=10` |
| Sonarr Tags | `GET /api/v3/tag` | — | | Sonarr Tags | `GET /api/v3/tag` | — |
| Sonarr Queue | `GET /api/v3/queue` | `includeSeries=true`, `includeEpisode=true` | | Sonarr Queue | `GET /api/v3/queue` | `includeSeries=true`, `includeEpisode=true` |
| Sonarr History | `GET /api/v3/history` | `pageSize=10`, `includeEpisode=true` | | Sonarr History | `GET /api/v3/history` | `pageSize=50`, `includeEpisode=true` |
| Radarr Tags | `GET /api/v3/tag` | — | | Radarr Tags | `GET /api/v3/tag` | — |
| Radarr Queue | `GET /api/v3/queue` | `includeMovie=true` | | Radarr Queue | `GET /api/v3/queue` | `includeMovie=true` |
| Radarr History | `GET /api/v3/history` | `pageSize=10` | | Radarr History | `GET /api/v3/history` | `pageSize=50` |
| qBittorrent | `GET /api/v2/sync/maindata?rid=N` | Fallback: `GET /api/v2/torrents/info` | | qBittorrent | `GET /api/v2/sync/maindata?rid=N` | Fallback: `GET /api/v2/torrents/info` |
Results are stored in `MemoryCache` under `poll:*` keys with TTL `POLL_INTERVAL × 3`. Per-task timings are recorded in `lastPollTimings` for the admin status panel. Results are stored in `MemoryCache` under `poll:*` keys with TTL `POLL_INTERVAL × 3`. Per-task timings are recorded in `lastPollTimings` for the admin status panel.
@@ -718,7 +718,9 @@ DownloadClient (abstract — server/clients/DownloadClient.js)
### 7.2 Queue & History Processing ### 7.2 Queue & History Processing
**`server/utils/historyFetcher.js`** fetches history records from all Sonarr/Radarr instances for a configurable date window. Results are cached under `history:sonarr` / `history:radarr` for 5 minutes. Exports `classifySonarrEvent` / `classifyRadarrEvent` (returns `'imported'` | `'failed'` | `'other'`) and `invalidateHistoryCache`. **`server/utils/historyFetcher.js`** fetches history records from all Sonarr/Radarr instances for a configurable date window. Results are cached under `history:sonarr` / `history:radarr` for 5 minutes. Exports `classifySonarrEvent` / `classifyRadarrEvent` (returns `'imported'` | `'failed'` | `'other'`), `invalidateHistoryCache`, and `onHistoryUpdate` / `emitHistoryUpdate` for SSE staging.
**Staged loading** — the fetcher returns up to `INITIAL_PAGE_SIZE` (100) records immediately from the cache or a quick fetch. If fewer than `MAX_TOTAL_RECORDS` (1,000) are present, a background fetch of up to `MAX_PAGES` (10) is triggered automatically. As the background fetch completes, `emitHistoryUpdate()` notifies all registered subscribers, which causes the SSE layer to push a `history-update` frame to every connected browser. The frontend (`client/src/ui/history.js`) listens for these events and re-renders the "Recently Completed" tab incrementally.
**`server/routes/history.js`** (`GET /api/history/recent`) returns recently completed (imported or failed) downloads filtered for the authenticated user. Supports `?days=N` (default `RECENT_COMPLETED_DAYS`, capped at 90) and `?showAll=true` for admins. Results are sorted newest first. **`server/routes/history.js`** (`GET /api/history/recent`) returns recently completed (imported or failed) downloads filtered for the authenticated user. Supports `?days=N` (default `RECENT_COMPLETED_DAYS`, capped at 90) and `?showAll=true` for admins. Results are sorted newest first.
@@ -844,7 +846,7 @@ sofarr/
│ ├── routes/ │ ├── routes/
│ │ ├── auth.js POST /login, GET /me, GET /csrf, POST /logout │ │ ├── auth.js POST /login, GET /me, GET /csrf, POST /logout
│ │ ├── dashboard.js SSE /stream, /user-downloads, /blocklist-search │ │ ├── dashboard.js SSE /stream, /user-downloads, /blocklist-search
│ │ ├── status.js GET /api/status/status — admin server/polling/webhook status │ │ ├── status.js GET /api/status — admin server/polling/webhook status
│ │ ├── history.js GET /api/history/recent │ │ ├── history.js GET /api/history/recent
│ │ ├── webhook.js POST /api/webhook/sonarr|radarr │ │ ├── webhook.js POST /api/webhook/sonarr|radarr
│ │ ├── sonarr.js Sonarr API proxy + webhook management │ │ ├── sonarr.js Sonarr API proxy + webhook management
+46
View File
@@ -6,6 +6,52 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
--- ---
## [1.6.0] - 2026-05-21
### Added
- **Staged history loading with SSE push** — history data from Sonarr/Radarr is now fetched in stages; `history-update` SSE events push incremental results to the dashboard so the "Recently Completed" tab populates without waiting for the full fetch.
- **Frontend unit tests** — added Vitest + jsdom test suite covering `client/src/` modules (formatters, storage, state).
- **Comprehensive tests for staged history loading** — backend tests verify the new background-fetch behaviour, cache TTL handling, and SSE emission.
- **Integration test coverage** — new integration and unit tests for `dashboard`, `emby`, `sonarr`, `radarr`, and `sabnzbd` routes.
### Changed
- **Technical-debt remediation — service extraction** — extracted matching and assembly logic from the monolithic `dashboard.js` (~1,360 lines → ~284 lines) into dedicated, testable services:
- `DownloadMatcher.js` — SABnzbd slot / torrent → *arr record matching
- `DownloadAssembler.js` — cover-art, episode, link, blocklist-eligibility helpers
- `DownloadBuilder.js` — orchestrator that reads the cache snapshot and builds the final user-facing download list
- `TagMatcher.js` — tag extraction, sanitisation, and user-ownership matching
- `WebhookStatus.js` — webhook configuration status aggregation
- **Frontend architecture** — migrated from a monolithic `public/app.js` to vanilla ES modules under `client/src/`, bundled by Vite into `public/app.js`.
- **History pagination** — replaced custom date-based cursor pagination with the retriever's built-in pagination (`pageSize=100`, up to 10 pages / 1,000 records total). Eliminates the 40-second response-time regression seen with large histories.
- **Status endpoint path** — admin status route moved from `/api/status/status` to `/api/status` for consistency with the router mount point.
- **Background-fetch safety** — the poller no longer overwrites the cache with empty data when a background history fetch fails or returns no records.
- **SABnzbd progress calculation** — progress is now computed from `slot.mb` and `slot.mbleft` / `slot.mbmissing` because the SABnzbd queue API does not expose a `percentage` field.
- **Speed formatting consistency** — `updateDownloadCard()` now calls `formatSpeed()` so all speed values display with uniform units (B/s, KB/s, MB/s, GB/s).
- **Status-panel error handling** — the panel now surfaces error messages (e.g. `403` for non-admin users) instead of showing a blank box.
### Fixed
- **CSRF token reference errors** — fixed `ReferenceError` bugs in `api.js`, `auth.js`, and the blocklist handler where `csrfToken` and `currentUser` were accessed as bare variables instead of via the shared `state` object.
- **Logout button** — fixed undefined variable references in `handleLogoutClick` that prevented the logout flow from completing.
- **Missing progress bar for SABnzbd** — SABnzbd downloads now render a correct progress bar instead of showing `undefined%`.
- **Status route 404** — corrected the Express router mount so `GET /api/status` responds instead of returning HTML 404.
- **Status button DOM ID** — fixed element-ID mismatch (`status-btn` vs `status-toggle`) that prevented the admin status button from toggling the panel.
- **Tab selection** — fixed tab switching to use `data-tab` attributes after the DOM IDs were removed during the ES-module migration.
- **CSP violations and `ignoreAvailable` reference error** — resolved frontend CSP compliance issues and a missing-variable error in the history tab.
- **Docker client-build stage** — removed `client/` from `.dockerignore` so the multi-stage Dockerfile can run the Vite build during image creation.
- **Unmatched torrent exclusion** — torrents that cannot be matched to a Sonarr/Radarr record are now correctly omitted from the download display.
- **Blocklist button CSRF** — fixed a `ReferenceError` that occurred when a non-admin user clicked the blocklist button.
### Breaking Changes
- **Unmatched torrents are no longer displayed** — torrents that do not match a Sonarr/Radarr queue or history record are excluded from the dashboard. Users who previously saw direct torrent-client downloads will no longer see them unless the *arr services track the item.
- **Frontend build process changed** — the monolithic `public/app.js` source file has been replaced by a Vite build from `client/src/`. Any custom deployment scripts that copy or modify `public/app.js` directly must be updated to run `npm run build` (or use the provided Dockerfile, which already does this).
- **Status API endpoint path changed** — the admin status endpoint moved from `/api/status/status` to `/api/status`. External integrations, monitoring checks, or scripts hitting the old path will receive `404`.
---
## [1.5.5] - 2026-05-20 ## [1.5.5] - 2026-05-20
### Added ### Added
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "sofarr", "name": "sofarr",
"version": "1.5.5", "version": "1.6.0",
"description": "A personal media download dashboard that shows your downloads 'so far' while you relax on the sofa waiting for your *arr services to finish", "description": "A personal media download dashboard that shows your downloads 'so far' while you relax on the sofa waiting for your *arr services to finish",
"main": "server/index.js", "main": "server/index.js",
"scripts": { "scripts": {