v0.280.0: the setup gate (decision 46); R-710 'I changed it' + absent-record window; R-709 password fields off the page; password:N:special generator
gates / gates (push) Successful in 25s
gates / gates (push) Successful in 25s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -46,10 +46,13 @@ type Server struct {
|
||||
updater *selfupdate.Updater
|
||||
logger *log.Logger
|
||||
version string
|
||||
encKey []byte // AES-256 key for decrypting app.yaml values
|
||||
tmpl *template.Template // the Hungarian set (i18n.Default) — every pre-i18n caller renders this
|
||||
tmplByLang map[string]*template.Template
|
||||
i18n *i18n.Bundle
|
||||
encKey []byte // AES-256 key for decrypting app.yaml values
|
||||
// gate / gateClock (v0.280.0, decision 46): the setup gate's key + used tokens; the clock is a test seam.
|
||||
gate gateState
|
||||
gateClock func() time.Time
|
||||
tmpl *template.Template // the Hungarian set (i18n.Default) — every pre-i18n caller renders this
|
||||
tmplByLang map[string]*template.Template
|
||||
i18n *i18n.Bundle
|
||||
|
||||
// versionPosition (v0.275.0) — where a just-restored app stands against the catalog; nil → the stack
|
||||
// manager's RestoredVersionPosition. A seam so the restore sentence is testable without a catalog.
|
||||
@@ -811,6 +814,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// R-254: the app's generated first-login password is fetched by an explicit authenticated act,
|
||||
// never templated into the info page. Placed BEFORE the /apps/ catch-all so the more specific
|
||||
// path wins. POST (not GET) so CsrfProtect covers it and it is not cacheable — see the handler.
|
||||
// v0.280.0: the household's two presses on the app page — "Done, I set it up" (decision 46) and "I changed
|
||||
// it" under a known default login (R-710). POST, so CsrfProtect covers them.
|
||||
case strings.HasPrefix(path, "/apps/") && strings.HasSuffix(path, "/setup-gate/open") && r.Method == http.MethodPost:
|
||||
s.appSetupGateOpenHandler(w, r, strings.TrimSuffix(strings.TrimPrefix(path, "/apps/"), "/setup-gate/open"))
|
||||
case strings.HasPrefix(path, "/apps/") && strings.HasSuffix(path, "/default-login/changed") && r.Method == http.MethodPost:
|
||||
s.appDefaultLoginChangedHandler(w, r, strings.TrimSuffix(strings.TrimPrefix(path, "/apps/"), "/default-login/changed"))
|
||||
case strings.HasPrefix(path, "/apps/") && strings.HasSuffix(path, "/initial-credentials/reveal") && r.Method == http.MethodPost:
|
||||
slug := strings.TrimSuffix(strings.TrimPrefix(path, "/apps/"), "/initial-credentials/reveal")
|
||||
s.appInitialCredsRevealHandler(w, r, slug)
|
||||
@@ -839,6 +848,16 @@ func (s *Server) CatchAllMiddleware(next http.Handler) http.Handler {
|
||||
if idx := strings.LastIndex(host, ":"); idx != -1 {
|
||||
host = host[:idx]
|
||||
}
|
||||
// v0.280.0 (decision 46): traefik's forwardAuth call for a gated app arrives with the controller's own
|
||||
// container name as Host, so it is answered before the host check. The start page is the dashboard's.
|
||||
if r.URL.Path == gateAuthPath {
|
||||
s.ServeGateAuth(w, r)
|
||||
return
|
||||
}
|
||||
if r.URL.Path == gateStartPath && strings.EqualFold(host, controllerHost) && r.Method == http.MethodGet {
|
||||
s.ServeGateStart(w, r)
|
||||
return
|
||||
}
|
||||
// Pass through: controller host, localhost (healthcheck/internal), or empty
|
||||
if strings.EqualFold(host, controllerHost) || host == "" ||
|
||||
host == "localhost" || host == "127.0.0.1" {
|
||||
|
||||
Reference in New Issue
Block a user