v0.320.0: FileBrowser is part of the box again; every app says where its login is; a „Fájlok" button
gates / gates (push) Successful in 50s
gates / gates (push) Successful in 50s
FileBrowser (and traefik, cloudflared, samba) has no app.yaml, so Deployed is false. Since v0.315.0 the app page read Deployed alone and showed FileBrowser "not installed", with Install and without its login. A protected infra stack is now installed by definition on the app page (Deployed || Protected, as every other surface already counted): settings/guide/log tabs, Restart only, no install page, no data tab. The Install press would have deployed the infra stack as an app: DeployStack and the deploy API now refuse a protected stack. In the operator state (the box does not know FileBrowser's password - both demo boxes) the login panel offers "Új jelszó beállítása": the file browser's own CLI with the server stopped (spiked on demo-hp: new 200, old 401, users/shares/settings unchanged), verified, stored encrypted, state generated. Every installed app's settings tab has the login panel with exactly one answer: the box's fields, the login the app wrote (InitialCreds), or a sentence chosen by the catalog's new app_info.login_kind (first_visit default, email, none; none behind the family gate says the family sign-in is the login). "Fájlok ↗" opens the library folder, else the first declared folder, else a media app's first present drive's media folder. The data tab lists each drive's media folders; a folder whose drive is not bound into FileBrowser is text, never a link. Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -55,23 +55,42 @@ func (s *Server) appPageHandler(w http.ResponseWriter, r *http.Request, slug, ta
|
||||
return
|
||||
}
|
||||
|
||||
if !found.Deployed {
|
||||
// v0.320.0: a protected infra stack is installed by definition — it is part of the box (appCommonData sets
|
||||
// Infra). It gets the settings, guide and log tabs; never the data tab (its export, move and backup are not the
|
||||
// household's), and never the install wizard. Pinned by TestAppPage_InfraAppHasNoInstall.
|
||||
infra, _ := data["Infra"].(bool)
|
||||
installed := found.Deployed || infra
|
||||
if !installed {
|
||||
if tab != "" {
|
||||
http.Redirect(w, r, s.cfg.AppPageURL(slug), http.StatusFound)
|
||||
return
|
||||
}
|
||||
tab = appTabGuide
|
||||
}
|
||||
if infra && tab == appTabData {
|
||||
http.Redirect(w, r, s.cfg.AppPageURL(slug), http.StatusFound)
|
||||
return
|
||||
}
|
||||
if tab == "" {
|
||||
tab = appTabSettings
|
||||
}
|
||||
|
||||
data["ActiveTab"] = tab
|
||||
data["AppTabs"] = s.appTabLinks(r, slug, found.Deployed, tab)
|
||||
data["Deployed"] = found.Deployed
|
||||
tabs := s.appTabLinks(r, slug, installed, tab)
|
||||
if infra {
|
||||
kept := tabs[:0]
|
||||
for _, t := range tabs {
|
||||
if t.Key != appTabData {
|
||||
kept = append(kept, t)
|
||||
}
|
||||
}
|
||||
tabs = kept
|
||||
}
|
||||
data["AppTabs"] = tabs
|
||||
data["Deployed"] = installed
|
||||
// The shell's „Megnyitas" shows only for an app that is actually up — a link to a stopped app's
|
||||
// address lands on the catch-all page, which is a worse answer than no link.
|
||||
data["AppOperational"] = found.Deployed && isOperationalState(found.State)
|
||||
data["AppOperational"] = installed && isOperationalState(found.State)
|
||||
|
||||
switch tab {
|
||||
case appTabSettings:
|
||||
@@ -133,12 +152,18 @@ func (s *Server) renderAppSettingsTab(w http.ResponseWriter, r *http.Request, fo
|
||||
// The form half of this tab is deployPageData's — the same keys the install page gets, with
|
||||
// AlreadyDeployed true. Merged OVER the common data, except the four keys that say which page
|
||||
// this is: those belong to the tab, not to the form.
|
||||
if form, ok := s.deployPageData(w, r, found.Name); ok {
|
||||
mergeAppPageData(data, form)
|
||||
} else {
|
||||
return
|
||||
//
|
||||
// v0.320.0: an infra stack has no install form, no app.yaml and no catalog fields — its tab is „Mukodes"
|
||||
// (restart only) and, for FileBrowser, the login. deployPageData is not asked.
|
||||
if infra, _ := data["Infra"].(bool); !infra {
|
||||
if form, ok := s.deployPageData(w, r, found.Name); ok {
|
||||
mergeAppPageData(data, form)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
data["AfterStartFields"] = s.afterStartFields(found)
|
||||
}
|
||||
data["AfterStartFields"] = s.afterStartFields(found)
|
||||
data["LoginAnswer"] = loginAnswerFor(data, &found.Meta)
|
||||
data["Title"] = found.Meta.DisplayName
|
||||
data["TitleKey"], data["TitleArgs"] = "page.title.app_settings", []interface{}{found.Meta.DisplayName}
|
||||
s.executeTemplate(w, r, "app_settings", data)
|
||||
@@ -214,6 +239,12 @@ func (s *Server) appInstallPageHandler(w http.ResponseWriter, r *http.Request, s
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
// v0.320.0: part of the box, never installed — the address answers with the app's page, like a tab of a
|
||||
// not-installed app does. The deploy POST refuses it too (api deployStack, stacks.DeployStack).
|
||||
if s.cfg.IsProtectedStack(name) {
|
||||
http.Redirect(w, r, s.cfg.AppPageURL(slug), http.StatusFound)
|
||||
return
|
||||
}
|
||||
s.deployHandler(w, r, name)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user