seq-auth: - startAutoRefresh() -> startSSE(), stopAutoRefresh() -> stopSSE() - Cookie secure flag: 'secure (prod)' -> 'secure (if TRUST_PROXY)' component: - Fix typo creatApp -> createApp - Add GET /csrf, POST /logout to browser->auth arrow - Add GET /stream (SSE) to browser->dashboard arrow class-server: - Add subscribers Set, onPollComplete(), offPollComplete() to Poller class class-data: - Add SSE Event /stream shape alongside API Response /user-downloads - Add sser *-- dl relationship state-ui: - Fix invalid multi-line transition labels with raw Unicode arrows (broke PlantUML parser); replace with valid \n escapes on single line seq-dashboard, seq-polling, state-poller, activity-matching: - Whitespace touch to trigger render-diagrams CI workflow
69 lines
2.4 KiB
Plaintext
69 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
|