v0.56.0: Phase 4 — FileBrowser scoping + deploy DB-on-SSD note + monitoring descriptions
4A: scope FileBrowser bind to <drive>/appdata (recovery units + Tier 2 copies under backups/ are no longer mounted into FileBrowser — customer can't browse/delete the thing that restores them). 4B: deploy storage-selection step states the chosen drive holds files while the DB runs on the fast internal SSD + is backed up with the app. 4C: buildStorageBars stable sort + purpose description on the monitoring storage list. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,23 @@
|
|||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
### v0.56.0 — Phase 4: FileBrowser scoping + deploy DB-on-SSD note + monitoring storage descriptions (2026-06-13)
|
||||||
|
|
||||||
|
Polish layer closing the slice.
|
||||||
|
|
||||||
|
- **4A FileBrowser scoping (safety):** the FileBrowser bind mount is now scoped to each drive's
|
||||||
|
`appdata/` subtree (`<drive>/appdata:/srv/<name>`) instead of the whole drive root. The recovery
|
||||||
|
units + Tier 2 copies under `backups/` are therefore **not mounted into FileBrowser at all** — the
|
||||||
|
customer browses their userdata but cannot reach (or even see) the thing that restores them. The
|
||||||
|
appdata dir is `mkdir`-ed before the bind so the source exists. (`syncFileBrowserMounts`.)
|
||||||
|
- **4B Deploy-UI communication:** the storage-selection step now states plainly (Hungarian) that the
|
||||||
|
chosen drive holds the app's **files**, while its **database runs on the fast internal SSD** and is
|
||||||
|
backed up alongside the app — so "the DB is on the SSD" stops being a surprise. (`deploy.html`.)
|
||||||
|
- **4C Monitoring storage list:** `buildStorageBars` now sorts deterministically (by path) and carries a
|
||||||
|
**purpose description** explaining the user-data drives (rendered on the monitoring "Tárolók
|
||||||
|
kapacitása" list). Note: this list is the controller's registered user-data drives only (the agent's
|
||||||
|
local/local-lvm/pbs storage is not in this registry), so the role-tier sort/`local`-vs-`local-lvm`
|
||||||
|
descriptions belong to the agent-backed storage-management page, not here.
|
||||||
|
|
||||||
### v0.55.0 — Phase 3: auto off-drive Tier 2 (rootfs-headroom guard, durable off-disk target) (2026-06-13)
|
### v0.55.0 — Phase 3: auto off-drive Tier 2 (rootfs-headroom guard, durable off-disk target) (2026-06-13)
|
||||||
|
|
||||||
Tier 2 = an **off-drive copy** of each HDD app's recovery unit + bulk userdata to a **different physical
|
Tier 2 = an **off-drive copy** of each HDD app's recovery unit + bulk userdata to a **different physical
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -31,13 +32,21 @@ var protectedStackSubdomains = map[string]string{
|
|||||||
type StorageBarInfo struct {
|
type StorageBarInfo struct {
|
||||||
Label string // e.g., "USB HDD 1TB", "SYS Storage 350G"
|
Label string // e.g., "USB HDD 1TB", "SYS Storage 350G"
|
||||||
Path string // e.g., "/mnt/hdd_1"
|
Path string // e.g., "/mnt/hdd_1"
|
||||||
|
Purpose string // Hungarian explanation of what this drive holds (monitoring page)
|
||||||
TotalGB float64
|
TotalGB float64
|
||||||
UsedGB float64
|
UsedGB float64
|
||||||
Percent float64
|
Percent float64
|
||||||
Disconnected bool
|
Disconnected bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// buildStorageBars returns usage bars for all registered storage paths.
|
// storageBarPurpose is the Hungarian description for the registered user-data drives shown in the
|
||||||
|
// monitoring "Tárolók kapacitása" list. These are all external/user-data drives (the agent's
|
||||||
|
// system/PBS storage is not in the controller's storage-path registry), matching the user-data
|
||||||
|
// purpose text on the storage-management page (Phase 4C).
|
||||||
|
const storageBarPurpose = "Külső adattároló — a telepített alkalmazások nagy méretű fájljai (média, dokumentumok) ide kerülnek; az adatbázisok a belső SSD-n vannak."
|
||||||
|
|
||||||
|
// buildStorageBars returns usage bars for all registered storage paths, in a stable order
|
||||||
|
// (by path) with a purpose description.
|
||||||
func (s *Server) buildStorageBars() []StorageBarInfo {
|
func (s *Server) buildStorageBars() []StorageBarInfo {
|
||||||
var bars []StorageBarInfo
|
var bars []StorageBarInfo
|
||||||
for _, sp := range s.settings.GetStoragePaths() {
|
for _, sp := range s.settings.GetStoragePaths() {
|
||||||
@@ -49,6 +58,7 @@ func (s *Server) buildStorageBars() []StorageBarInfo {
|
|||||||
bars = append(bars, StorageBarInfo{
|
bars = append(bars, StorageBarInfo{
|
||||||
Label: sp.Label,
|
Label: sp.Label,
|
||||||
Path: sp.Path,
|
Path: sp.Path,
|
||||||
|
Purpose: storageBarPurpose,
|
||||||
Disconnected: true,
|
Disconnected: true,
|
||||||
})
|
})
|
||||||
continue
|
continue
|
||||||
@@ -60,11 +70,14 @@ func (s *Server) buildStorageBars() []StorageBarInfo {
|
|||||||
bars = append(bars, StorageBarInfo{
|
bars = append(bars, StorageBarInfo{
|
||||||
Label: sp.Label,
|
Label: sp.Label,
|
||||||
Path: sp.Path,
|
Path: sp.Path,
|
||||||
|
Purpose: storageBarPurpose,
|
||||||
TotalGB: di.TotalGB,
|
TotalGB: di.TotalGB,
|
||||||
UsedGB: di.UsedGB,
|
UsedGB: di.UsedGB,
|
||||||
Percent: di.UsedPercent,
|
Percent: di.UsedPercent,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// Deterministic order regardless of registry insertion order.
|
||||||
|
sort.Slice(bars, func(i, j int) bool { return bars[i].Path < bars[j].Path })
|
||||||
return bars
|
return bars
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1369,11 +1382,18 @@ func (s *Server) syncFileBrowserMounts(resetDBOnChange bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build volume mount lines
|
// Build volume mount lines. SCOPE to the drive's `appdata/` subtree only (Phase 4A): the customer
|
||||||
|
// browses their userdata, but the recovery units + Tier 2 copies under `backups/` are NOT mounted
|
||||||
|
// into FileBrowser at all — so the thing that restores them can't be browsed or (even read-only)
|
||||||
|
// surfaced. mkdir the appdata dir first so the bind source exists with sane ownership.
|
||||||
var storageMounts []string
|
var storageMounts []string
|
||||||
for _, sp := range paths {
|
for _, sp := range paths {
|
||||||
mountName := filepath.Base(sp.Path) // "/mnt/hdd_1" → "hdd_1"
|
mountName := filepath.Base(sp.Path) // "/mnt/hdd_1" → "hdd_1"
|
||||||
line := fmt.Sprintf(" - %s:/srv/%s", sp.Path, mountName)
|
appdataSrc := filepath.Join(sp.Path, "appdata")
|
||||||
|
if err := os.MkdirAll(appdataSrc, 0755); err != nil {
|
||||||
|
s.logger.Printf("[WARN] [web] FileBrowser: could not ensure appdata dir %s: %v", appdataSrc, err)
|
||||||
|
}
|
||||||
|
line := fmt.Sprintf(" - %s:/srv/%s", appdataSrc, mountName)
|
||||||
storageMounts = append(storageMounts, line)
|
storageMounts = append(storageMounts, line)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -538,6 +538,10 @@
|
|||||||
<div id="storage-space-warn" class="form-hint" style="color:var(--yellow);display:none">
|
<div id="storage-space-warn" class="form-hint" style="color:var(--yellow);display:none">
|
||||||
⚠️ A kiválasztott tárhely majdnem megtelt.
|
⚠️ A kiválasztott tárhely majdnem megtelt.
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-hint" style="margin-top:.4rem;opacity:.8">
|
||||||
|
ℹ️ A kiválasztott meghajtón az alkalmazás <strong>fájljai</strong> (média, dokumentumok) tárolódnak.
|
||||||
|
Az <strong>adatbázis a gyors belső SSD-n</strong> fut — és az alkalmazással együtt készül róla biztonsági mentés.
|
||||||
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<input type="text" id="field-{{.EnvVar}}" name="{{.EnvVar}}"
|
<input type="text" id="field-{{.EnvVar}}" name="{{.EnvVar}}"
|
||||||
class="form-control" value="{{.Default}}"
|
class="form-control" value="{{.Default}}"
|
||||||
|
|||||||
@@ -106,6 +106,7 @@
|
|||||||
<div class="system-bar">
|
<div class="system-bar">
|
||||||
<div class="system-bar-fill {{usageColor .Percent | printf "system-bar-%s"}}" style="width:{{printf "%.1f" .Percent}}%"></div>
|
<div class="system-bar-fill {{usageColor .Percent | printf "system-bar-%s"}}" style="width:{{printf "%.1f" .Percent}}%"></div>
|
||||||
</div>
|
</div>
|
||||||
|
{{if .Purpose}}<div class="storage-purpose" style="font-size:.72rem;opacity:.65;margin-top:.2rem">{{.Purpose}}</div>{{end}}
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
Reference in New Issue
Block a user