README.md: - Node prerequisite: v12+ → v22+ - Real-Time Updates: describe SSE push, remove polling/refresh-selector wording - On-demand mode: update for SSE connect triggering poll - API Endpoints: add /stream, /me, /csrf, /user-summary, /status, /cover-art - Remove stale /api/qbittorrent proxy entry - Docker tags: update to 1.0.x SECURITY.md: - Supported versions: add 1.0.x, retire 0.2.x - CSP header: add style-src-attr 'unsafe-inline' - Nginx example: add proxy_buffering off / proxy_read_timeout for SSE Diagrams: - seq-dashboard.puml: rewrite as SSE stream sequence (connect, initial payload, pushed updates, heartbeat, disconnect) - seq-polling.puml: add SSE subscriber notification step after cache population - state-ui.puml: replace Refresh Rate sub-state with SSE Connection state machine; update splash loading and logout transitions - state-poller.puml: add Notifying SSE subscribers step in Polling state package.json: bump to 1.0.0
68 lines
2.4 KiB
Plaintext
68 lines
2.4 KiB
Plaintext
@startuml seq-dashboard
|
|
!theme plain
|
|
title sofarr — Dashboard SSE Stream Sequence
|
|
|
|
actor User as user
|
|
participant "Browser\n(app.js)" as browser
|
|
participant "Express\n/api/dashboard" as dashboard
|
|
participant "MemoryCache" as cache
|
|
participant "Poller" as poller
|
|
participant "External\nServices" as ext
|
|
|
|
== SSE Connection (on login / page load) ==
|
|
user -> browser : Login success\nor valid session
|
|
activate browser
|
|
browser -> dashboard : GET /api/dashboard/stream\n(EventSource, Cookie: emby_user)
|
|
activate dashboard
|
|
|
|
dashboard -> dashboard : requireAuth: parse cookie\nextract username, isAdmin
|
|
dashboard -> dashboard : Set headers:\nContent-Type: text/event-stream\nX-Accel-Buffering: no
|
|
dashboard -> dashboard : Register in activeClients Map\n{ user, type:'sse', connectedAt }
|
|
|
|
alt Polling disabled AND cache empty
|
|
dashboard -> poller : pollAllServices()
|
|
activate poller
|
|
poller -> ext : Parallel API calls
|
|
ext --> poller : Raw data
|
|
poller -> cache : set poll:* keys (TTL=30s)
|
|
deactivate poller
|
|
end
|
|
|
|
== Initial Payload (sent immediately on connect) ==
|
|
dashboard -> cache : get all poll:* keys
|
|
dashboard -> dashboard : Build seriesMap, moviesMap,\nsonarrTagMap, radarrTagMap
|
|
alt showAll=true
|
|
dashboard -> cache : get('emby:users')
|
|
alt cache miss
|
|
dashboard -> ext : GET /Users (Emby)
|
|
ext --> dashboard : [{ Name, ... }]
|
|
dashboard -> cache : set('emby:users', map, 60s)
|
|
end
|
|
end
|
|
dashboard -> dashboard : Match SABnzbd/qBit downloads\nvs Sonarr/Radarr records\nextractUserTag / buildTagBadges
|
|
dashboard --> browser : data: { user, isAdmin, downloads }
|
|
browser -> browser : hideLoading()\nrenderDownloads()
|
|
|
|
== Pushed Updates (on every poll cycle) ==
|
|
loop Each poll cycle completes
|
|
poller -> poller : pollAllServices() complete
|
|
poller -> dashboard : onPollComplete callback fires
|
|
dashboard -> cache : get all poll:* keys
|
|
dashboard -> dashboard : Rebuild download payload
|
|
dashboard --> browser : data: { user, isAdmin, downloads }
|
|
browser -> browser : renderDownloads() (diff-based)
|
|
end
|
|
|
|
== Heartbeat (every 25s) ==
|
|
dashboard --> browser : : heartbeat
|
|
note right : Keeps connection alive\nthrough idle-timeout proxies
|
|
|
|
== Client Disconnects ==
|
|
user -> browser : Close tab / logout
|
|
browser -> dashboard : TCP close (req 'close' event)
|
|
dashboard -> dashboard : offPollComplete(callback)\nclearInterval(heartbeat)\ndelete activeClients[key]
|
|
deactivate dashboard
|
|
deactivate browser
|
|
|
|
@enduml
|