merge: develop into main (fix Mermaid diagram rendering)
This commit is contained in:
+18
-18
@@ -941,8 +941,8 @@ graph TB
|
|||||||
sonarr_r --> sonarr
|
sonarr_r --> sonarr
|
||||||
radarr_r --> radarr
|
radarr_r --> radarr
|
||||||
|
|
||||||
appjs -->|POST /login\nGET /me\nGET /csrf\nPOST /logout| auth
|
appjs -->|POST /login, GET /me, GET /csrf, POST /logout| auth
|
||||||
appjs -->|GET /stream SSE\nGET /user-downloads\nGET /status| dashboard
|
appjs -->|GET /stream SSE, GET /user-downloads, GET /status| dashboard
|
||||||
es -->|serve static| html
|
es -->|serve static| html
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -976,16 +976,16 @@ sequenceDiagram
|
|||||||
Note over Browser,Emby: Login
|
Note over Browser,Emby: Login
|
||||||
User->>Browser: Enter credentials (+ rememberMe)
|
User->>Browser: Enter credentials (+ rememberMe)
|
||||||
Browser->>Auth: POST /api/auth/login
|
Browser->>Auth: POST /api/auth/login
|
||||||
Note right of Auth: Rate limit: max 10 failed\nattempts per IP / 15 min
|
Note right of Auth: Rate limit: max 10 failed attempts per IP / 15 min
|
||||||
Auth->>Emby: POST /Users/authenticatebyname\nDeviceId = sha256(username)[0:16]
|
Auth->>Emby: POST /Users/authenticatebyname (DeviceId = sha256(username)[0:16])
|
||||||
alt Valid credentials
|
alt Valid credentials
|
||||||
Emby-->>Auth: { User.Id, AccessToken }
|
Emby-->>Auth: { User.Id, AccessToken }
|
||||||
Auth->>Emby: GET /Users/{id}
|
Auth->>Emby: GET /Users/{id}
|
||||||
Emby-->>Auth: { Name, Policy.IsAdministrator }
|
Emby-->>Auth: { Name, Policy.IsAdministrator }
|
||||||
Auth->>Tokens: storeToken(userId, AccessToken)
|
Auth->>Tokens: storeToken(userId, AccessToken)
|
||||||
Note right of Tokens: Server-side only\n31-day TTL, atomic write
|
Note right of Tokens: Server-side only, 31-day TTL, atomic write
|
||||||
Auth->>Auth: Set emby_user cookie\nhttpOnly, sameSite=strict\nsecure (if TRUST_PROXY)\nrememberMe → Max-Age 30d
|
Auth->>Auth: Set emby_user cookie (httpOnly, sameSite=strict, secure if TRUST_PROXY)
|
||||||
Auth->>Auth: Set csrf_token cookie\nhttpOnly=false, sameSite=strict
|
Auth->>Auth: Set csrf_token cookie (httpOnly=false, sameSite=strict)
|
||||||
Auth-->>Browser: { success: true, user, csrfToken }
|
Auth-->>Browser: { success: true, user, csrfToken }
|
||||||
Browser->>Browser: showDashboard() + startSSE()
|
Browser->>Browser: showDashboard() + startSSE()
|
||||||
else Invalid credentials
|
else Invalid credentials
|
||||||
@@ -1023,7 +1023,7 @@ sequenceDiagram
|
|||||||
User->>Browser: Login success / valid session
|
User->>Browser: Login success / valid session
|
||||||
Browser->>Dashboard: GET /api/dashboard/stream (EventSource)
|
Browser->>Dashboard: GET /api/dashboard/stream (EventSource)
|
||||||
Dashboard->>Dashboard: requireAuth: extract user/isAdmin
|
Dashboard->>Dashboard: requireAuth: extract user/isAdmin
|
||||||
Dashboard->>Dashboard: Set Content-Type: text/event-stream\nRegister in activeClients
|
Dashboard->>Dashboard: Set Content-Type: text/event-stream, register in activeClients
|
||||||
|
|
||||||
opt Polling disabled AND cache empty
|
opt Polling disabled AND cache empty
|
||||||
Dashboard->>Poller: pollAllServices()
|
Dashboard->>Poller: pollAllServices()
|
||||||
@@ -1033,7 +1033,7 @@ sequenceDiagram
|
|||||||
end
|
end
|
||||||
|
|
||||||
Dashboard->>Cache: get all poll:* keys
|
Dashboard->>Cache: get all poll:* keys
|
||||||
Dashboard->>Dashboard: Build maps, match downloads\nextractUserTag / buildTagBadges
|
Dashboard->>Dashboard: Build maps, match downloads, extractUserTag / buildTagBadges
|
||||||
Dashboard-->>Browser: data: { user, isAdmin, downloads }
|
Dashboard-->>Browser: data: { user, isAdmin, downloads }
|
||||||
Browser->>Browser: hideLoading() + renderDownloads()
|
Browser->>Browser: hideLoading() + renderDownloads()
|
||||||
|
|
||||||
@@ -1050,7 +1050,7 @@ sequenceDiagram
|
|||||||
|
|
||||||
User->>Browser: Close tab / logout
|
User->>Browser: Close tab / logout
|
||||||
Browser->>Dashboard: TCP close (req close event)
|
Browser->>Dashboard: TCP close (req close event)
|
||||||
Dashboard->>Dashboard: offPollComplete(cb)\nclearInterval(heartbeat)\ndelete activeClients[key]
|
Dashboard->>Dashboard: offPollComplete(cb), clearInterval(heartbeat), delete activeClients[key]
|
||||||
```
|
```
|
||||||
|
|
||||||
### 13.4 Background Polling Cycle
|
### 13.4 Background Polling Cycle
|
||||||
@@ -1094,8 +1094,8 @@ sequenceDiagram
|
|||||||
Poller->>QBT: getTorrents()
|
Poller->>QBT: getTorrents()
|
||||||
QBT-->>Poller: [{ name, progress, ... }]
|
QBT-->>Poller: [{ name, progress, ... }]
|
||||||
|
|
||||||
Poller->>Poller: Record per-task timings\nlastPollTimings = { totalMs, timestamp, tasks }
|
Poller->>Poller: Record per-task timings: lastPollTimings = { totalMs, timestamp, tasks }
|
||||||
Poller->>Cache: set poll:* keys (TTL = POLL_INTERVAL × 3)
|
Poller->>Cache: set poll:* keys (TTL = POLL_INTERVAL x 3)
|
||||||
Poller->>Poller: Notify SSE subscribers (forEach cb())
|
Poller->>Poller: Notify SSE subscribers (forEach cb())
|
||||||
Poller->>Poller: polling = false
|
Poller->>Poller: polling = false
|
||||||
```
|
```
|
||||||
@@ -1330,11 +1330,11 @@ stateDiagram-v2
|
|||||||
Submitting --> [*] : Auth success
|
Submitting --> [*] : Auth success
|
||||||
}
|
}
|
||||||
|
|
||||||
LoginForm --> Dashboard : Auth success\n(fade transition)
|
LoginForm --> Dashboard : Auth success (fade transition)
|
||||||
|
|
||||||
state Dashboard {
|
state Dashboard {
|
||||||
[*] --> Rendering
|
[*] --> Rendering
|
||||||
Rendering --> Rendering : SSE message → renderDownloads()
|
Rendering --> Rendering : SSE message triggers renderDownloads()
|
||||||
Rendering --> Rendering : Theme change
|
Rendering --> Rendering : Theme change
|
||||||
|
|
||||||
state SSEConnection {
|
state SSEConnection {
|
||||||
@@ -1368,13 +1368,13 @@ stateDiagram-v2
|
|||||||
|
|
||||||
state Disabled {
|
state Disabled {
|
||||||
[*] --> OnDemand
|
[*] --> OnDemand
|
||||||
OnDemand : No background timer.\nData fetched when dashboard\nrequest finds empty cache.
|
OnDemand : No background timer. Data fetched when dashboard request finds empty cache.
|
||||||
}
|
}
|
||||||
|
|
||||||
Disabled --> Polling : dashboard triggers pollAllServices()
|
Disabled --> Polling : dashboard triggers pollAllServices()
|
||||||
Polling --> Disabled : Poll complete (on-demand)
|
Polling --> Disabled : Poll complete (on-demand)
|
||||||
|
|
||||||
Idle --> Polling : setInterval fires\nor immediate first poll
|
Idle --> Polling : setInterval fires or immediate first poll
|
||||||
|
|
||||||
state Polling {
|
state Polling {
|
||||||
[*] --> Locked
|
[*] --> Locked
|
||||||
@@ -1382,7 +1382,7 @@ stateDiagram-v2
|
|||||||
Locked --> Fetching
|
Locked --> Fetching
|
||||||
Fetching --> Storing : All promises resolved
|
Fetching --> Storing : All promises resolved
|
||||||
Fetching --> HandleError : Per-service error (caught)
|
Fetching --> HandleError : Per-service error (caught)
|
||||||
Storing --> Notifying : Cache updated\nTTL = POLL_INTERVAL × 3
|
Storing --> Notifying : Cache updated, TTL = POLL_INTERVAL x 3
|
||||||
Notifying : Notify SSE subscribers
|
Notifying : Notify SSE subscribers
|
||||||
Notifying --> Done
|
Notifying --> Done
|
||||||
Done : polling = false
|
Done : polling = false
|
||||||
@@ -1401,7 +1401,7 @@ stateDiagram-v2
|
|||||||
[*] --> Skip
|
[*] --> Skip
|
||||||
Skip : polling === true, skip cycle
|
Skip : polling === true, skip cycle
|
||||||
}
|
}
|
||||||
Idle --> ConcurrentSkip : Interval fires while\nprevious still running
|
Idle --> ConcurrentSkip : Interval fires while previous still running
|
||||||
ConcurrentSkip --> Idle : Log skip
|
ConcurrentSkip --> Idle : Log skip
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user