Files
sofarr/docs/diagrams/class-data.puml
Gronod 9751dbf98d
Some checks failed
Build and Push Docker Image / build (push) Successful in 31s
CI / Security audit (push) Successful in 51s
CI / Tests & coverage (push) Successful in 1m6s
Render PlantUML Diagrams / Render .puml → .png (push) Failing after 47s
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
2026-05-17 10:20:52 +01:00

240 lines
5.2 KiB
Plaintext

@startuml class-data
!theme plain
title sofarr — Data Model Diagram
skinparam classAttributeIconSize 0
package "External API Responses" {
class "SABnzbd Queue Slot" as sabq {
+ filename : string
+ nzbname : string
+ percentage : string
+ mb : string
+ mbmissing : string
+ size : string
+ timeleft : string
+ status : string
+ storage : string
}
class "SABnzbd History Slot" as sabh {
+ name : string
+ nzb_name : string
+ nzbname : string
+ status : string
+ size : string
+ completed_time : string
+ storage : string
}
class "Sonarr Queue Record" as sqr {
+ id : number
+ seriesId : number
+ series : SonarrSeries
+ title : string
+ sourceTitle : string
+ trackedDownloadStatus : string
+ trackedDownloadState : string
+ statusMessages : StatusMessage[]
+ errorMessage : string
}
class "Sonarr History Record" as shr {
+ id : number
+ seriesId : number
+ title : string
+ sourceTitle : string
+ eventType : string
}
class "SonarrSeries" as ss {
+ id : number
+ title : string
+ titleSlug : string
+ path : string
+ tags : number[]
+ images : Image[]
+ _instanceUrl : string
}
class "Radarr Queue Record" as rqr {
+ id : number
+ movieId : number
+ movie : RadarrMovie
+ title : string
+ sourceTitle : string
+ trackedDownloadStatus : string
+ trackedDownloadState : string
+ statusMessages : StatusMessage[]
+ errorMessage : string
}
class "Radarr History Record" as rhr {
+ id : number
+ movieId : number
+ title : string
+ sourceTitle : string
+ eventType : string
}
class "RadarrMovie" as rm {
+ id : number
+ title : string
+ titleSlug : string
+ path : string
+ tags : number[]
+ images : Image[]
+ _instanceUrl : string
}
class "Tag" as tag {
+ id : number
+ label : string
}
class "Image" as img {
+ coverType : string
+ remoteUrl : string
+ url : string
}
class "StatusMessage" as sm {
+ title : string
+ messages : string[]
}
class "qBittorrent Torrent" as qbt {
+ name : string
+ hash : string
+ size : number
+ completed : number
+ progress : number (0-1)
+ state : string
+ dlspeed : number
+ eta : number
+ num_seeds : number
+ num_leechs : number
+ availability : number
+ category : string
+ tags : string
+ save_path : string
+ content_path : string
+ instanceId : string
+ instanceName : string
}
class "Emby User" as eu {
+ Id : string
+ Name : string
+ Policy : { IsAdministrator: boolean }
}
sqr *-- ss : embedded\n(includeSeries)
rqr *-- rm : embedded\n(includeMovie)
sqr *-- sm
rqr *-- sm
ss *-- img
rm *-- img
}
package "sofarr Internal Models" {
class "Download Object" as dl {
+ type : 'series' | 'movie' | 'torrent'
+ title : string
+ coverArt : string | null
+ status : string
+ progress : string
+ mb : string
+ mbmissing : string
+ size : string
+ speed : string
+ eta : string
+ seriesName : string | null
+ movieName : string | null
+ episodeInfo : object | null
+ movieInfo : object | null
+ allTags : string[]
+ matchedUserTag : string | null
+ tagBadges : TagBadge[] | undefined
+ importIssues : string[] | null
+ downloadPath : string | null
+ targetPath : string | null
+ arrLink : string | null
+ qbittorrent : boolean
+ seeds : number
+ peers : number
+ availability : string
+ rawSize : number
+ rawSpeed : number
+ rawEta : number
+ hash : string
+ category : string
+ completedAt : string
}
class "TagBadge" as tagbadge <<value>> {
+ label : string
+ matchedUser : string | null
}
class "API Response\n/user-downloads" as apir {
+ user : string
+ isAdmin : boolean
+ 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
+ cache : CacheStats
+ clients : ClientInfo[]
}
class "ServerInfo" as si {
+ uptimeSeconds : number
+ nodeVersion : string
+ memoryUsageMB : number
+ heapUsedMB : number
+ heapTotalMB : number
}
class "PollingInfo" as pi {
+ enabled : boolean
+ intervalMs : number
+ lastPoll : PollTimings
}
class "Session Cookie\nemby_user" as cookie {
+ id : string
+ name : string
+ isAdmin : boolean
' Note: Emby AccessToken intentionally excluded
}
apir *-- dl
sser *-- dl
statr *-- si
statr *-- pi
}
' Data flow connections
sabq ..> dl : matched &\ntransformed
sabh ..> dl : matched &\ntransformed
qbt ..> dl : mapTorrentToDownload()
ss ..> dl : coverArt, seriesName,\npath, tags
rm ..> dl : coverArt, movieName,\npath, tags
tag ..> dl : allTags / matchedUserTag
eu ..> cookie : login creates
eu ..> tagbadge : buildTagBadges()
dl *-- tagbadge : tagBadges[]
@enduml