- docs/ARCHITECTURE.md: full system overview, technology stack, directory structure, component architecture, data flow, auth, polling/caching, download matching pipeline, API reference, frontend architecture, configuration, deployment guide - docs/diagrams/component.puml: system component diagram - docs/diagrams/seq-auth.puml: authentication sequence diagram - docs/diagrams/seq-dashboard.puml: dashboard request sequence diagram - docs/diagrams/seq-polling.puml: background polling cycle sequence - docs/diagrams/class-server.puml: server-side class/module diagram - docs/diagrams/class-data.puml: data model / entity diagram - docs/diagrams/state-ui.puml: frontend UI state diagram - docs/diagrams/state-poller.puml: poller state diagram - docs/diagrams/activity-matching.puml: download matching activity diagram
90 lines
3.0 KiB
Plaintext
90 lines
3.0 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 : polling = false\nlog elapsed time
|
||
|
||
deactivate poller
|
||
|
||
@enduml
|