controller v0.264.0: the household is told when an update is undone or held, in its language
gates / gates (push) Successful in 25s

app_update_undone / app_update_held events (09 decision 15), on by
default and seeded once on existing boxes; R-606 update sentences as
key+args rendered per reader; R-646 startup applied-meta backfill for
apps current with the catalog; R-620 a disabled notifier WARNs once per
event type. Needs hub v0.120.0.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-09-23 13:51:21 +02:00
parent c3a2aba0d2
commit bc278944a3
27 changed files with 1086 additions and 86 deletions
+36 -19
View File
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"gitea.dooplex.hu/admin/felhom-controller/internal/i18n"
"gitea.dooplex.hu/admin/felhom-controller/internal/util"
"os"
"path/filepath"
@@ -142,37 +143,53 @@ func (m *Manager) UpdateTierOrderFor(stackName string) []int {
// UpdateCopyHolds is the customer phrase for what a copy on `tier` holds for this app — the second half
// of the R-479 ruling: the hold sentence names WHAT the chosen copy holds, not only where it is.
//
// v0.264.0 (R-606): the phrase is a bundle key — it is a PROMISE ABOUT WHETHER THE HOUSEHOLD'S FILES
// COME BACK and must reach an English household in English. The value returned (and stored in the
// hold) is still the Hungarian, byte for byte; copyHoldsIn maps it back to its key at render time, so
// a hold written by any earlier version localises too.
func (m *Manager) UpdateCopyHolds(stackName string, tier int) string {
outside := m.DataOutsideUnit(stackName)
return util.Text(i18n.Default, updateCopyHoldsKey(m.DataOutsideUnit(stackName), tier))
}
func updateCopyHoldsKey(outside bool, tier int) string {
switch tier {
case UpdateTierLocal:
if outside {
return "csak a beállításokat és az adatbázist tartalmazza, a fájlokat nem"
return "hold.copy_holds.db_only"
}
return "a beállításokat, az adatbázist és az adatköteteket tartalmazza"
case UpdateTierSecondDrive:
return "hold.copy_holds.volumes"
case UpdateTierSecondDrive, UpdateTierOffsite:
if outside {
return "a beállításokat, az adatbázist és a fájlokat tartalmazza"
return "hold.copy_holds.files"
}
return "a beállításokat, az adatbázist és az adatköteteket tartalmazza"
case UpdateTierOffsite:
if outside {
return "a beállításokat, az adatbázist és a fájlokat tartalmazza"
}
return "a beállításokat, az adatbázist és az adatköteteket tartalmazza"
return "hold.copy_holds.volumes"
}
return ""
}
// UpdateTierLabel is a tier's name in the customer's hold sentence. "" for an unknown tier.
func UpdateTierLabel(tier int) string {
// copyHoldKeys are the phrases a hold may have stored; the stored value is the Hungarian.
var copyHoldKeys = []string{"hold.copy_holds.db_only", "hold.copy_holds.volumes", "hold.copy_holds.files"}
// copyHoldsIn renders a STORED copy-holds phrase in lang. An unknown phrase is returned as stored —
// never an empty clause (a hold must not lose the sentence that says what the copy holds).
func copyHoldsIn(lang, stored string) string {
for _, k := range copyHoldKeys {
if util.Text(i18n.Default, k) == stored {
return util.Text(lang, k)
}
}
return stored
}
// UpdateTierLabel is a tier's name in the customer's hold sentence (Hungarian). "" for an unknown tier.
func UpdateTierLabel(tier int) string { return UpdateTierLabelIn(i18n.Default, tier) }
// UpdateTierLabelIn is UpdateTierLabel in lang (v0.264.0, R-606).
func UpdateTierLabelIn(lang string, tier int) string {
switch tier {
case UpdateTierSecondDrive:
return "második meghajtó"
case UpdateTierLocal:
return "saját meghajtó"
case UpdateTierOffsite:
return "távoli mentés"
case UpdateTierSecondDrive, UpdateTierLocal, UpdateTierOffsite:
return util.Text(lang, fmt.Sprintf("hold.update.tier.%d", tier))
}
return ""
}