diff --git a/backend/db/flags.go b/backend/db/flags.go index 15e11fb..1393a5a 100644 --- a/backend/db/flags.go +++ b/backend/db/flags.go @@ -50,7 +50,7 @@ type MemberFlag struct { const ( MaxPrideFlags = 100 MaxPrideFlagTitleLength = 100 - MaxPrideFlagDescLength = 200 + MaxPrideFlagDescLength = 500 ) const ( diff --git a/backend/routes/user/flags.go b/backend/routes/user/flags.go index 2402137..2ad6ebd 100644 --- a/backend/routes/user/flags.go +++ b/backend/routes/user/flags.go @@ -10,7 +10,9 @@ import ( "codeberg.org/u1f320/pronouns.cc/backend/log" "codeberg.org/u1f320/pronouns.cc/backend/server" "emperror.dev/errors" + "github.com/go-chi/chi/v5" "github.com/go-chi/render" + "github.com/rs/xid" ) func (s *Server) getUserFlags(w http.ResponseWriter, r *http.Request) error { @@ -112,7 +114,91 @@ func (s *Server) postUserFlag(w http.ResponseWriter, r *http.Request) error { return nil } +type patchUserFlagRequest struct { + Name *string `json:"name"` + Description *string `json:"description"` +} + func (s *Server) patchUserFlag(w http.ResponseWriter, r *http.Request) error { + ctx := r.Context() + claims, _ := server.ClaimsFromContext(ctx) + + if !claims.TokenWrite { + return server.APIError{Code: server.ErrMissingPermissions, Details: "This token is read-only"} + } + + flagID, err := xid.FromString(chi.URLParam(r, "flagID")) + if err != nil { + return server.APIError{Code: server.ErrNotFound, Details: "Invalid flag ID"} + } + + flags, err := s.DB.AccountFlags(ctx, claims.UserID) + if err != nil { + return errors.Wrap(err, "getting current user flags") + } + if len(flags) >= db.MaxPrideFlags { + return server.APIError{ + Code: server.ErrFlagLimitReached, + } + } + var found bool + for _, flag := range flags { + if flag.ID == flagID { + found = true + break + } + } + if !found { + return server.APIError{Code: server.ErrNotFound, Details: "No flag with that ID found"} + } + + var req patchUserFlagRequest + err = render.Decode(r, &req) + if err != nil { + return server.APIError{Code: server.ErrBadRequest} + } + + if req.Name != nil { + *req.Name = strings.TrimSpace(*req.Name) + } + if req.Description != nil { + *req.Description = strings.TrimSpace(*req.Description) + } + + if req.Name == nil && req.Description == nil { + return server.APIError{Code: server.ErrBadRequest, Details: "Request cannot be empty"} + } + + if s := common.StringLength(req.Name); s > db.MaxPrideFlagTitleLength { + return server.APIError{ + Code: server.ErrBadRequest, + Details: fmt.Sprintf("name too long, must be %v characters or less, is %v", db.MaxPrideFlagTitleLength, s), + } + } + if s := common.StringLength(req.Description); s > db.MaxPrideFlagDescLength { + return server.APIError{ + Code: server.ErrBadRequest, + Details: fmt.Sprintf("description too long, must be %v characters or less, is %v", db.MaxPrideFlagDescLength, s), + } + } + + tx, err := s.DB.Begin(ctx) + if err != nil { + return errors.Wrap(err, "beginning transaction") + } + defer tx.Rollback(ctx) + + flag, err := s.DB.EditFlag(ctx, tx, flagID, req.Name, req.Description, nil) + if err != nil { + return errors.Wrap(err, "updating flag") + } + + err = tx.Commit(ctx) + if err != nil { + return errors.Wrap(err, "committing transaction") + } + + render.JSON(w, r, flag) return nil } diff --git a/backend/routes/user/routes.go b/backend/routes/user/routes.go index dbd943d..c5998f1 100644 --- a/backend/routes/user/routes.go +++ b/backend/routes/user/routes.go @@ -32,7 +32,7 @@ func Mount(srv *server.Server, r chi.Router) { r.Get("/@me/flags", server.WrapHandler(s.getUserFlags)) r.Post("/@me/flags", server.WrapHandler(s.postUserFlag)) - r.Patch("/@me/flags", server.WrapHandler(s.patchUserFlag)) + r.Patch("/@me/flags/{flagID}", server.WrapHandler(s.patchUserFlag)) r.Delete("/@me/flags", server.WrapHandler(s.deleteUserFlag)) }) })