diff --git a/cmd/api/routes.go b/cmd/api/routes.go index 51240f8..aff0cb7 100644 --- a/cmd/api/routes.go +++ b/cmd/api/routes.go @@ -1,8 +1,9 @@ package main import ( - "github.com/julienschmidt/httprouter" "net/http" + + "github.com/julienschmidt/httprouter" ) func (app *application) routes() http.Handler { @@ -29,6 +30,8 @@ func (app *application) routes() http.Handler { router.HandlerFunc(http.MethodPatch, "/v1/movies/:id", app.updateMovieHandler) router.HandlerFunc(http.MethodDelete, "/v1/movies/:id", app.deleteMovieHandler) + router.HandlerFunc(http.MethodPost, "/v1/users", app.registerUserHandler) + // Return the httprouter instance. // Wrap the router with the panic recovery middleware // Wrap the router with the rateLimit() middleware diff --git a/cmd/api/users.go b/cmd/api/users.go new file mode 100644 index 0000000..b74be07 --- /dev/null +++ b/cmd/api/users.go @@ -0,0 +1,67 @@ +package main + +import ( + "errors" + "net/http" + + "greenlight.craftr.fr/internal/data" + "greenlight.craftr.fr/internal/validator" +) + +func (app *application) registerUserHandler(w http.ResponseWriter, r *http.Request) { + // Create an anonymous struct to hold the expected data from the request body + var input struct { + Name string `json:"name"` + Email string `json:"email"` + Password string `json:"password"` + } + + // Parse the request body into the anonymous struct + err := app.readJSON(w, r, &input) + if err != nil { + app.badRequestResponse(w, r, err) + return + } + + // Copy the data from the request body into a new User struct. Notice also that we set the Activated field to false, which isn't strictly necessary because the Activated field will have the zero-value of false by default. But setting this explicitly helps to male our intentions clear to anyone reading the code. + user := &data.User{ + Name: input.Name, + Email: input.Email, + Activated: false, + } + + // Use the Password.Set() method to generate and store the hashed and plaintext passwords + err = user.Password.Set(input.Password) + if err != nil { + app.serverErrorResponse(w, r, err) + return + } + + v := validator.New() + + // Validate the user struct and return the error messages to the client if any of the checks fail + if data.ValidateUser(v, user); !v.Valid() { + app.failedValidationResponse(w, r, v.Errors) + return + } + + // Insert the user data into the database + err = app.models.Users.Insert(user) + if err != nil { + switch { + // If we get a ErrDuplicateEmail error, use the v.AddError() method to manually add a message to the validator instance, and then cal our failedValidationResponse() helper + case errors.Is(err, data.ErrDuplicateEmail): + v.AddError("email", "a user with this email address already exists") + app.failedValidationResponse(w, r, v.Errors) + default: + app.serverErrorResponse(w, r, err) + } + return + } + + // Write a JSON response containing the user data along with a 201 Created status code + err = app.writeJSON(w, http.StatusCreated, envelope{"user": user}, nil) + if err != nil { + app.serverErrorResponse(w, r, err) + } +}