package web import ( "net/http" "git.noroutes.net/NoRoutes/vibetunes/internal/auth" "git.noroutes.net/NoRoutes/vibetunes/internal/database" ) func (h *Web) handleSettings(w http.ResponseWriter, r *http.Request) { h.renderSettings(w, r, "", "", "") } func (h *Web) renderSettings(w http.ResponseWriter, r *http.Request, newAPIKey, flash, flashKind string) { user := auth.UserFromContext(r.Context()) keys, err := database.ListAPIKeysForUser(h.DB, user.ID) if err != nil { http.Error(w, "internal error", http.StatusInternalServerError) return } sessions, err := database.ListUserSessions(h.DB, user.ID) if err != nil { http.Error(w, "internal error", http.StatusInternalServerError) return } h.flash(w, r, "settings", "", "Settings", flash, flashKind, struct { APIKeys []*database.APIKey Sessions []*database.Session NewAPIKey string }{keys, sessions, newAPIKey}) } func (h *Web) handlePasswordChange(w http.ResponseWriter, r *http.Request) { user := auth.UserFromContext(r.Context()) if err := r.ParseForm(); err != nil { http.Error(w, "bad request", http.StatusBadRequest) return } current := r.FormValue("current_password") newPassword := r.FormValue("new_password") if !user.PasswordHash.Valid { h.renderSettings(w, r, "", "This account has no local password to change (it signs in via external authentication).", "danger") return } ok, err := auth.VerifyPassword(current, user.PasswordHash.String) if err != nil || !ok { h.renderSettings(w, r, "", "Current password is incorrect.", "danger") return } if len(newPassword) < 8 { h.renderSettings(w, r, "", "New password must be at least 8 characters.", "danger") return } if err := auth.SetUserPassword(h.DB, h.EncryptionKey, user.ID, newPassword); err != nil { http.Error(w, "internal error", http.StatusInternalServerError) return } _ = database.DeleteUserSessions(h.DB, user.ID) if err := h.Sessions.Issue(w, r, user.ID); err != nil { http.Error(w, "internal error", http.StatusInternalServerError) return } h.renderSettings(w, r, "", "Password updated.", "success") } func (h *Web) handleAPIKeyCreate(w http.ResponseWriter, r *http.Request) { user := auth.UserFromContext(r.Context()) if err := r.ParseForm(); err != nil { http.Error(w, "bad request", http.StatusBadRequest) return } name := r.FormValue("name") clientDescription := r.FormValue("client_description") rawKey, err := auth.GenerateAPIKey() if err != nil { http.Error(w, "internal error", http.StatusInternalServerError) return } if _, err := database.CreateAPIKey(h.DB, user.ID, name, auth.HashAPIKey(rawKey), clientDescription); err != nil { http.Error(w, "internal error", http.StatusInternalServerError) return } h.renderSettings(w, r, rawKey, "", "") } func (h *Web) handleAPIKeyRevoke(w http.ResponseWriter, r *http.Request) { user := auth.UserFromContext(r.Context()) id := r.PathValue("id") key, err := database.GetAPIKey(h.DB, id) if err != nil || key.UserID != user.ID { http.NotFound(w, r) return } if err := database.RevokeAPIKey(h.DB, id); err != nil { http.Error(w, "internal error", http.StatusInternalServerError) return } http.Redirect(w, r, "/settings", http.StatusFound) } func (h *Web) handleSessionRevoke(w http.ResponseWriter, r *http.Request) { user := auth.UserFromContext(r.Context()) id := r.PathValue("id") sess, err := database.GetSession(h.DB, id) if err != nil || sess.UserID != user.ID { http.NotFound(w, r) return } if err := database.DeleteSession(h.DB, id); err != nil { http.Error(w, "internal error", http.StatusInternalServerError) return } http.Redirect(w, r, "/settings", http.StatusFound) }