package webui import ( "encoding/base64" "encoding/json" "net/http" ) // Flash mirrors one Flask flash() message: (message, category). type Flash struct { Category string `json:"c"` Message string `json:"m"` } const flashCookieName = "flash" // setFlash mirrors flask.flash(): appends one message to the flash cookie so it // survives the redirect that (almost) always follows a form POST in this app. // No signing: these are cosmetic toast notifications, not a trust boundary. func setFlash(w http.ResponseWriter, category, message string) { flashes := []Flash{{Category: category, Message: message}} encoded, _ := json.Marshal(flashes) http.SetCookie(w, &http.Cookie{ Name: flashCookieName, Value: base64.URLEncoding.EncodeToString(encoded), Path: "/", HttpOnly: true, SameSite: http.SameSiteLaxMode, }) } // popFlashes mirrors get_flashed_messages(with_categories=true): reads and clears the // flash cookie so each message is shown exactly once, on the very next render. func popFlashes(w http.ResponseWriter, r *http.Request) []Flash { c, err := r.Cookie(flashCookieName) if err != nil || c.Value == "" { return nil } http.SetCookie(w, &http.Cookie{Name: flashCookieName, Value: "", Path: "/", MaxAge: -1}) raw, err := base64.URLEncoding.DecodeString(c.Value) if err != nil { return nil } var flashes []Flash if err := json.Unmarshal(raw, &flashes); err != nil { return nil } return flashes } // AppPasswordReveal carries a freshly-generated app password secret across the // create->redirect hop, kept out of the toast-driven Flash system so it can render // as its own centered modal (with a copy button) instead of a toast that's easy to // miss and can't be copied without retyping. type AppPasswordReveal struct { Label string `json:"l"` Secret string `json:"s"` } const appPasswordRevealCookieName = "app_pw_reveal" // setAppPasswordReveal mirrors setFlash but for the one-time secret reveal. func setAppPasswordReveal(w http.ResponseWriter, label, secret string) { encoded, _ := json.Marshal(AppPasswordReveal{Label: label, Secret: secret}) http.SetCookie(w, &http.Cookie{ Name: appPasswordRevealCookieName, Value: base64.URLEncoding.EncodeToString(encoded), Path: "/", HttpOnly: true, SameSite: http.SameSiteLaxMode, }) } // popAppPasswordReveal mirrors popFlashes but for the one-time secret reveal. func popAppPasswordReveal(w http.ResponseWriter, r *http.Request) *AppPasswordReveal { c, err := r.Cookie(appPasswordRevealCookieName) if err != nil || c.Value == "" { return nil } http.SetCookie(w, &http.Cookie{Name: appPasswordRevealCookieName, Value: "", Path: "/", MaxAge: -1}) raw, err := base64.URLEncoding.DecodeString(c.Value) if err != nil { return nil } var reveal AppPasswordReveal if err := json.Unmarshal(raw, &reveal); err != nil { return nil } return &reveal }