feat: replace client polling with Server-Sent Events (SSE)
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:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user