e66d4669d6
gates / gates (push) Failing after 47s
An app was six pages — details, install/settings, logs, export, the per-app backup panel and a
details card for moving data — and every "Vissza" on them went to the app LIST, so there was no way
back to the app you were looking at. It is now ONE page with four tab URLs under /apps/{slug}/,
one shared header and one set of banners.
settings Mukodes (Start/Restart/Stop, Stop confirms) + the cards that want something from the
household + the install-time and optional settings. The default for an installed app,
RENDERED at the bare URL and never redirected to.
data the data folders, move to another drive, the nightly copy, the 2nd-copy setting, export
guide tagline, screenshots, use cases, first steps, the catalog's guide, the FAQ, the docs
link — and the WHOLE page for an app that is not installed
logs the log, and the only tab whose poller runs
The tabs are links, not panels: a link in an alert has to land ON a tab, and the log poller must not
run on the other three. Start/Restart/Stop left the page header, where they read as menu buttons.
The old /stacks/{name}/{deploy,logs,export,backup} GETs answer 302 — never 301, which a browser
caches beyond anyone's reach — to the tab that now holds them, query string carried through, with
name->slug resolved through the stack list rather than assumed equal. Every POST on those paths is
untouched: the backup form, the app-email toggle and the reveal endpoints keep their URLs.
A value asked AFTER the app starts is now generic (D2). The catalog marks a field `after_start: true`
and supplies its label; the ACTION is Go, in a registry keyed by slug + env var. The catalog can
never declare what runs — a declared field with no registered action renders nothing, POSTs 404 and
logs one WARN with the env var KEY, never the value. A red-proof found the second half of that guard
unpinned: deleting the "the catalog must have DECLARED this field" check left the suite green, and a
POST for an undeclared field then reached Plex and opened the setup gate.
TestAfterStart_RegistryAloneIsNotAnInvitation pins it.
New catalog block app_info.guide (sections + faq + checked_version), parsed, localised field by
field and rendered with the household's own address filled in. It renders only when checked_version
names the image tag its author clicked through. No template carries one yet.
The Pi and "csak x86" badges are gone; PiCompatible and the catalog key stay this release, or a box
on 0.314.0 would read "csak x86" on every app (R-1067).
template_id_gate.py now follows {{template}} includes, with a scope decoy proving the walk did not
become vacuous.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117kRTSgs3rJWUFGxyoWN6Y
404 lines
23 KiB
Go
404 lines
23 KiB
Go
package web
|
|
|
|
import (
|
|
"time"
|
|
|
|
"gitea.dooplex.hu/admin/felhom-controller/internal/settings"
|
|
"gitea.dooplex.hu/admin/felhom-controller/internal/stacks"
|
|
)
|
|
|
|
// ── i18n slice 1, release A (v0.248.0) — parity cases for apps and settings ─────────────────────────
|
|
//
|
|
// Every case here was captured from the UNCONVERTED template (see REPORT.md for the commit). Data
|
|
// carries no one-word Hungarian values (the English page test would read them as template copy) and
|
|
// no real customer value.
|
|
|
|
func i18nStack(name, display string, st stacks.ContainerState, deployed bool) stacks.Stack {
|
|
s := stacks.Stack{Name: name, State: st, Deployed: deployed}
|
|
s.Meta = stacks.Metadata{DisplayName: display, Slug: name, Description: "Egy teszt alkalmazás leírása",
|
|
Resources: stacks.ResourceHints{MemRequest: "128M", PiCompatible: true, NeedsHDD: true, HungarianUI: true}}
|
|
return s
|
|
}
|
|
|
|
func i18nCasesA() []i18nCase {
|
|
at := i18nFixtureTime()
|
|
cases := []i18nCase{
|
|
{"dashboard_full", "dashboard", func() map[string]interface{} {
|
|
d := i18nLayoutData("dashboard", "Vezérlőpult")
|
|
d["RunningCount"], d["StoppedCount"], d["TotalCount"] = 3, 1, 5
|
|
d["SystemInfo"] = map[string]interface{}{"TotalMemMB": uint64(8192), "UsedMemMB": uint64(4096), "MemPercent": 50.0,
|
|
"CPUPercent": 12.0, "LoadAvg1": 0.5, "LoadAvg5": 0.4, "LoadAvg15": 0.3, "TemperatureCelsius": 48.0,
|
|
"DiskKnown": true, "DiskUsedGB": 80.0, "DiskTotalGB": 100.0, "DiskPercent": 80.0}
|
|
d["StorageBars"] = []map[string]interface{}{
|
|
{"Label": "Adat lemez", "Disconnected": false, "Percent": 95.0, "UsedGB": 950.0, "TotalGB": 1000.0},
|
|
{"Label": "Régi lemez", "Disconnected": true},
|
|
}
|
|
d["SettingsWarning"] = "A beállítások helyreállítva."
|
|
d["DiskWarnings"] = []Alert{{Level: "warning", Message: "A lemez majdnem megtelt.", Link: "/storage", LinkText: "Tárhely oldal"}}
|
|
d["DiskHealthRows"] = []DiskHealthRow{{Label: "sda lemez", ChipLabel: "Rendben van", ChipClass: "state-text-run", Temp: "34"}}
|
|
d["BackupEnabled"] = true
|
|
d["BackupStatus"] = map[string]interface{}{"Success": true, "LastRun": at}
|
|
d["DBDumpStatus"] = map[string]interface{}{"Results": []int{1, 2}}
|
|
fb := i18nStack("filebrowser", "Fájlkezelő app", stacks.StateRunning, true)
|
|
fb.Protected = true
|
|
orphan := i18nStack("oldapp", "Régi alkalmazás", stacks.StateStopped, true)
|
|
orphan.Orphaned = true
|
|
d["Stacks"] = []stacks.Stack{
|
|
fb,
|
|
i18nStack("privatebin", "Private Bin", stacks.StateUnhealthy, true),
|
|
i18nStack("kimai", "Kimai Time", stacks.StateStopped, true),
|
|
orphan,
|
|
i18nStack("gokapi", "Go Kapi", stacks.StateNotDeployed, false),
|
|
}
|
|
d["MissingStorage"] = map[string]string{"privatebin": "HDD egy"}
|
|
d["OOMKilled"] = map[string]bool{"privatebin": true}
|
|
d["NetworkStubs"] = map[string]string{"privatebin": "NAS egy"}
|
|
d["NetworkWarnings"] = map[string]string{"privatebin": "NAS egy"}
|
|
d["Subdomains"] = map[string]string{"filebrowser": "files", "privatebin": "paste"}
|
|
return d
|
|
}},
|
|
{"dashboard_sparse", "dashboard", func() map[string]interface{} {
|
|
d := i18nLayoutData("dashboard", "Vezérlőpult")
|
|
d["RunningCount"], d["StoppedCount"], d["TotalCount"] = 0, 0, 0
|
|
d["SystemInfo"] = map[string]interface{}{"TotalMemMB": uint64(8192), "UsedMemMB": uint64(1024), "MemPercent": 12.0,
|
|
"CPUPercent": 1.0, "LoadAvg1": 0.1, "LoadAvg5": 0.1, "LoadAvg15": 0.1, "TemperatureCelsius": 0.0, "DiskKnown": false}
|
|
d["StorageBars"] = []map[string]interface{}{{"Label": "Adat lemez", "Disconnected": false, "Percent": 80.0, "UsedGB": 800.0, "TotalGB": 1000.0}}
|
|
d["BackupEnabled"] = true
|
|
d["BackupStatus"] = map[string]interface{}{"Success": false, "LastRun": at}
|
|
d["Stacks"] = []stacks.Stack{}
|
|
return d
|
|
}},
|
|
// R-522: a disconnected tunnel (cloudflared unhealthy) carries the „Kívülről nem érhető el" tag. Born as a
|
|
// key — its fixture was captured from this render, never from an unconverted template.
|
|
{"dashboard_tunnel_offline", "dashboard", func() map[string]interface{} {
|
|
d := i18nLayoutData("dashboard", "Vezérlőpult")
|
|
d["RunningCount"], d["StoppedCount"], d["TotalCount"] = 0, 0, 1
|
|
d["SystemInfo"] = map[string]interface{}{"TotalMemMB": uint64(0)}
|
|
d["BackupEnabled"] = false
|
|
cf := i18nStack("cloudflared", "Cloudflared", stacks.StateUnhealthy, true)
|
|
cf.Protected = true
|
|
d["Stacks"] = []stacks.Stack{cf}
|
|
d["MissingStorage"] = map[string]string{}
|
|
d["NetworkStubs"] = map[string]string{}
|
|
d["NetworkWarnings"] = map[string]string{}
|
|
d["Subdomains"] = map[string]string{}
|
|
return d
|
|
}},
|
|
{"dashboard_diskcrit", "dashboard", func() map[string]interface{} {
|
|
d := i18nLayoutData("dashboard", "Vezérlőpult")
|
|
d["RunningCount"], d["StoppedCount"], d["TotalCount"] = 1, 0, 1
|
|
d["SystemInfo"] = map[string]interface{}{"TotalMemMB": uint64(8192), "UsedMemMB": uint64(8000), "MemPercent": 97.0,
|
|
"CPUPercent": 99.0, "LoadAvg1": 4.0, "LoadAvg5": 3.0, "LoadAvg15": 2.0, "TemperatureCelsius": 80.0,
|
|
"DiskKnown": true, "DiskUsedGB": 97.0, "DiskTotalGB": 100.0, "DiskPercent": 97.0}
|
|
d["BackupEnabled"] = false
|
|
d["Stacks"] = []stacks.Stack{}
|
|
return d
|
|
}},
|
|
{"dashboard_nobackup", "dashboard", func() map[string]interface{} {
|
|
d := i18nLayoutData("dashboard", "Vezérlőpult")
|
|
d["RunningCount"], d["StoppedCount"], d["TotalCount"] = 1, 0, 1
|
|
d["SystemInfo"] = map[string]interface{}{"TotalMemMB": uint64(0)}
|
|
d["BackupEnabled"] = true
|
|
d["Stacks"] = []stacks.Stack{}
|
|
return d
|
|
}},
|
|
{"stacks_full", "stacks", func() map[string]interface{} {
|
|
d := i18nLayoutData("stacks", "Alkalmazások")
|
|
fb := i18nStack("filebrowser", "Fájlkezelő app", stacks.StateRunning, true)
|
|
fb.Protected = true
|
|
held := i18nStack("kimai", "Kimai Time", stacks.StateStopped, true)
|
|
held.HoldReason = "A frissítés megállt, a mentés hiányzik."
|
|
upd := i18nStack("romm", "Rom Manager", stacks.StateRunning, true)
|
|
upd.Updating, upd.UpdatePhase, upd.UpdatePhaseLabel = true, "pull", "Letöltés folyamatban…"
|
|
errRun := i18nStack("gokapi", "Go Kapi", stacks.StateRunning, true)
|
|
errRun.UpdateError = "A frissítés nem sikerült ma."
|
|
errRun.Containers = []stacks.ContainerInfo{{Name: "gokapi-app", State: stacks.StateRunning, Status: "Up 2 hours"}}
|
|
errStop := i18nStack("bookstack", "Book Stack", stacks.StateStopped, true)
|
|
errStop.UpdateError = "A frissítés nem sikerült tegnap."
|
|
orphan := i18nStack("oldapp", "Régi alkalmazás", stacks.StateStopped, true)
|
|
orphan.Orphaned = true
|
|
d["Stacks"] = []stacks.Stack{fb, held, upd, errRun, errStop, orphan,
|
|
i18nStack("privatebin", "Private Bin", stacks.StateUnhealthy, true),
|
|
i18nStack("immich", "Immich Photos", stacks.StateNotDeployed, false)}
|
|
d["Subdomains"] = map[string]string{"filebrowser": "files", "privatebin": "paste"}
|
|
d["MissingStorage"] = map[string]string{"privatebin": "HDD egy"}
|
|
d["NetworkStubs"] = map[string]string{"privatebin": "NAS egy"}
|
|
d["NetworkWarnings"] = map[string]string{"privatebin": "NAS egy"}
|
|
d["StorageLabels"] = map[string]string{"privatebin": "HDD egy"}
|
|
return d
|
|
}},
|
|
{"monitoring_hub_ok", "monitoring", func() map[string]interface{} {
|
|
d := i18nLayoutData("monitoring", "Rendszermonitor")
|
|
d["SystemInfo"] = map[string]interface{}{"TotalMemMB": uint64(8192), "DiskUsedGB": 40.0, "DiskTotalGB": 100.0, "DiskPercent": 40.0}
|
|
d["StorageBars"] = []map[string]interface{}{
|
|
{"Label": "Adat lemez", "Disconnected": false, "Percent": 50.0, "UsedGB": 500.0, "TotalGB": 1000.0, "Purpose": "Alkalmazás adatok helye"},
|
|
{"Label": "Régi lemez", "Disconnected": true},
|
|
}
|
|
d["DiskWarnings"] = []Alert{{Level: "warning", Message: "A lemez majdnem megtelt.", Link: "/storage", LinkText: "Tárhely oldal"}}
|
|
d["HubEnabled"], d["HubConnected"] = true, true
|
|
d["HubURL"], d["CustomerID"] = "https://hub.example", "test-customer"
|
|
d["HubLastSuccess"] = at
|
|
d["HubLastError"] = "connection refused"
|
|
d["MonitoringEnabled"] = true
|
|
return d
|
|
}},
|
|
{"monitoring_hub_down", "monitoring", func() map[string]interface{} {
|
|
d := i18nLayoutData("monitoring", "Rendszermonitor")
|
|
d["SystemInfo"] = map[string]interface{}{"TotalMemMB": uint64(8192), "DiskUsedGB": 40.0, "DiskTotalGB": 100.0, "DiskPercent": 40.0}
|
|
d["HubEnabled"], d["HubConnected"] = true, false
|
|
d["HubURL"], d["CustomerID"] = "https://hub.example", "test-customer"
|
|
d["HubLastSuccess"] = time.Time{}
|
|
return d
|
|
}},
|
|
{"monitoring_hub_off", "monitoring", func() map[string]interface{} {
|
|
d := i18nLayoutData("monitoring", "Rendszermonitor")
|
|
d["SystemInfo"] = map[string]interface{}{"TotalMemMB": uint64(8192), "DiskUsedGB": 40.0, "DiskTotalGB": 100.0, "DiskPercent": 40.0}
|
|
d["HubEnabled"] = false
|
|
return d
|
|
}},
|
|
{"app_import_empty", "app_import", func() map[string]interface{} {
|
|
return i18nLayoutData("stacks", "Alkalmazás importálása")
|
|
}},
|
|
{"app_import_bundles", "app_import", func() map[string]interface{} {
|
|
d := i18nLayoutData("stacks", "Alkalmazás importálása")
|
|
d["Bundles"] = []map[string]interface{}{
|
|
{"DisplayName": "Private Bin", "AppName": "privatebin", "ExportedAt": "2026-09-14 03:12", "SizeHuman": "12 MB",
|
|
"DriveLabel": "HDD egy", "DrivePath": "/mnt/hdd1", "Encrypted": true, "Path": "/mnt/hdd1/x.fab"},
|
|
{"DisplayName": "", "AppName": "kimai", "ExportedAt": "2026-09-13 03:12", "SizeHuman": "40 MB",
|
|
"DriveLabel": "", "DrivePath": "/mnt/hdd2", "Encrypted": false, "Path": "/mnt/hdd2/y.fab"},
|
|
}
|
|
return d
|
|
}},
|
|
{"app_data_export", "app_data", func() map[string]interface{} {
|
|
d := i18nLayoutData("stacks", "Private Bin exportálása")
|
|
st := i18nStack("privatebin", "Private Bin", stacks.StateRunning, true)
|
|
st.Meta.Subdomain = "paste"
|
|
d["Stack"], d["Meta"], d["AppInfo"] = st, st.Meta, st.Meta.AppInfo
|
|
d["ExportOffered"] = true
|
|
d["Drives"] = []map[string]interface{}{{"Path": "/mnt/hdd1", "Label": "HDD egy"}, {"Path": "/mnt/hdd2", "Label": ""}}
|
|
return d
|
|
}},
|
|
{"deploy_new", "deploy", func() map[string]interface{} {
|
|
return i18nDeployData(false, stacks.StateNotDeployed, 1)
|
|
}},
|
|
{"deploy_new_blocked", "deploy", func() map[string]interface{} {
|
|
return i18nDeployData(false, stacks.StateNotDeployed, 2)
|
|
}},
|
|
{"deploy_new_memory_blocked", "deploy", func() map[string]interface{} {
|
|
return i18nDeployData(false, stacks.StateNotDeployed, 3)
|
|
}},
|
|
{"app_settings_running", "app_settings", func() map[string]interface{} {
|
|
return i18nDeployData(true, stacks.StateRunning, 1)
|
|
}},
|
|
{"app_settings_stopped", "app_settings", func() map[string]interface{} {
|
|
return i18nDeployData(true, stacks.StateStopped, 2)
|
|
}},
|
|
// v0.275.0 (R-694): a login the restore generated — no value, the "use your old password" hint.
|
|
{"app_settings_restored_login", "app_settings", func() map[string]interface{} {
|
|
d := i18nDeployData(true, stacks.StateRunning, 1)
|
|
d["DeployedFieldValues"] = map[string]string{"SUBDOMAIN": "paste"}
|
|
d["RestoredLogins"] = map[string]bool{"ADMIN_PASSWORD": true}
|
|
return d
|
|
}},
|
|
{"settings_system_full", "settings_system", func() map[string]interface{} {
|
|
d := i18nLayoutData("settings", "Rendszer beállítások")
|
|
d["CustomerID"], d["CustomerDomain"] = "test-customer", "example.hu"
|
|
d["GitRepoURL"], d["GitSyncInterval"] = "https://git.example/catalog", "15m"
|
|
d["BackupEnabled"], d["DBDumpSchedule"], d["ResticSchedule"] = true, "02:30", "03:30"
|
|
d["MonitoringEnabled"], d["HealthchecksBase"], d["HubEnabled"] = true, "https://hc.example", true
|
|
d["SelfUpdateEnabled"], d["LatestVersion"], d["UpdateAvailable"] = true, "0.300.0", true
|
|
d["LastCheckTime"], d["RegistryAnonymous"], d["LastCheckError"] = "2026-09-14 03:12", true, "registry timeout"
|
|
d["AutoUpdateEnabled"], d["AutoUpdateTime"], d["ControllerFloor"] = true, "04:00", "0.246.0"
|
|
d["UpdateRunning"] = true
|
|
d["LastUpdateState"] = map[string]interface{}{"Status": "success", "PreviousVersion": "0.245.0", "TargetVersion": "0.246.0"}
|
|
d["NetLANAddress"], d["NetSMBName"], d["NetGateway"] = "192.168.1.10", "FELHOM", "192.168.1.1"
|
|
d["MemorySupported"], d["MemoryReachable"] = true, true
|
|
d["MemoryAllocatedMB"], d["MemoryUsageMB"], d["MemoryMinMB"], d["MemoryMaxMB"] = 4096, 2048, 2048, 8192
|
|
d["PowerOffSupported"] = true // R-998: the power-off card with its button
|
|
return d
|
|
}},
|
|
{"settings_system_failed", "settings_system", func() map[string]interface{} {
|
|
d := i18nLayoutData("settings", "Rendszer beállítások")
|
|
d["CustomerID"], d["CustomerDomain"] = "test-customer", "example.hu"
|
|
d["GitSyncInterval"] = "15m"
|
|
d["SelfUpdateEnabled"], d["LatestVersion"], d["UpdateAvailable"] = true, "0.247.0", false
|
|
d["RegistryAnonymous"] = false
|
|
d["LastUpdateState"] = map[string]interface{}{"Status": "failed", "Error": "pull failed"}
|
|
d["MemorySupported"], d["MemoryReachable"], d["MemoryProcMB"] = true, false, 4096
|
|
d["PowerOffSupported"] = true
|
|
return d
|
|
}},
|
|
{"settings_system_pending", "settings_system", func() map[string]interface{} {
|
|
d := i18nLayoutData("settings", "Rendszer beállítások")
|
|
d["CustomerID"], d["CustomerDomain"] = "test-customer", "example.hu"
|
|
d["SelfUpdateEnabled"] = true
|
|
d["LastUpdateState"] = map[string]interface{}{"Status": "pending"}
|
|
d["MemorySupported"], d["MemoryReachable"], d["MemoryProcMB"] = true, false, 0
|
|
d["PowerOffSupported"] = true
|
|
return d
|
|
}},
|
|
{"settings_system_nomemory", "settings_system", func() map[string]interface{} {
|
|
d := i18nLayoutData("settings", "Rendszer beállítások")
|
|
d["MemorySupported"] = false
|
|
d["PowerOffSupported"] = false // R-998: an old agent — the card's one-line note instead of the button
|
|
return d
|
|
}},
|
|
{"settings_security_full", "settings_security", func() map[string]interface{} {
|
|
d := i18nLayoutData("settings-security", "Biztonság és hozzáférés")
|
|
d["PasswordError"] = "A két jelszó nem egyezik."
|
|
d["CFConfigured"], d["GeoEnabled"] = true, true
|
|
d["DeployedApps"] = []map[string]string{{"Name": "privatebin", "Display": "Private Bin"}}
|
|
d["GeoLastSync"], d["GeoLastError"] = "2026-09-14 03:12", "api error"
|
|
d["GeoAllowedCountries"] = []string{"HU"}
|
|
d["GeoAppOverrides"] = map[string][]string{"privatebin": {"HU", "AT"}}
|
|
d["HasRetrievalPassword"] = true
|
|
d["CustomerID"], d["HubURL"] = "test-customer", "https://hub.example"
|
|
d["SupportEmail"], d["SupportURL"] = "support@example.hu", "https://example.hu/kapcsolat"
|
|
return d
|
|
}},
|
|
{"settings_security_noauth", "settings_security", func() map[string]interface{} {
|
|
d := i18nLayoutData("settings-security", "Biztonság és hozzáférés")
|
|
d["AuthEnabled"] = false
|
|
d["CFConfigured"], d["GeoEnabled"] = false, false
|
|
d["GeoAllowedCountries"] = []string{}
|
|
d["GeoAppOverrides"] = map[string][]string{}
|
|
return d
|
|
}},
|
|
{"settings_notifications_hub", "settings_notifications", func() map[string]interface{} {
|
|
d := i18nLayoutData("settings-notifications", "Értesítések")
|
|
d["HubEnabled"] = true
|
|
d["NotificationSuccess"], d["NotificationError"] = "A beállítás elmentve.", "A mentés nem sikerült."
|
|
d["NotificationPrefs"] = &settings.NotificationPrefs{Email: "anna@example.hu", EnabledEvents: []string{"backup_failed", "node_down"}, CooldownHours: 4}
|
|
d["AppEmailAvailable"], d["AppEmailEnabled"], d["AppEmailFromName"] = true, true, "Kovács család"
|
|
d["AppEmailSuccess"], d["AppEmailError"] = "Az e-mail beállítás mentve.", "Az e-mail beállítás hibás."
|
|
return d
|
|
}},
|
|
// v0.308.0 (R-922 warning, decision 196): the clear-address warning and its confirm button, captured when born.
|
|
{"settings_notifications_clear_confirm", "settings_notifications", func() map[string]interface{} {
|
|
d := i18nLayoutData("settings-notifications", "Értesítések")
|
|
d["HubEnabled"] = true
|
|
d["NotificationClearConfirm"] = true
|
|
d["NotificationPrefs"] = &settings.NotificationPrefs{Email: "", EnabledEvents: []string{}, CooldownHours: 6}
|
|
return d
|
|
}},
|
|
// v0.309.0 (R-388 slice 1, decision 200): every group „egyedi" (custom) — some of its detectors on,
|
|
// some off — so each group's custom badge and hint renders; captured when born.
|
|
{"settings_notifications_groups_custom", "settings_notifications", func() map[string]interface{} {
|
|
d := i18nLayoutData("settings-notifications", "Értesítések")
|
|
d["HubEnabled"] = true
|
|
d["NotificationPrefs"] = &settings.NotificationPrefs{Email: "anna@example.hu", EnabledEvents: []string{"disk_warning", "app_update_held", "backup_failed"}, CooldownHours: 6}
|
|
return d
|
|
}},
|
|
{"settings_notifications_nohub", "settings_notifications", func() map[string]interface{} {
|
|
d := i18nLayoutData("settings-notifications", "Értesítések")
|
|
d["HubEnabled"] = false
|
|
return d
|
|
}},
|
|
{"app_logs", "app_logs", func() map[string]interface{} {
|
|
d := i18nLayoutData("stacks", "Private Bin naplók")
|
|
st := i18nStack("privatebin", "Private Bin", stacks.StateRunning, true)
|
|
d["Stack"], d["Meta"], d["AppInfo"] = st, st.Meta, st.Meta.AppInfo
|
|
d["Logs"] = "2026-09-14 line one\nline two"
|
|
return d
|
|
}},
|
|
}
|
|
return cases
|
|
}
|
|
|
|
// i18nDeployData builds the deploy page. variant 1 and 2 split the branches that cannot co-exist.
|
|
func i18nDeployData(deployed bool, st stacks.ContainerState, variant int) map[string]interface{} {
|
|
title := "Private Bin telepítése"
|
|
d := i18nLayoutData("stacks", title)
|
|
stack := i18nStack("privatebin", "Private Bin", st, deployed)
|
|
stack.Meta.Subdomain = "paste"
|
|
stack.Meta.AppInfo = stacks.AppInfo{FirstSteps: []string{"Nyisd meg a címet"}, DefaultCreds: "nincs ilyen", DocsURL: "https://example.org/docs"}
|
|
d["Stack"] = stack
|
|
d["Meta"] = stack.Meta
|
|
// v0.315.0: the settings TAB renders these same keys inside the app page's shell, which shows
|
|
// the app's tagline and badges. The install page ignores AppInfo, so one data builder still
|
|
// serves both surfaces.
|
|
d["AppInfo"] = stack.Meta.AppInfo
|
|
d["AlreadyDeployed"] = deployed
|
|
d["LogoURL"], d["LogoPNGURL"], d["AppPageURL"] = "/logo.svg", "/logo.png", "/apps/privatebin"
|
|
d["EffectiveSubdomain"] = "paste"
|
|
d["SystemDataPath"] = "/mnt/sys_drive/data"
|
|
d["FlashSuccess"] = "A mentés sikeresen elkészült."
|
|
d["FlashError"] = "A művelet most nem sikerült."
|
|
d["DeployedFieldValues"] = map[string]string{"SUBDOMAIN": "paste", "ADMIN_PASSWORD": "rejtett érték"}
|
|
d["RestoreFieldValues"] = map[string]string{}
|
|
d["CurrentValues"] = map[string]string{"API_KEY": "abc123"}
|
|
d["AutoFieldValues"] = map[string]string{"SECRET_KEY": "generated-secret-value", "DB_NAME": "privatebin_db"}
|
|
d["AutoFields"] = []map[string]interface{}{
|
|
{"EnvVar": "SECRET_KEY", "Label": "Titkos kulcs érték", "Type": "secret"},
|
|
{"EnvVar": "DB_NAME", "Label": "Adatbázis neve itt", "Type": "text"},
|
|
{"EnvVar": "EMPTY_ONE", "Label": "Üres mező értéke", "Type": "text"},
|
|
}
|
|
storagePaths := []map[string]interface{}{
|
|
{"Path": "/mnt/hdd1", "Label": "HDD egy", "FreeHuman": "500 GB", "FreePercent": 50.0, "NotAllowed": false, "IsDefault": true},
|
|
{"Path": "/mnt/nas", "Label": "NAS egy", "FreeHuman": "900 GB", "FreePercent": 90.0, "NotAllowed": true, "NotAllowedNote": "hálózati tárhely nem használható", "IsDefault": false},
|
|
{"Path": "/mnt/hdd2", "Label": "HDD kettő", "FreeHuman": "100 GB", "FreePercent": 10.0, "NotAllowed": false, "IsDefault": false},
|
|
}
|
|
d["UserFields"] = []map[string]interface{}{
|
|
{"EnvVar": "SUBDOMAIN", "Label": "Webcím eleje", "Type": "subdomain", "Required": true, "Default": "paste", "Description": "A cím első része"},
|
|
{"EnvVar": "LANG_SEL", "Label": "Felület nyelve", "Type": "select", "Options": []map[string]string{{"Value": "hu", "Label": "Magyar nyelv"}}},
|
|
{"EnvVar": "ADMIN_PASSWORD", "Label": "Admin jelszó mező", "Type": "password", "Placeholder": "Legalább 12 karakter", "LockedAfterDeploy": true},
|
|
{"EnvVar": "FLAG", "Label": "Kapcsoló mező itt", "Type": "boolean", "Description": "Egy kapcsoló leírása"},
|
|
{"EnvVar": "HDD_PATH", "Label": "Adatok helye itt", "Type": "path", "Required": true, "Placeholder": "/mnt/valahol"},
|
|
{"EnvVar": "NOTE", "Label": "Megjegyzés mező", "Type": "text", "Placeholder": "Írj ide valamit"},
|
|
}
|
|
d["HasIntegrations"] = true
|
|
d["Integrations"] = []map[string]interface{}{
|
|
{"Label": "Integráció egy", "Description": "Első integráció leírása", "Target": "a", "TargetDeployed": false},
|
|
{"Label": "Integráció kettő", "Description": "Második integráció leírása", "Target": "b", "TargetDeployed": true, "TargetRunning": false},
|
|
{"Label": "Integráció három", "Description": "Harmadik integráció leírása", "Target": "c", "TargetDeployed": true, "TargetRunning": true, "Status": "error"},
|
|
{"Label": "Integráció négy", "Description": "Negyedik integráció leírása", "Target": "d", "TargetDeployed": true, "TargetRunning": true, "Status": "ok", "Enabled": true},
|
|
{"Label": "Integráció öt", "Description": "Ötödik integráció leírása", "Target": "e", "TargetDeployed": true, "TargetRunning": true, "Status": "ok", "Enabled": false},
|
|
}
|
|
d["GeoGlobalEnabled"] = true
|
|
d["GeoGlobalCountries"] = []string{"HU"}
|
|
d["GeoAppOverrideCountries"] = []string{"HU", "AT"}
|
|
d["HasOptionalConfig"] = true
|
|
d["OptionalConfig"] = []map[string]interface{}{
|
|
{"Group": "Külső szolgáltatások", "Description": "Opcionális kulcsok leírása", "Fields": []map[string]interface{}{
|
|
{"EnvVar": "API_KEY", "Label": "API kulcs mező", "Type": "secret_input", "HelpText": "Ide jön a kulcs", "HelpURL": "https://example.org/reg"},
|
|
{"EnvVar": "OTHER", "Label": "Másik mező itt", "Type": "text"},
|
|
}},
|
|
}
|
|
d["AppEmailSupported"] = true
|
|
d["AppEmailFromAddress"] = "noreply@example.hu"
|
|
if variant == 1 {
|
|
d["StoragePaths"] = storagePaths
|
|
d["RestoreHasRecord"] = true
|
|
d["RestoreRecordedAddress"] = "paste.example.hu"
|
|
d["RestoreRecordedDrive"] = "HDD egy"
|
|
d["RestorePrefillHDDPath"] = "/mnt/hdd1"
|
|
d["GeoAppOverride"] = true
|
|
d["AppEmailGlobalOn"] = false
|
|
d["AppEmailAppOn"] = true
|
|
d["MemoryInfo"] = map[string]interface{}{"Available": true, "Blocked": false, "OvercommitWarn": true, "AfterMB": 3000, "UsableMB": 4000,
|
|
"Percent": 75, "UsedPercent": 70, "UsedMB": 2800, "NewRequestMB": 128}
|
|
d["StorageInfo"] = map[string]interface{}{"Label": "HDD egy", "Path": "/mnt/hdd1", "DataSizeHuman": "2 GB", "FreeHuman": "500 GB", "FreePercent": 50.0}
|
|
d["OtherStoragePaths"] = []string{"/mnt/hdd2"}
|
|
d["StaleData"] = []map[string]interface{}{{"Label": "HDD kettő", "Path": "/mnt/hdd2", "SizeHuman": "1 GB", "Mounts": []string{"/data/uploads"}}}
|
|
d["CurrentHDDPath"] = "/mnt/hdd1"
|
|
} else if variant == 3 {
|
|
d["StoragePaths"] = storagePaths
|
|
d["MemoryInfo"] = map[string]interface{}{"Available": true, "Blocked": true, "AfterMB": 5000, "UsableMB": 4000,
|
|
"Percent": 100, "UsedPercent": 90, "UsedMB": 3600, "NewRequestMB": 128}
|
|
d["CurrentHDDPath"] = "/mnt/gone"
|
|
d["CurrentHDDPathMissing"] = true
|
|
} else {
|
|
d["RestoreRecordedDeclaresPath"] = true
|
|
d["DockerBelowReserve"] = true
|
|
d["DockerFreeHuman"], d["DockerReserveHuman"] = "1 GB", "5 GB"
|
|
d["AppEmailGlobalOn"] = true
|
|
d["MemoryInfo"] = map[string]interface{}{"Available": true, "Blocked": true, "AfterMB": 5000, "UsableMB": 4000,
|
|
"Percent": 100, "UsedPercent": 90, "UsedMB": 3600, "NewRequestMB": 128}
|
|
d["StorageInfo"] = map[string]interface{}{"Label": "HDD egy", "Path": "/mnt/hdd1"}
|
|
d["CurrentHDDPath"] = "/mnt/gone"
|
|
d["CurrentHDDPathMissing"] = true
|
|
d["StoragePaths"] = nil
|
|
}
|
|
return d
|
|
}
|