docs(diagrams): review + fix all .puml files; touch all to trigger render
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
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
!theme plain
|
||||
title sofarr — Download Matching Activity Diagram
|
||||
|
||||
|
||||
start
|
||||
|
||||
:Read cached data from MemoryCache;
|
||||
|
||||
@@ -183,6 +183,14 @@ package "sofarr Internal Models" {
|
||||
+ downloads : Download[]
|
||||
}
|
||||
|
||||
class "SSE Event\n/stream (data: frame)" as sser {
|
||||
+ user : string
|
||||
+ isAdmin : boolean
|
||||
+ downloads : Download[]
|
||||
' Same shape as /user-downloads response.
|
||||
' Pushed after every poll cycle completes.
|
||||
}
|
||||
|
||||
class "Status Response\n/status" as statr {
|
||||
+ server : ServerInfo
|
||||
+ polling : PollingInfo
|
||||
@@ -212,6 +220,7 @@ package "sofarr Internal Models" {
|
||||
}
|
||||
|
||||
apir *-- dl
|
||||
sser *-- dl
|
||||
statr *-- si
|
||||
statr *-- pi
|
||||
}
|
||||
|
||||
@@ -136,11 +136,14 @@ package "server/utils" {
|
||||
- polling : boolean
|
||||
- lastPollTimings : PollTimings|null
|
||||
- intervalHandle : number|null
|
||||
- subscribers : Set<Function>
|
||||
--
|
||||
+ startPoller() : void
|
||||
+ stopPoller() : void
|
||||
+ pollAllServices() : Promise<void>
|
||||
+ getLastPollTimings() : PollTimings|null
|
||||
+ onPollComplete(cb) : void
|
||||
+ offPollComplete(cb) : void
|
||||
--
|
||||
- timed(label, fn) : TimedResult
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ package "Browser" as browser {
|
||||
package "Express Server" as server {
|
||||
|
||||
[index.js\nEntry Point] as entry
|
||||
[app.js\ncreatApp() factory] as appfactory
|
||||
[app.js\ncreateApp() factory] as appfactory
|
||||
|
||||
package "Middleware" {
|
||||
[helmet\n(CSP nonce, HSTS)] as hm
|
||||
@@ -111,8 +111,8 @@ poller --> sonarr : tags + queue + history
|
||||
poller --> radarr : tags + queue + history
|
||||
qbt --> qbit : login + torrents/info
|
||||
|
||||
appjs --> auth : POST /login\nGET /me
|
||||
appjs --> dashboard : GET /user-downloads\nGET /status
|
||||
appjs --> auth : POST /login\nGET /me\nGET /csrf\nPOST /logout
|
||||
appjs --> dashboard : GET /stream (SSE)\nGET /user-downloads\nGET /status
|
||||
es --> html : serve static
|
||||
|
||||
@enduml
|
||||
|
||||
@@ -54,14 +54,14 @@ alt Valid credentials
|
||||
Never sent to the client.
|
||||
31-day TTL, atomic JSON write.
|
||||
end note
|
||||
auth -> auth : Set emby_user cookie\n{ id, name, isAdmin }\nhttpOnly, sameSite=strict\nsecure (prod), signed (COOKIE_SECRET)\nrememberMe=true → Max-Age 30d\nrememberMe=false → session cookie
|
||||
auth -> auth : Set emby_user cookie\n{ id, name, isAdmin }\nhttpOnly, sameSite=strict\nsecure (if TRUST_PROXY), signed (COOKIE_SECRET)\nrememberMe=true → Max-Age 30d\nrememberMe=false → session cookie
|
||||
auth -> auth : Generate csrfToken\n(32-byte random hex)
|
||||
auth -> auth : Set csrf_token cookie\nhttpOnly=false (JS-readable)\nsameSite=strict, secure (prod)
|
||||
auth -> auth : Set csrf_token cookie\nhttpOnly=false (JS-readable)\nsameSite=strict, secure (if TRUST_PROXY)
|
||||
auth --> browser : { success: true, user, csrfToken }
|
||||
browser -> browser : store csrfToken in memory
|
||||
browser -> browser : fadeOutLogin()
|
||||
browser -> browser : showDashboard()
|
||||
browser -> browser : startAutoRefresh()
|
||||
browser -> browser : startSSE()
|
||||
browser -> browser : dismissSplash()
|
||||
else Invalid credentials
|
||||
emby --> auth : 401 Error
|
||||
@@ -82,7 +82,7 @@ browser -> browser : store new csrfToken in memory
|
||||
|
||||
== Logout ==
|
||||
user -> browser : Click Logout
|
||||
browser -> browser : stopAutoRefresh()
|
||||
browser -> browser : stopSSE()
|
||||
browser -> auth : POST /api/auth/logout\n(no CSRF required — auth routes\nexempt; sameSite:strict protects)
|
||||
activate auth
|
||||
auth -> auth : Parse emby_user cookie → user
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
!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
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
!theme plain
|
||||
title sofarr — Background Polling Cycle
|
||||
|
||||
|
||||
participant "index.js\n(startup)" as entry
|
||||
participant "Poller" as poller
|
||||
participant "Config" as config
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
!theme plain
|
||||
title sofarr — Poller State Diagram
|
||||
|
||||
|
||||
[*] --> CheckConfig : startPoller()
|
||||
|
||||
state CheckConfig <<choice>>
|
||||
|
||||
@@ -43,15 +43,12 @@ state Dashboard {
|
||||
state "Status Panel Closed" as status_closed
|
||||
|
||||
[*] --> rendering
|
||||
rendering --> rendering : SSE message received
|
||||
→ renderDownloads()
|
||||
rendering --> rendering : SSE message received\n-> renderDownloads()
|
||||
rendering --> rendering : Theme change
|
||||
|
||||
status_closed --> status_open : Click "Status" btn
|
||||
(admin only)
|
||||
status_open --> status_closed : Click close (×)
|
||||
status_open --> status_open : 5s timer
|
||||
→ renderStatusPanel()
|
||||
status_closed --> status_open : Click "Status" btn\n(admin only)
|
||||
status_open --> status_closed : Click close (x)
|
||||
status_open --> status_open : 5s timer -> renderStatusPanel()
|
||||
|
||||
[*] --> status_closed
|
||||
|
||||
@@ -66,8 +63,6 @@ state Dashboard {
|
||||
}
|
||||
}
|
||||
|
||||
Dashboard --> LoginForm : Logout
|
||||
(stopSSE,
|
||||
clear state)
|
||||
Dashboard --> LoginForm : Logout\n(stopSSE, clear state)
|
||||
|
||||
@enduml
|
||||
|
||||
Reference in New Issue
Block a user