docs: update ARCHITECTURE.md and README for history feature (v2)
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user