R-1278: the install page says when a backup of an earlier install is kept and links the restore page (HU and EN). Red-proved. Commit only.
gates / gates (push) Successful in 1m33s
gates / gates (push) Successful in 1m33s
This commit is contained in:
@@ -2,6 +2,15 @@
|
||||
|
||||
**MinAgent: 0.158.1** (unchanged). The hold acts only on a box whose agent reports `waiting_for_quiet` (≥ 0.165.0).
|
||||
|
||||
### The install page says when a backup of an earlier install is kept (R-1278, P3)
|
||||
|
||||
- `internal/web/handlers.go` + `templates/deploy.html`: for an app not installed, when the box keeps a backup of an
|
||||
earlier install (`backup.KeptBackupsFor`: its set-aside units, D47, and the unit „remove — keep backups" left), an
|
||||
info notice says the new install starts empty and links „A megőrzött mentések megnyitása" (`/backups/restore`). HU and
|
||||
EN keys `deploy.kept_backup_offer{,_link}`; the hidden case renders byte for byte as before (the parity fixtures
|
||||
unchanged); a new parity case `deploy_new_kept_backup_r1278`. Tests: `TestR1278_*` (render per branch, the wiring, the
|
||||
count). Red-proof: felhom.eu `documentation/audits/night-2026-10-25/redproof-r1278.txt`.
|
||||
|
||||
### FileBrowser is off the app network: only traefik (and a linked OnlyOffice) reach it (R-1281, P2, D44 #23)
|
||||
|
||||
- **Measured before** (the night's rig, 0.344.0-rc.2): from the qBittorrent and Transmission containers (on
|
||||
@@ -13,6 +22,8 @@
|
||||
login was refused (R-1126's gate, unchanged) — never traefik-public any more; one an earlier controller put on
|
||||
traefik-public leaves it. OnlyOffice joins `felhom-files` while — and only while — its FileBrowser link is on (it
|
||||
fetches and saves documents at `http://filebrowser:80`, R-1018) — the one stated exception. The route stays by name.
|
||||
- **On the release every box re-creates FileBrowser once** (its rendered compose changed — a comment); files.<domain>
|
||||
is away for a few seconds, then served on the files network (seen on the night's rig).
|
||||
- Tests: `TestR1281_*` (three) + the two R-1126 gate tests updated to the files network. Red-proofs: felhom.eu
|
||||
`documentation/audits/night-2026-10-25/redproof-r1281.txt`.
|
||||
|
||||
|
||||
@@ -240,6 +240,26 @@ func (m *Manager) driveOfNamespace(nsRoot string) string {
|
||||
return nsRoot
|
||||
}
|
||||
|
||||
// KeptBackupsFor (R-1278) counts the backups of an EARLIER install of stack that the box keeps: its set-aside units
|
||||
// (D47) and the unit „remove — keep backups" left (a removed app's). The install page names them beside a fresh start.
|
||||
func (m *Manager) KeptBackupsFor(stack string) int {
|
||||
if m == nil || !validMirrorStackName(stack) {
|
||||
return 0
|
||||
}
|
||||
n := 0
|
||||
if m.settings != nil {
|
||||
for _, k := range m.settings.GetKeptCopies() {
|
||||
if k.Kind == KeptKindUnit && k.Source == stack {
|
||||
n++
|
||||
}
|
||||
}
|
||||
}
|
||||
if _, ok := m.RemovedAppUnitFor(stack); ok {
|
||||
n++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// SetAsideDirsForApp lists the app's set-aside folders (<root>/backups/set-aside/<stack>) that exist, on every
|
||||
// namespace root a unit of it can sit on.
|
||||
func (m *Manager) SetAsideDirsForApp(stack string) []string {
|
||||
|
||||
@@ -373,3 +373,21 @@ func TestR1288_ARestoreOfASetAsideUnitRetiresTheReplacedUnitQuietly(t *testing.T
|
||||
t.Fatalf("the next backup set something aside again: alarms %v, set-aside %v", alarms, r.setAside(t))
|
||||
}
|
||||
}
|
||||
|
||||
// R-1278: the install page's count of an earlier install's kept backups — a set-aside unit counts once per copy.
|
||||
func TestR1278_KeptBackupsForCountsTheSetAsideUnits(t *testing.T) {
|
||||
r := d47Setup(t, "aaaa000000000001")
|
||||
if n := r.m.KeptBackupsFor("tandoor"); n != 0 {
|
||||
t.Fatalf("before any set-aside: %d", n)
|
||||
}
|
||||
r.reinstall("bbbb000000000002")
|
||||
if err := r.m.captureRecoveryUnit("tandoor", false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if n := r.m.KeptBackupsFor("tandoor"); n != 1 {
|
||||
t.Fatalf("after one set-aside: %d, want 1", n)
|
||||
}
|
||||
if n := r.m.KeptBackupsFor("other"); n != 0 {
|
||||
t.Fatalf("another app counts nothing: %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1156,6 +1156,8 @@
|
||||
"deploy.email_kuldes": "Sending e-mail",
|
||||
"deploy.email_kuldes_engedelyezese_ennel_az": "Allow sending e-mail for this app",
|
||||
"deploy.engedelyezett_orszagok_ehhez_az_alkalmaz": "Allowed countries for this app",
|
||||
"deploy.kept_backup_offer": "The box kept a backup of an earlier install of this app. The new install starts empty; afterwards you can load the backup on the Restore page.",
|
||||
"deploy.kept_backup_offer_link": "Open the kept backups",
|
||||
"deploy.ennek_az_alkalmazasnak_van_korabbi": "This app has an earlier backup, so we filled in the fields below from its own backup.",
|
||||
"deploy.ez_az_alkalmazas_mar_telepitve": "This app is already installed. The settings below are read-only.",
|
||||
"deploy.ez_az_alkalmazas_tud_emailt": "This app can send e-mail (for example password resets, invitations) through Felhom,\n without setting up a separate provider. The sender address: <strong>{{.AppEmailFromAddress}}</strong>.",
|
||||
|
||||
@@ -1151,6 +1151,8 @@
|
||||
"deploy.email_kuldes": "Email-küldés",
|
||||
"deploy.email_kuldes_engedelyezese_ennel_az": "Email-küldés engedélyezése ennél az alkalmazásnál",
|
||||
"deploy.engedelyezett_orszagok_ehhez_az_alkalmaz": "Engedélyezett országok ehhez az alkalmazáshoz",
|
||||
"deploy.kept_backup_offer": "A doboz megőrizte ennek az alkalmazásnak egy korábbi telepítéséből származó mentését. Az új telepítés üresen indul; a mentést utána a Visszaállítás oldalon töltheted vissza.",
|
||||
"deploy.kept_backup_offer_link": "A megőrzött mentések megnyitása",
|
||||
"deploy.ennek_az_alkalmazasnak_van_korabbi": "Ennek az alkalmazásnak van korábbi mentése, ezért az alábbi mezőket a saját mentése alapján töltöttük ki.",
|
||||
"deploy.ez_az_alkalmazas_mar_telepitve": "Ez az alkalmazás már telepítve van. Az alábbi beállítások csak olvashatók.",
|
||||
"deploy.ez_az_alkalmazas_tud_emailt": "Ez az alkalmazás tud emailt küldeni (pl. jelszó-visszaállítás, meghívók) a Felhom-on keresztül,\n külön szolgáltató beállítása nélkül. A feladó címe: <strong>{{.AppEmailFromAddress}}</strong>.",
|
||||
|
||||
@@ -531,9 +531,11 @@ func (s *Server) deployPageData(w http.ResponseWriter, r *http.Request, name str
|
||||
data["RestoreRecordedAddress"] = ""
|
||||
data["RestoreRecordedDeclaresPath"] = declaresDataPath
|
||||
data["RestoreHasRecord"] = false
|
||||
data["KeptBackups"] = 0 // R-1278: backups of an earlier install the box keeps (set-aside or removed-app units)
|
||||
// Part 1's visibility line needs the real path, not a literal in a template.
|
||||
data["SystemDataPath"] = s.cfg.Paths.SystemDataPath
|
||||
if !alreadyDeployed && s.backupMgr != nil {
|
||||
data["KeptBackups"] = s.backupMgr.KeptBackupsFor(name)
|
||||
if place, addr, ok := s.backupMgr.RecordedUnitForStack(name); ok {
|
||||
prefill := map[string]string{}
|
||||
if addr.Known() {
|
||||
|
||||
@@ -181,6 +181,12 @@ func i18nCasesA() []i18nCase {
|
||||
{"deploy_new", "deploy", func() map[string]interface{} {
|
||||
return i18nDeployData(false, stacks.StateNotDeployed, 1)
|
||||
}},
|
||||
// R-1278 (2026-10-25 night): a backup of an earlier install is kept — the install page says so.
|
||||
{"deploy_new_kept_backup_r1278", "deploy", func() map[string]interface{} {
|
||||
d := i18nDeployData(false, stacks.StateNotDeployed, 1)
|
||||
d["KeptBackups"] = 1
|
||||
return d
|
||||
}},
|
||||
// R-1125 (v0.327.0, D10 = A): this page shows the same memory sentence — re-captured under this name.
|
||||
{"deploy_new_blocked_r1125", "deploy", func() map[string]interface{} {
|
||||
return i18nDeployData(false, stacks.StateNotDeployed, 2)
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// R-1278: a volume app's reinstall said nothing about the backup of the earlier install the box keeps (set aside by
|
||||
// D47, or left by „remove — keep backups"). The install page now says the install starts empty and links the restore
|
||||
// page. One render per branch of the gate (the template-gate rule): kept + not installed → shown; none → not shown;
|
||||
// installed → not shown.
|
||||
// RED-PROOF: delete the {{if .KeptBackups}} block → the first case fails.
|
||||
func TestR1278_TheInstallPageOffersTheKeptBackup(t *testing.T) {
|
||||
shown := renderDeployWithFields(t, false, map[string]interface{}{"KeptBackups": 2})
|
||||
if !strings.Contains(shown, `data-kept-backups="2"`) || !strings.Contains(shown, `href="/backups/restore"`) {
|
||||
t.Fatal("with a kept backup of an earlier install the page must say so and link the restore page")
|
||||
}
|
||||
if strings.Contains(renderDeployWithFields(t, false, map[string]interface{}{"KeptBackups": 0}), "kept-backup-offer") {
|
||||
t.Fatal("no kept backup → no notice")
|
||||
}
|
||||
if strings.Contains(renderDeployWithFields(t, false, map[string]interface{}{"KeptBackups": 1, "AlreadyDeployed": true}), "kept-backup-offer") {
|
||||
t.Fatal("an installed app shows no install-time notice")
|
||||
}
|
||||
}
|
||||
|
||||
// The page reads the count from the backup manager (seam-built-but-never-wired is a recorded class).
|
||||
func TestR1278_TheDeployPageAsksTheBackupManager(t *testing.T) {
|
||||
b, err := os.ReadFile("handlers.go")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(string(b), `data["KeptBackups"] = s.backupMgr.KeptBackupsFor(name)`) {
|
||||
t.Fatal("deployPageData must set KeptBackups from the backup manager")
|
||||
}
|
||||
}
|
||||
@@ -78,6 +78,15 @@
|
||||
{{T "deploy.ha_most_mashova_szeretned_telepiteni"}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{- if and (not .AlreadyDeployed) .KeptBackups}}{{/* R-1278: a backup of an earlier install is kept (set aside, D47,
|
||||
or left by „remove — keep backups"). The install starts empty; the backup is loaded afterwards on the
|
||||
restore page („Megőrzött másolatok"). */}}
|
||||
<div class="alert alert-info kept-backup-offer" data-kept-backups="{{.KeptBackups}}">
|
||||
<svg class="ico ico-sm"><use href="#i-info"/></svg>
|
||||
{{T "deploy.kept_backup_offer"}}
|
||||
<a href="/backups/restore" class="alert-link">{{T "deploy.kept_backup_offer_link"}}</a>
|
||||
</div>
|
||||
{{- end}}
|
||||
{{/* R-351 / Part 1 — WHERE THE DATA WILL LIVE, before the button is pressed. Measured
|
||||
2026-08-21: 13 of 53 catalog templates declare a storage field; the other 40 have none and
|
||||
their data goes to the system drive, which no screen said. This states it. It changes no
|
||||
|
||||
+1262
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user