v0.342.0 (unreleased): the upload and CPU watch — an app that sends or burns far more than its own numbers explain is one operator-only alarm; the line learnt from the off-site push; qBittorrent's default upload limit (never over the household's)
gates / gates (push) Successful in 1m17s
gates / gates (push) Successful in 1m17s
internal/upwatch: exact per-container counters from the Docker Engine API, per-app judging (upload >= max(5 Mbit/s, 25% of the line) for 15 min; CPU >= max(150%, 50% x cores) for 30 min), explainers qBittorrent / Transmission / Plex, app_unexplained_upload / app_unexplained_cpu + _cleared (hub 0.169.0). The off-site push is sampled once a second for the line (a lower bound). qBittorrent limit = 50% of the line, the household's limit always wins. 20 red-proofs. Spike + design: felhom.eu audits/SPIKE-upload-watch-2026-10-23.md, architecture/08 section 9. Not released.
This commit is contained in:
@@ -27,6 +27,7 @@ import (
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/settings"
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/stacks"
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/system"
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/upwatch"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
@@ -804,6 +805,13 @@ func (s *Server) appCommonData(w http.ResponseWriter, r *http.Request, slug stri
|
||||
if lost, at := lostFilesNote(found.Deployed, found.AppConfig); len(lost) > 0 {
|
||||
data["LostFiles"], data["LostBootAt"] = lost, at
|
||||
}
|
||||
// The upload watch (internal/upwatch): qBittorrent's limit, while the box owns it — one line the household can act on
|
||||
// in qBittorrent itself. A limit the household set is theirs and is not described here.
|
||||
if found.Meta.Slug == "qbittorrent" && found.Deployed && s.upwatchView != nil {
|
||||
if v := s.upwatchView(); v.QBLimitBps > 0 {
|
||||
data["QBLimitNote"] = s.msgLang(s.langFor(r), "upwatch.qb_limit_note", upwatch.MbpsText(v.QBLimitBps))
|
||||
}
|
||||
}
|
||||
// v0.280.0 (decision 46): the setup gate's card, while the gate stands.
|
||||
data["SetupGateClosed"] = found.Deployed && found.AppConfig != nil && found.AppConfig.SetupGate.Closed()
|
||||
data["SetupGateHasProbe"] = found.Meta.SetupDoneProbe != nil && found.Meta.SetupDoneProbe.URL != ""
|
||||
@@ -2349,6 +2357,12 @@ func (s *Server) settingsBaseData(page, title string) map[string]interface{} {
|
||||
func (s *Server) systemPageData() map[string]interface{} {
|
||||
data := s.settingsBaseData("settings", "Beállítások")
|
||||
data["TitleKey"] = "page.title.settings" // i18n: the Hungarian title above is what hu renders
|
||||
// The upload watch's measured line (a lower bound, learnt from the nightly off-site push); "" = still learning.
|
||||
if s.upwatchView != nil {
|
||||
if v := s.upwatchView(); v.LineKnown {
|
||||
data["UplinkLine"] = upwatch.MbpsText(int64(v.LineMbps * 1e6 / 8))
|
||||
}
|
||||
}
|
||||
data["GitRepoURL"] = s.cfg.Git.RepoURL
|
||||
data["GitSyncInterval"] = s.cfg.Git.SyncInterval
|
||||
data["BackupEnabled"] = s.cfg.Backup.Enabled
|
||||
|
||||
Reference in New Issue
Block a user