From 6f9a511874005695d3c989c1a98e569bdc0489da Mon Sep 17 00:00:00 2001 From: Eli Ribble Date: Sun, 29 Mar 2026 17:09:01 -0700 Subject: [PATCH] WIP of user avatar work Switching from laptop --- api/handler.go | 20 ++++++++++++++++++++ api/resource.go | 15 +++++++++++++++ api/routes.go | 1 + api/user.go | 4 ++++ db/migrations/00125_user_props.sql | 14 ++++++++++++++ platform/user.go | 2 ++ ts/components/Card.vue | 10 ++++++++++ ts/view/Sudo.vue | 2 +- ts/view/configuration/UserEdit.vue | 2 +- 9 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 api/resource.go create mode 100644 db/migrations/00125_user_props.sql create mode 100644 ts/components/Card.vue diff --git a/api/handler.go b/api/handler.go index 2b6541d8..0ebb4147 100644 --- a/api/handler.go +++ b/api/handler.go @@ -85,6 +85,26 @@ func authenticatedHandlerJSONPost[ReqType any](f handlerFunctionPostAuthenticate http.Redirect(w, r, path, http.StatusFound) }) } + +type handlerFunctionPutAuthenticated[ReqType any] func(context.Context, *http.Request, platform.User, ReqType) (string, *nhttp.ErrorWithStatus) + +func authenticatedHandlerJSONPut[ReqType any](f handlerFunctionPutAuthenticated[ReqType]) http.Handler { + return auth.NewEnsureAuth(func(w http.ResponseWriter, r *http.Request, u platform.User) { + w.Header().Set("Content-Type", "application/json") + req, e := parseRequest[ReqType](r) + if e != nil { + serializeError(w, e) + return + } + ctx := r.Context() + path, e := f(ctx, r, u, *req) + if e != nil { + serializeError(w, e) + return + } + http.Redirect(w, r, path, http.StatusFound) + }) +} func parseRequest[ReqType any](r *http.Request) (*ReqType, *nhttp.ErrorWithStatus) { var req ReqType body, err := io.ReadAll(r.Body) diff --git a/api/resource.go b/api/resource.go new file mode 100644 index 00000000..830deb56 --- /dev/null +++ b/api/resource.go @@ -0,0 +1,15 @@ +package api + +import ( +//"encoding/json" +) + +type resource[T any] struct { + inner *T +} + +func newResource[T any](inner *T) resource[T] { + return resource[T]{ + inner: inner, + } +} diff --git a/api/routes.go b/api/routes.go index 269adb52..cfd04814 100644 --- a/api/routes.go +++ b/api/routes.go @@ -48,6 +48,7 @@ func AddRoutes(r chi.Router) { r.Method("GET", "/user/self", authenticatedHandlerJSON(getUserSelf)) r.Method("GET", "/user/suggestion", authenticatedHandlerJSON(listUserSuggestion)) r.Method("GET", "/user", authenticatedHandlerJSON(listUser)) + r.Method("PUT", "/user/{id}", authenticatedHandlerJSONPut(userPut)) // Unauthenticated endpoints r.Get("/district", apiGetDistrict) diff --git a/api/user.go b/api/user.go index 91c01593..4b5f0cb7 100644 --- a/api/user.go +++ b/api/user.go @@ -95,3 +95,7 @@ func listUserSuggestion(ctx context.Context, r *http.Request, user platform.User Users: users, }, nil } + +func userPut(ctx context.Context, r *http.Request, user platform.User, updates platform.User) { + //if updates.Avatar +} diff --git a/db/migrations/00125_user_props.sql b/db/migrations/00125_user_props.sql new file mode 100644 index 00000000..b34d896a --- /dev/null +++ b/db/migrations/00125_user_props.sql @@ -0,0 +1,14 @@ +-- +goose Up +ALTER TABLE user_ + ADD COLUMN is_active BOOLEAN, + ADD COLUMN is_drone_pilot BOOLEAN, + ADD COLUMN is_warrant BOOLEAN, + ADD COLUMN avatar UUID; +UPDATE user_ SET is_active = TRUE; +ALTER TABLE user_ ALTER COLUMN is_active SET NOT NULL; +-- +goose Down +ALTER TABLE user_ + DROP COLUMN is_active, + DROP COLUMN is_drone_pilot, + DROP COLUMN is_warrant, + DROP COLUMN avatar; diff --git a/platform/user.go b/platform/user.go index 2a3c7184..20c2bd3a 100644 --- a/platform/user.go +++ b/platform/user.go @@ -24,6 +24,7 @@ func (e NoUserError) Error() string { return "That user does not exist" } type User struct { Active bool `json:"active"` + Avatar string `json:"avatar"` DisplayName string `json:"display_name"` ID int `json:"id"` Initials string `json:"initials"` @@ -53,6 +54,7 @@ func (u User) HasRoot() bool { func newUser(ctx context.Context, org Organization, user *models.User) User { u := User{ Active: true, + Avatar: user.Avatar, DisplayName: user.DisplayName, ID: int(user.ID), Initials: extractInitials(user.DisplayName), diff --git a/ts/components/Card.vue b/ts/components/Card.vue new file mode 100644 index 00000000..7ebeecdc --- /dev/null +++ b/ts/components/Card.vue @@ -0,0 +1,10 @@ + diff --git a/ts/view/Sudo.vue b/ts/view/Sudo.vue index 829644e6..fbc60339 100644 --- a/ts/view/Sudo.vue +++ b/ts/view/Sudo.vue @@ -85,7 +85,7 @@ - +
diff --git a/ts/view/configuration/UserEdit.vue b/ts/view/configuration/UserEdit.vue index f4f91ce8..a28c0bc2 100644 --- a/ts/view/configuration/UserEdit.vue +++ b/ts/view/configuration/UserEdit.vue @@ -350,7 +350,7 @@ const saveChanges = async () => { console.error("Failed to upload avatar", error); } } - const response = await fetch(user.urls.api.user, { + const response = await fetch(user.urls.api.users, { method: "PUT", headers: { "Content-Type": "application/json",