Files
sofarr/docs/diagrams/class-server.puml
Gronod 36d183cba9
All checks were successful
Build and Push Docker Image / build (push) Successful in 23s
docs: comprehensive architecture documentation with PlantUML diagrams
- 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
2026-05-16 00:30:38 +01:00

198 lines
4.4 KiB
Plaintext

@startuml class-server
!theme plain
title sofarr — Server Class / Module Diagram
package "server/index.js" as entry {
class "EntryPoint" as ep <<module>> {
- LOG_LEVELS : Object
- currentLevel : number
- logFile : WriteStream
+ shouldLog(level) : boolean
--
Configures Express app,
mounts routes, starts poller
}
}
package "server/routes" {
class "auth.js" as auth <<router>> {
+ POST /login
+ GET /me
+ POST /logout
--
Authenticates via Emby API
Sets/reads httpOnly cookie
}
class "dashboard.js" as dashboard <<router>> {
- activeClients : Map<string, ClientInfo>
- CLIENT_STALE_MS : 30000
--
+ GET /user-downloads
+ GET /user-summary
+ GET /status
--
- getCoverArt(item) : string|null
- extractUserTag(tags, tagMap) : string|null
- sanitizeTagLabel(input) : string
- tagMatchesUser(tag, username) : boolean
- getImportIssues(record) : string[]|null
- getSonarrLink(series) : string|null
- getRadarrLink(movie) : string|null
- getActiveClients() : ClientInfo[]
}
class "emby.js" as emby_r <<router>> {
+ GET /sessions
+ GET /users/:id
+ GET /users
+ GET /session/:sessionId/user
}
class "sabnzbd.js" as sab_r <<router>> {
+ GET /queue
+ GET /history
}
class "sonarr.js" as sonarr_r <<router>> {
+ GET /queue
+ GET /history
+ GET /series/:id
+ GET /series
}
class "radarr.js" as radarr_r <<router>> {
+ GET /queue
+ GET /history
+ GET /movies/:id
+ GET /movies
}
}
package "server/utils" {
class "MemoryCache" as cache {
- store : Map<string, CacheEntry>
+ get(key) : any|null
+ set(key, value, ttlMs) : void
+ invalidate(key) : void
+ clear() : void
+ getStats() : CacheStats
}
class "CacheEntry" as ce <<value>> {
+ value : any
+ expiresAt : number
}
class "CacheStats" as cs <<value>> {
+ entryCount : number
+ totalSizeBytes : number
+ entries : CacheEntryStats[]
}
class "Poller" as poller <<module>> {
- POLL_INTERVAL : number
- POLLING_ENABLED : boolean
- polling : boolean
- lastPollTimings : PollTimings|null
- intervalHandle : number|null
--
+ startPoller() : void
+ stopPoller() : void
+ pollAllServices() : Promise<void>
+ getLastPollTimings() : PollTimings|null
--
- timed(label, fn) : TimedResult
}
class "PollTimings" as pt <<value>> {
+ totalMs : number
+ timestamp : string (ISO)
+ tasks : { label, ms }[]
}
class "Config" as config <<module>> {
+ getSABnzbdInstances() : Instance[]
+ getSonarrInstances() : Instance[]
+ getRadarrInstances() : Instance[]
+ getQbittorrentInstances() : Instance[]
--
- parseInstances(envVar, ...) : Instance[]
}
class "Instance" as inst <<value>> {
+ id : string
+ name : string
+ url : string
+ apiKey : string
+ username? : string
+ password? : string
}
class "QBittorrentClient" as qbt {
- id : string
- name : string
- url : string
- username : string
- password : string
- authCookie : string|null
--
+ login() : Promise<boolean>
+ makeRequest(endpoint, config) : Promise<Response>
+ getTorrents() : Promise<Torrent[]>
}
class "qbittorrent.js" as qbt_mod <<module>> {
- persistedClients : QBittorrentClient[]|null
--
+ getTorrents() : Promise<Torrent[]>
+ getClients() : QBittorrentClient[]
+ mapTorrentToDownload(torrent) : Download
+ formatBytes(bytes) : string
+ formatSpeed(bps) : string
+ formatEta(seconds) : string
}
class "Logger" as logger <<module>> {
- logFile : WriteStream
+ logToFile(message) : void
}
class "ClientInfo" as ci <<value>> {
+ user : string
+ refreshRateMs : number
+ lastSeen : number (timestamp)
}
}
' Relationships
ep --> auth
ep --> dashboard
ep --> emby_r
ep --> sab_r
ep --> sonarr_r
ep --> radarr_r
ep --> poller : startPoller()
dashboard --> cache : read/write
dashboard --> poller : pollAllServices()
dashboard --> qbt_mod : mapTorrentToDownload()
dashboard --> config
poller --> cache : set poll:* keys
poller --> config : get instances
poller --> qbt_mod : getTorrents()
qbt_mod --> config : getQbittorrentInstances()
qbt_mod *-- qbt : creates
qbt --> logger
cache *-- ce : stores
cache ..> cs : returns from getStats()
poller ..> pt : stores/returns
dashboard *-- ci : stores in activeClients
config ..> inst : returns
@enduml