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
95 lines
3.2 KiB
Plaintext
95 lines
3.2 KiB
Plaintext
@startuml seq-polling
|
||
!theme plain
|
||
title sofarr — Background Polling Cycle
|
||
|
||
|
||
participant "index.js\n(startup)" as entry
|
||
participant "Poller" as poller
|
||
participant "Config" as config
|
||
participant "SABnzbd\n(per instance)" as sab
|
||
participant "Sonarr\n(per instance)" as sonarr
|
||
participant "Radarr\n(per instance)" as radarr
|
||
participant "qBittorrent\nClient" as qbt
|
||
participant "MemoryCache" as cache
|
||
|
||
== Startup ==
|
||
entry -> poller : startPoller()
|
||
activate poller
|
||
|
||
alt POLL_INTERVAL > 0
|
||
poller -> poller : pollAllServices() (immediate)
|
||
poller -> poller : setInterval(pollAllServices,\nPOLL_INTERVAL)
|
||
else POLL_INTERVAL = 0
|
||
poller --> entry : "Polling disabled, on-demand mode"
|
||
end
|
||
|
||
== Poll Cycle ==
|
||
poller -> poller : Check: polling flag?\n(skip if concurrent)
|
||
poller -> poller : polling = true
|
||
poller -> poller : start = Date.now()
|
||
|
||
poller -> config : getSABnzbdInstances()
|
||
config --> poller : [{ id, url, apiKey }]
|
||
poller -> config : getSonarrInstances()
|
||
config --> poller : [{ id, url, apiKey }]
|
||
poller -> config : getRadarrInstances()
|
||
config --> poller : [{ id, url, apiKey }]
|
||
|
||
note over poller : All fetches run in\nparallel via Promise.all,\neach wrapped in timed()
|
||
|
||
par SABnzbd Queue
|
||
poller -> sab : GET /api?mode=queue
|
||
sab --> poller : { queue: { slots, status, speed } }
|
||
and SABnzbd History
|
||
poller -> sab : GET /api?mode=history&limit=10
|
||
sab --> poller : { history: { slots } }
|
||
and Sonarr Tags
|
||
poller -> sonarr : GET /api/v3/tag
|
||
sonarr --> poller : [{ id, label }]
|
||
and Sonarr Queue
|
||
poller -> sonarr : GET /api/v3/queue\n?includeSeries=true
|
||
sonarr --> poller : { records: [{ seriesId, series, ... }] }
|
||
and Sonarr History
|
||
poller -> sonarr : GET /api/v3/history\n?pageSize=10
|
||
sonarr --> poller : { records: [{ seriesId, ... }] }
|
||
and Radarr Queue
|
||
poller -> radarr : GET /api/v3/queue\n?includeMovie=true
|
||
radarr --> poller : { records: [{ movieId, movie, ... }] }
|
||
and Radarr History
|
||
poller -> radarr : GET /api/v3/history\n?pageSize=10
|
||
radarr --> poller : { records: [{ movieId, ... }] }
|
||
and Radarr Tags
|
||
poller -> radarr : GET /api/v3/tag
|
||
radarr --> poller : [{ id, label }]
|
||
and qBittorrent
|
||
poller -> qbt : getTorrents()
|
||
qbt --> poller : [{ name, progress, ... }]
|
||
end
|
||
|
||
poller -> poller : Record per-task timings\nlastPollTimings = { totalMs,\ntimestamp, tasks: [{label, ms}] }
|
||
|
||
poller -> poller : cacheTTL = POLL_INTERVAL × 3
|
||
|
||
poller -> cache : set('poll:sab-queue', ..., cacheTTL)
|
||
poller -> cache : set('poll:sab-history', ..., cacheTTL)
|
||
poller -> cache : set('poll:sonarr-tags', ..., cacheTTL)
|
||
|
||
note over poller : Tag queue records with\n_instanceUrl on embedded\nseries/movie objects
|
||
|
||
poller -> cache : set('poll:sonarr-queue', ..., cacheTTL)
|
||
poller -> cache : set('poll:sonarr-history', ..., cacheTTL)
|
||
poller -> cache : set('poll:radarr-queue', ..., cacheTTL)
|
||
poller -> cache : set('poll:radarr-history', ..., cacheTTL)
|
||
poller -> cache : set('poll:radarr-tags', ..., cacheTTL)
|
||
poller -> cache : set('poll:qbittorrent', ..., cacheTTL)
|
||
|
||
poller -> poller : Notify SSE subscribers\npollSubscribers.forEach(cb => cb())
|
||
|
||
note over poller : Each registered SSE client\ncallback rebuilds its payload\nand writes a data: frame
|
||
|
||
poller -> poller : polling = false\nlog elapsed time
|
||
|
||
deactivate poller
|
||
|
||
@enduml
|