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
1.8 KiB
Plaintext
68 lines
1.8 KiB
Plaintext
@startuml state-poller
|
||
!theme plain
|
||
title sofarr — Poller State Diagram
|
||
|
||
[*] --> CheckConfig : startPoller()
|
||
|
||
state CheckConfig <<choice>>
|
||
CheckConfig --> Disabled : POLL_INTERVAL = 0\nor 'off' / 'false'
|
||
CheckConfig --> Idle : POLL_INTERVAL > 0
|
||
|
||
state Disabled {
|
||
state "On-demand mode\nNo background timer" as od
|
||
od : Data fetched only when\na dashboard request\nfinds empty cache
|
||
}
|
||
|
||
Disabled --> Polling : pollAllServices()\n(triggered by dashboard request)
|
||
Polling --> Disabled : Poll complete\n(return to on-demand)
|
||
|
||
state Idle {
|
||
state "Waiting for\nnext interval" as waiting
|
||
}
|
||
|
||
Idle --> Polling : setInterval fires\nor immediate first poll
|
||
|
||
state Polling {
|
||
state "polling = true" as lock
|
||
state "Fetching all services\n(Promise.all)" as fetching
|
||
state "Storing results\nin cache" as storing
|
||
state "Recording timings" as timing
|
||
|
||
[*] --> lock
|
||
lock --> fetching
|
||
fetching --> storing : All promises resolved
|
||
fetching --> ErrorState : Any individual service\nerror (caught per-service)
|
||
storing --> notifying : Cache updated
|
||
state "Notifying SSE\nsubscribers" as notifying
|
||
notifying --> timing
|
||
timing --> [*] : polling = false
|
||
}
|
||
|
||
state ErrorState as "Handle Error" {
|
||
state "Log error\npolling = false" as err
|
||
}
|
||
|
||
ErrorState --> Idle : Next interval
|
||
Polling --> Idle : Poll complete\n(back to waiting)
|
||
|
||
state "Concurrent Poll\nAttempt" as skip {
|
||
state "polling === true\n→ skip" as sk
|
||
}
|
||
|
||
Idle --> skip : Interval fires while\nprevious still running
|
||
skip --> Idle : Log "still running,\nskipping"
|
||
|
||
note right of Polling
|
||
**Cache TTL**: POLL_INTERVAL × 3
|
||
Ensures data survives between polls
|
||
even if one cycle is slow.
|
||
end note
|
||
|
||
note right of Disabled
|
||
**Cache TTL**: 30000ms (30s)
|
||
After expiry, next dashboard
|
||
request triggers a fresh poll.
|
||
end note
|
||
|
||
@enduml
|