Settings page: themes (Dark, Light, Cyberpunk, Retro) with xterm palettes and hub-synced choice, change password, access tokens, about
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
254e0cdd58
commit
a4f39859b6
@@ -143,6 +143,9 @@ func New(cfg *config.Config, sessions *auth.Sessions, mgr *term.Manager, col *me
|
||||
m.HandleFunc("POST /api/storage/zfs/snapshot", s.auth(s.zfsSnapshot))
|
||||
m.HandleFunc("GET /api/procs", s.auth(s.procList))
|
||||
m.HandleFunc("POST /api/procs/{pid}/signal", s.auth(s.procSignal))
|
||||
m.HandleFunc("POST /api/password", s.auth(s.changePassword))
|
||||
m.HandleFunc("GET /api/prefs/ui", s.auth(s.getUIPrefs))
|
||||
m.HandleFunc("PUT /api/prefs/ui", s.auth(s.putUIPrefs))
|
||||
m.HandleFunc("GET /api/prefs/dashboard", s.auth(s.getDashPrefs))
|
||||
m.HandleFunc("PUT /api/prefs/dashboard", s.auth(s.putDashPrefs))
|
||||
m.HandleFunc("/api/", func(w http.ResponseWriter, _ *http.Request) { jsonErr(w, 404, "not found") })
|
||||
@@ -1190,6 +1193,61 @@ func (s *Server) procSignal(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(204)
|
||||
}
|
||||
|
||||
// ---- account ----
|
||||
|
||||
func (s *Server) changePassword(w http.ResponseWriter, r *http.Request) {
|
||||
var body struct{ Current, Password string }
|
||||
if err := json.NewDecoder(io.LimitReader(r.Body, 4096)).Decode(&body); err != nil {
|
||||
jsonErr(w, 400, "bad request")
|
||||
return
|
||||
}
|
||||
if !auth.VerifyPassword(s.cfg.PasswordHash, body.Current) {
|
||||
jsonErr(w, 403, "current password is wrong")
|
||||
return
|
||||
}
|
||||
if len(body.Password) < 12 {
|
||||
jsonErr(w, 400, "password must be at least 12 characters")
|
||||
return
|
||||
}
|
||||
h, err := auth.HashPassword(body.Password)
|
||||
if err != nil {
|
||||
jsonErr(w, 500, err.Error())
|
||||
return
|
||||
}
|
||||
if err := s.cfg.Update(func(c *config.Config) { c.PasswordHash = h }); err != nil {
|
||||
jsonErr(w, 500, err.Error())
|
||||
return
|
||||
}
|
||||
log.Printf("password changed (from %s)", clientIP(r))
|
||||
w.WriteHeader(204)
|
||||
}
|
||||
|
||||
// ---- ui prefs (theme) ----
|
||||
|
||||
func (s *Server) getUIPrefs(w http.ResponseWriter, _ *http.Request) {
|
||||
var raw json.RawMessage
|
||||
s.store.View(func(st *store.State) { raw = st.UI })
|
||||
if len(raw) == 0 {
|
||||
raw = json.RawMessage("{}")
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Header().Set("Cache-Control", "no-store")
|
||||
_, _ = w.Write(raw)
|
||||
}
|
||||
|
||||
func (s *Server) putUIPrefs(w http.ResponseWriter, r *http.Request) {
|
||||
b, err := io.ReadAll(io.LimitReader(r.Body, 16<<10))
|
||||
if err != nil || !json.Valid(b) || len(b) == 0 || b[0] != '{' {
|
||||
jsonErr(w, 400, "expected a JSON object")
|
||||
return
|
||||
}
|
||||
if err := s.store.Update(func(st *store.State) { st.UI = json.RawMessage(b) }); err != nil {
|
||||
jsonErr(w, 500, err.Error())
|
||||
return
|
||||
}
|
||||
w.WriteHeader(204)
|
||||
}
|
||||
|
||||
// ---- dashboard layout prefs (opaque JSON object, owned by the UI) ----
|
||||
|
||||
func (s *Server) getDashPrefs(w http.ResponseWriter, _ *http.Request) {
|
||||
|
||||
Reference in New Issue
Block a user