feat: replace client polling with Server-Sent Events (SSE)
Some checks failed
Build and Push Docker Image / build (push) Successful in 23s
CI / Security audit (push) Successful in 38s
CI / Tests & coverage (push) Failing after 38s

Server:
- poller.js: add pollSubscribers Set with onPollComplete/offPollComplete;
  notify all SSE callbacks immediately after every successful poll
- dashboard.js: add GET /api/dashboard/stream endpoint (text/event-stream)
  - requireAuth enforced via cookie (no CSRF needed — GET is a safe method)
  - X-Accel-Buffering: no for nginx proxy compatibility
  - 25s heartbeat comments to survive proxy idle timeouts
  - initial payload sent immediately on connect
  - cleanup on req.close: deregister callback, stop heartbeat, remove client
  - active client tracking updated: type='sse', connectedAt, no refreshRateMs

Frontend:
- app.js: replace setInterval/fetchUserDownloads with EventSource
  - startSSE() opens /api/dashboard/stream; stopSSE() closes it
  - first incoming message hides loading spinner
  - showAll toggle re-opens stream with ?showAll=true param
  - logout calls stopSSE() before POST /api/auth/logout
  - status panel: fixed 5s refresh, shows SSE clients + connect duration
  - statusRefreshHandle now always 5s, not tied to old refresh-rate selector
- index.html: remove now-unused refresh-rate <select> element

Docs:
- ARCHITECTURE.md §4.3: update poller description
- ARCHITECTURE.md §5: rename to SSE Stream (§5.2) + Download Matching (§5.3)
- ARCHITECTURE.md §7: update active client tracking description
- ARCHITECTURE.md §9: add /stream endpoint, update /status clients schema
- ARCHITECTURE.md §10: update key functions table; replace Auto-Refresh
  section with Live Push via SSE
- class-server.puml: add /stream to dashboard routes; update ClientInfo
- component.puml: annotate dashboard with SSE note; update label
This commit is contained in:
2026-05-17 08:35:22 +01:00
parent 55e4aedfca
commit abdd0da306
7 changed files with 413 additions and 125 deletions

View File

@@ -42,9 +42,11 @@ package "server/routes" {
- activeClients : Map<string, ClientInfo>
- CLIENT_STALE_MS : 30000
--
+ GET /stream (SSE, text/event-stream)
+ GET /user-downloads
+ GET /user-summary
+ GET /status
+ GET /cover-art
--
- getCoverArt(item) : string|null
- extractAllTags(tags, tagMap) : string[]
@@ -224,7 +226,8 @@ package "server/utils" {
class "ClientInfo" as ci <<value>> {
+ user : string
+ refreshRateMs : number
+ type : 'sse'
+ connectedAt : number (timestamp)
+ lastSeen : number (timestamp)
}
}

View File

@@ -30,7 +30,7 @@ package "Express Server" as server {
package "Routes" as routes {
[auth.js\n/api/auth\n(pre-CSRF)] as auth
[dashboard.js\n/api/dashboard] as dashboard
[dashboard.js\n/api/dashboard\n(+SSE /stream)] as dashboard
[emby.js\n/api/emby] as emby_route
[sabnzbd.js\n/api/sabnzbd] as sab_route
[sonarr.js\n/api/sonarr] as sonarr_route
@@ -86,6 +86,9 @@ package "Express Server" as server {
auth ..> sanitize
dashboard ..> sanitize
note "Browser uses EventSource\n(SSE) to /stream.\nServer pushes on each\npoll cycle — no client timer." as sseNote
sseNote .. dashboard
}
cloud "External Services" as external {