From 5172400803a0de94695d7d522a9ea1621980e58d Mon Sep 17 00:00:00 2001 From: Eli Ribble Date: Wed, 1 Apr 2026 15:32:27 +0000 Subject: [PATCH] Begin switch to gorilla/mux I'm realizing with this code that I'm going to have a problem if I want to do HATEOAS-style APIs. chi just doesn't do resource-oriented API design, and I'd have to build a lot of stuff myself. I'm in the middle of swapping out the UI. Now is the time to make the switch. --- api/handler.go | 7 ++++++- api/user.go | 16 ++++++++++++++-- platform/user.go | 29 +++++++++++++++++++++++++++++ ts/view/configuration/UserEdit.vue | 7 ++++--- 4 files changed, 53 insertions(+), 6 deletions(-) diff --git a/api/handler.go b/api/handler.go index 0ebb4147..a6d488ad 100644 --- a/api/handler.go +++ b/api/handler.go @@ -102,7 +102,12 @@ func authenticatedHandlerJSONPut[ReqType any](f handlerFunctionPutAuthenticated[ serializeError(w, e) return } - http.Redirect(w, r, path, http.StatusFound) + if path == "" { + w.WriteHeader(http.StatusNoContent) + return + } + w.Header().Set("Location", path) + http.Redirect(w, r, path, http.StatusCreated) }) } func parseRequest[ReqType any](r *http.Request) (*ReqType, *nhttp.ErrorWithStatus) { diff --git a/api/user.go b/api/user.go index e919e598..4c7ade50 100644 --- a/api/user.go +++ b/api/user.go @@ -3,11 +3,14 @@ package api import ( "context" "net/http" + "strconv" "github.com/Gleipnir-Technology/nidus-sync/config" "github.com/Gleipnir-Technology/nidus-sync/html" nhttp "github.com/Gleipnir-Technology/nidus-sync/http" "github.com/Gleipnir-Technology/nidus-sync/platform" + "github.com/go-chi/chi/v5" + "github.com/rs/zerolog/log" ) type contentURLAPI struct { @@ -96,7 +99,16 @@ func listUserSuggestion(ctx context.Context, r *http.Request, user platform.User }, nil } -func userPut(ctx context.Context, r *http.Request, user platform.User, updates platform.User) (string, *nhttp.ErrorWithStatus) { - //if updates.Avatar +func userPut(ctx context.Context, r *http.Request, user platform.User, updates platform.UserChangeRequest) (string, *nhttp.ErrorWithStatus) { + log.Info().Str("avatar", updates.Avatar).Msg("doing updates") + user_id_str := chi.URLParam(r, "id") + user_id, err := strconv.Atoi(user_id_str) + if err != nil { + return "", nhttp.NewErrorStatus(http.StatusBadRequest, "user update: %w", err) + } + err = platform.UserUpdate(ctx, user, user_id, updates) + if err != nil { + return "", nhttp.NewError("user update: %w", err) + } return "", nil } diff --git a/platform/user.go b/platform/user.go index b9a52553..abe5741e 100644 --- a/platform/user.go +++ b/platform/user.go @@ -15,6 +15,7 @@ import ( "github.com/Gleipnir-Technology/nidus-sync/db/models" "github.com/Gleipnir-Technology/nidus-sync/debug" "github.com/aarondl/opt/omit" + "github.com/aarondl/opt/omitnull" "github.com/google/uuid" "github.com/rs/zerolog/log" ) @@ -154,6 +155,34 @@ func userSuggestionNonRoot(ctx context.Context, user User, query_arg string) ([] } return results, nil } + +type UserChangeRequest struct { + Avatar string `json:"avatar"` + DisplayName string `json:"display_name"` +} + +func UserUpdate(ctx context.Context, user User, user_id int, updates UserChangeRequest) error { + setter := models.UserSetter{} + target_user, err := models.FindUser(ctx, db.PGInstance.BobDB, int32(user_id)) + if err != nil { + return fmt.Errorf("find user: %w", err) + } + if user.model.Role != enums.UserroleRoot && target_user.OrganizationID != target_user.OrganizationID { + return fmt.Errorf("Current user (%d) isn't allowed to change this user (%d)", user.ID, target_user.ID) + } + if updates.Avatar != "" { + u, err := uuid.Parse(updates.Avatar) + if err != nil { + return fmt.Errorf("parse uuid: %w", err) + } + setter.Avatar = omitnull.From(u) + } + if updates.DisplayName != "" { + setter.DisplayName = omit.From(updates.DisplayName) + } + err = target_user.Update(ctx, db.PGInstance.BobDB, &setter) + return err +} func userSuggestionRoot(ctx context.Context, user User, query_arg string) ([]*User, error) { users, err := models.Users.Query( sm.Where( diff --git a/ts/view/configuration/UserEdit.vue b/ts/view/configuration/UserEdit.vue index 012a8262..429e9a08 100644 --- a/ts/view/configuration/UserEdit.vue +++ b/ts/view/configuration/UserEdit.vue @@ -362,11 +362,12 @@ const saveChanges = async () => { console.error("Failed to upload avatar", error); } } - const url = session.urls?.api.user; - if (!url) { - console.log("empty avatar url"); + const u = user.value; + if (!u) { + console.log("empty user"); return; } + const url = "/api" + u.uri; const response = await fetch(url, { method: "PUT", headers: {