docs: update ARCHITECTURE.md and README for history feature (v2)

This commit is contained in:
2026-05-17 12:05:53 +01:00
parent 57b3254f70
commit fa3c625fb8

View File

@@ -132,13 +132,15 @@ sofarr/
│ │ ├── emby.js # Proxy routes to Emby API
│ │ ├── sabnzbd.js # Proxy routes to SABnzbd API
│ │ ├── sonarr.js # Proxy routes to Sonarr API
│ │ ── radarr.js # Proxy routes to Radarr API
│ │ ── radarr.js # Proxy routes to Radarr API
│ │ └── history.js # GET /api/history/recent — recently completed downloads
│ ├── middleware/
│ │ ├── requireAuth.js # httpOnly cookie auth enforcement
│ │ └── verifyCsrf.js # CSRF double-submit cookie validation
│ └── utils/
│ ├── cache.js # MemoryCache class (Map + TTL + stats)
│ ├── config.js # Multi-instance service configuration parser
│ ├── historyFetcher.js # Fetch + cache Sonarr/Radarr history; event classification
│ ├── logger.js # File logger (DATA_DIR/server.log)
│ ├── poller.js # Background polling engine + timing
│ ├── qbittorrent.js # qBittorrent client with auth + torrent mapping
@@ -209,6 +211,7 @@ sofarr/
| `sabnzbd.js` | `/api/sabnzbd` | Yes (`requireAuth`) | Yes | Proxy to SABnzbd API |
| `sonarr.js` | `/api/sonarr` | Yes (`requireAuth`) | Yes | Proxy to Sonarr API |
| `radarr.js` | `/api/radarr` | Yes (`requireAuth`) | Yes | Proxy to Radarr API |
| `history.js` | `/api/history` | Yes (`requireAuth`) | No (GET only) | Recently completed downloads from Sonarr/Radarr history |
**`requireAuth`** (`server/middleware/requireAuth.js`) reads the `emby_user` cookie (signed if `COOKIE_SECRET` is set) and attaches the parsed `{ id, name, isAdmin }` user to `req.user`. Returns `401` if the cookie is absent, tampered, or schema-invalid.
@@ -230,6 +233,8 @@ sofarr/
**`sanitizeError.js`** — Redacts secrets from error message strings before they are logged or returned in API responses. Patterns: URL query-param secrets (`apikey=`, `token=`, etc.), HTTP auth headers (`Authorization:`, `X-Emby-Authorization:`, etc.), Bearer tokens, and basic-auth credentials in URLs.
**`historyFetcher.js`** — Fetches history records from all Sonarr/Radarr instances for a configurable date window (`since`). Results are cached under `history:sonarr` / `history:radarr` for 5 minutes. Exports `classifySonarrEvent` / `classifyRadarrEvent` (returns `'imported'` | `'failed'` | `'other'`) and `invalidateHistoryCache`.
**`logger.js`** — Simple file appender writing timestamped messages to `DATA_DIR/server.log`.
---
@@ -342,6 +347,8 @@ Users are matched to downloads via tags in Sonarr/Radarr:
| `poll:radarr-tags` | `[{id, label}]` | Radarr tag API |
| `poll:qbittorrent` | `[torrent, ...]` | qBittorrent all torrents |
| `emby:users` | `Map<lowerName, displayName>` | Full Emby user list (60s TTL) |
| `history:sonarr` | `[record, ...]` — flat array with `_instanceUrl` / `_instanceName` | Sonarr history (5 min TTL, fetched on-demand by `/api/history/recent`) |
| `history:radarr` | `[record, ...]` — flat array with `_instanceUrl` / `_instanceName` | Radarr history (5 min TTL, fetched on-demand by `/api/history/recent`) |
### TTL Strategy
@@ -587,6 +594,50 @@ Admin-only per-user download counts (fetches live from APIs, not cached).
---
### `GET /api/history/recent`
Returns recently completed (imported or failed) downloads from Sonarr/Radarr history for the authenticated user, filtered to the last `days` days.
**Query Parameters:**
| Param | Type | Default | Description |
|-------|------|---------|-------------|
| `days` | integer | `RECENT_COMPLETED_DAYS` env (default `7`) | How many days back to search. Capped at 90. |
| `showAll` | `"true"` | — | (Admin) Return records for all tagged users, not just the current user |
**Response (200):**
```json
{
"user": "Alice",
"isAdmin": false,
"days": 7,
"history": [
{
"type": "series",
"outcome": "imported",
"title": "Show.S01E01.720p",
"seriesName": "My Show",
"coverArt": "https://…/poster.jpg",
"completedAt": "2026-05-15T18:00:00.000Z",
"quality": "720p",
"instanceName": "Main Sonarr",
"arrLink": "https://sonarr.example.com/series/my-show",
"allTags": ["alice"],
"matchedUserTag": "alice",
"arrRecordId": 1234,
"failureMessage": null
}
]
}
```
- `outcome` is `"imported"` or `"failed"`. Records with other event types (e.g. `grabbed`) are filtered out.
- `failureMessage` is only included when the authenticated user is an admin and `outcome` is `"failed"`.
- `arrRecordId` is only included for admin users.
- Results are sorted newest first.
- History data is cached server-side for 5 minutes (`history:sonarr` / `history:radarr` cache keys).
---
## 10. Frontend Architecture
The frontend is a **vanilla JavaScript SPA** with no build step. All logic resides in `app.js`, styled by `style.css`, and structured by `index.html`.
@@ -701,6 +752,7 @@ The status panel refreshes on a fixed 5-second timer and shows each SSE client w
| Variable | Required | Default | Description |
|----------|:--------:|---------|-------------|
| `POLL_INTERVAL` | No | `5000` | Poll interval in ms. Set to `0`, `off`, or `false` to disable background polling (on-demand mode). |
| `RECENT_COMPLETED_DAYS` | No | `7` | Default lookback window (days) for `GET /api/history/recent`. Overridable per-request via `?days=`. Max 90. |
| `LOG_LEVEL` | No | `info` | `debug`, `info`, `warn`, `error`, `silent` |
### Instance JSON Format
@@ -835,6 +887,7 @@ graph TB
sab_r[sabnzbd.js\n/api/sabnzbd]
sonarr_r[sonarr.js\n/api/sonarr]
radarr_r[radarr.js\n/api/radarr]
history_r[history.js\n/api/history]
end
subgraph Utilities
@@ -845,6 +898,7 @@ graph TB
tokenstore[tokenStore.js\ntokens.json]
sanitize[sanitizeError.js]
logger[logger.js]
historyfetcher[historyFetcher.js]
end
entry --> appfactory
@@ -854,11 +908,13 @@ graph TB
appfactory --> hm & rl & cp & ej
appfactory -->|pre-CSRF| auth
appfactory --> verifycsrf
appfactory --> dashboard & emby_r & sab_r & sonarr_r & radarr_r
appfactory --> dashboard & emby_r & sab_r & sonarr_r & radarr_r & history_r
dashboard & emby_r & sab_r & sonarr_r & radarr_r --> requireauth
dashboard & emby_r & sab_r & sonarr_r & radarr_r & history_r --> requireauth
auth --> tokenstore
dashboard --> cache & poller & config & qbt
history_r --> cache & config & historyfetcher
historyfetcher --> cache & config
poller --> cache & config & qbt & logger
qbt --> config & logger
auth & dashboard -.-> sanitize