All checks were successful
Build and Push Docker Image / build (push) Successful in 24s
ARCHITECTURE.md: - Directory structure: add middleware/requireAuth.js and favicon assets - §4.1: remove CORS from middleware list - §4.2: all proxy routes now auth-required via requireAuth; add middleware description - §6: cookie payload corrected (no token); document secure+sameSite - §7: add emby:users cache key (60s TTL) - §8: Download Object table: userTag → allTags/matchedUserTag/tagBadges - §9 POST /login: document cookie security attributes - §10: add Tag Badge Rendering section; remove hardcoded line count Diagrams: - class-server.puml: add requireAuth middleware module; update dashboard.js methods (extractAllTags, extractUserTag w/ username, buildTagBadges, getEmbyUsers); add TagBadge value class; add auth relationships for all proxy routes - class-data.puml: Download Object userTag → allTags/matchedUserTag/ tagBadges; add TagBadge class; remove token from Session Cookie - seq-auth.puml: cookie payload no longer contains token; add secure/sameSite note - component.puml: remove CORS component; add requireAuth; consolidate Emby connection to show tag badge + user-summary usage - activity-matching.puml: update to extractAllTags/extractUserTag (with username); showAll uses hasAnyTag; tagBadges built from embyUserMap; add Emby user fetch step; update legend - seq-dashboard.puml: add emby:users cache lookup / Emby fetch for showAll; update matching groups to show tag classification; add tag badge rendering note on renderDownloads()
231 lines
4.9 KiB
Plaintext
231 lines
4.9 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 "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
|
|
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
|