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:
@@ -768,7 +768,24 @@ func (s *Server) appCommonData(w http.ResponseWriter, r *http.Request, slug stri
|
||||
// without a second decision to get wrong.
|
||||
found = stacks.LocalizeStackPtr(found, s.langFor(r))
|
||||
|
||||
// v0.320.0: a protected infra stack (FileBrowser, traefik, cloudflared, samba) is PART OF THE BOX. It has no
|
||||
// app.yaml, so Deployed is false; every other surface already counts `Deployed || Protected` as on the box, and
|
||||
// the app page now does too (appPageHandler). Its name and address are the curated ones (inframeta.go).
|
||||
infraApp := found.Protected || s.cfg.IsProtectedStack(found.Name)
|
||||
var infraInfo *InfraMeta
|
||||
if infraApp {
|
||||
infraInfo = infraMetaIn(s.bundle(), s.langFor(r), found.Name)
|
||||
if infraInfo != nil {
|
||||
found.Meta.DisplayName = infraInfo.DisplayName
|
||||
}
|
||||
if effectiveSubdomain == "" {
|
||||
effectiveSubdomain = protectedStackSubdomains[found.Name]
|
||||
}
|
||||
}
|
||||
|
||||
data := s.baseData("stacks", found.Meta.DisplayName)
|
||||
data["Infra"] = infraApp
|
||||
data["InfraInfo"] = infraInfo
|
||||
data["Stack"] = found
|
||||
data["Meta"] = found.Meta
|
||||
// R-498: the catalog copy says `wiki.DOMAIN`; the household needs its own address.
|
||||
@@ -816,6 +833,10 @@ func (s *Server) appCommonData(w http.ResponseWriter, r *http.Request, slug stri
|
||||
// app_export.html bug (a CSRF token rendered where the domain belonged) came from assuming it did.
|
||||
if cards := s.buildDataPathCards(found, s.langFor(r)); len(cards) > 0 {
|
||||
data["DataPathCards"] = cards
|
||||
// v0.320.0: the header's „Fajlok" button — one folder of the app, opened in FileBrowser.
|
||||
if link := s.filesButtonLink(found, cards); link != "" {
|
||||
data["FilesLink"] = link
|
||||
}
|
||||
}
|
||||
|
||||
// Initial auto-generated login (e.g. Crafty writes a random admin password to a file at first
|
||||
@@ -2700,6 +2721,39 @@ func (s *Server) appInitialCredsRevealHandler(w http.ResponseWriter, r *http.Req
|
||||
escrowJSON(w, http.StatusOK, map[string]any{"password": creds.Password}, "")
|
||||
}
|
||||
|
||||
// appFileBrowserResetHandler — POST /apps/filebrowser/initial-credentials/reset (v0.320.0). The household's
|
||||
// „Új jelszó beállítása" in the `operator` state: the box does not know the file manager's password, so it sets a
|
||||
// new one (stacks.ResetFileBrowserAdminPassword — stop, the file browser's own CLI, start, verify, record).
|
||||
// POST + CSRF (router), no-store, logged as an act; the value is never in the response — the page reloads and
|
||||
// offers the existing reveal. Only for FileBrowser; any other slug is 404.
|
||||
func (s *Server) appFileBrowserResetHandler(w http.ResponseWriter, r *http.Request, slug string) {
|
||||
w.Header().Set("Cache-Control", "no-store")
|
||||
if s.stackMgr == nil {
|
||||
escrowJSON(w, http.StatusServiceUnavailable, nil, s.msg(r, "escrow.stack_mgr_unavailable"))
|
||||
return
|
||||
}
|
||||
if s.stackNameForSlug(slug) != fileBrowserStack {
|
||||
escrowJSON(w, http.StatusNotFound, nil, s.msg(r, "escrow.unknown_app"))
|
||||
return
|
||||
}
|
||||
// The mount sync must not recreate the file browser while it is stopped for its CLI.
|
||||
s.fileBrowserMu.Lock()
|
||||
defer s.fileBrowserMu.Unlock()
|
||||
s.logger.Printf("[INFO] [web] filebrowser: new admin password requested from %s", clientIP(r))
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 4*time.Minute)
|
||||
defer cancel()
|
||||
if err := s.stackMgr.ResetFileBrowserAdminPassword(ctx); err != nil {
|
||||
if errors.Is(err, stacks.ErrFileBrowserNotOperator) {
|
||||
escrowJSON(w, http.StatusConflict, nil, s.msg(r, "filebrowser.reset_not_operator"))
|
||||
return
|
||||
}
|
||||
s.logger.Printf("[WARN] [web] filebrowser: new admin password FAILED: %v", err)
|
||||
escrowJSON(w, http.StatusBadGateway, nil, s.msg(r, "filebrowser.reset_failed"))
|
||||
return
|
||||
}
|
||||
escrowJSON(w, http.StatusOK, nil, "")
|
||||
}
|
||||
|
||||
// fileBrowserStack is the protected infra stack whose admin password R-513 manages.
|
||||
const fileBrowserStack = "filebrowser"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user