diff --git a/backend/db/user.go b/backend/db/user.go index 16f699e..95fb3e2 100644 --- a/backend/db/user.go +++ b/backend/db/user.go @@ -156,7 +156,7 @@ func (db *DB) UpdateUser( links *[]string, avatarURLs []string, ) (u User, err error) { - if displayName == nil && bio == nil && links == nil { + if displayName == nil && bio == nil && links == nil && avatarURLs == nil { return u, ErrNothingToUpdate } diff --git a/backend/main.go b/backend/main.go index 7d4c64c..152d706 100644 --- a/backend/main.go +++ b/backend/main.go @@ -2,6 +2,7 @@ package main import ( "context" + "io" "net/http" "os" "os/signal" @@ -9,6 +10,7 @@ import ( "codeberg.org/u1f320/pronouns.cc/backend/log" "codeberg.org/u1f320/pronouns.cc/backend/server" + "github.com/go-chi/render" _ "github.com/joho/godotenv/autoload" ) @@ -20,6 +22,9 @@ func main() { log.Fatalf("Error creating server: %v", err) } + // set render.Decode to a custom one that checks content length + render.Decode = decode + // mount api routes mountRoutes(s) @@ -44,3 +49,29 @@ func main() { log.Fatalf("Error running server: %v", err) } } + +const MaxContentLength = 2 * 1024 * 1024 + +// decode is a custom render.Decode function that makes sure the request doesn't exceed 2 megabytes. +func decode(r *http.Request, v any) error { + if r.ContentLength > MaxContentLength { + return server.APIError{ + Code: server.ErrRequestTooBig, + } + } + + // this is copied from render.Decode to replace r.Body with an io.LimitedReader + var err error + lr := io.LimitReader(r.Body, MaxContentLength) + + switch render.GetRequestContentType(r) { + case render.ContentTypeJSON: + err = render.DecodeJSON(lr, v) + default: + err = server.APIError{ + Code: server.ErrBadRequest, + } + } + + return err +} diff --git a/backend/routes.go b/backend/routes.go index d23f5f9..9a52ab7 100644 --- a/backend/routes.go +++ b/backend/routes.go @@ -3,6 +3,7 @@ package main import ( "codeberg.org/u1f320/pronouns.cc/backend/routes/auth" "codeberg.org/u1f320/pronouns.cc/backend/routes/bot" + "codeberg.org/u1f320/pronouns.cc/backend/routes/member" "codeberg.org/u1f320/pronouns.cc/backend/routes/user" "codeberg.org/u1f320/pronouns.cc/backend/server" "github.com/go-chi/chi/v5" @@ -15,6 +16,7 @@ func mountRoutes(s *server.Server) { s.Router.Route("/v1", func(r chi.Router) { auth.Mount(s, r) user.Mount(s, r) + member.Mount(s, r) bot.Mount(s, r) }) } diff --git a/backend/server/errors.go b/backend/server/errors.go index 49987a7..5ba4578 100644 --- a/backend/server/errors.go +++ b/backend/server/errors.go @@ -79,6 +79,13 @@ const ( // User-related error codes ErrUserNotFound = 2001 + + // Member-related error codes + ErrMemberNotFound = 3001 + ErrMemberLimitReached = 3002 + + // General request error codes + ErrRequestTooBig = 4001 ) var errCodeMessages = map[int]string{ @@ -94,6 +101,11 @@ var errCodeMessages = map[int]string{ ErrInvalidToken: "Supplied token was invalid", ErrUserNotFound: "User not found", + + ErrMemberNotFound: "Member not found", + ErrMemberLimitReached: "Member limit reached", + + ErrRequestTooBig: "Request too big (max 2 MB)", } var errCodeStatuses = map[int]int{ @@ -109,4 +121,9 @@ var errCodeStatuses = map[int]int{ ErrInvalidToken: http.StatusUnauthorized, ErrUserNotFound: http.StatusNotFound, + + ErrMemberNotFound: http.StatusNotFound, + ErrMemberLimitReached: http.StatusBadRequest, + + ErrRequestTooBig: http.StatusBadRequest, } diff --git a/backend/server/server.go b/backend/server/server.go index cfc6218..40b971b 100644 --- a/backend/server/server.go +++ b/backend/server/server.go @@ -25,7 +25,7 @@ type Server struct { } func New() (*Server, error) { - db, err := db.New(os.Getenv("DATABASE_URL")) + db, err := db.New() if err != nil { return nil, err }