ch15.4
This commit is contained in:
@@ -21,6 +21,7 @@ func (app *application) routes() http.Handler {
|
|||||||
router.HandlerFunc(http.MethodDelete, "/v1/movies/:id", app.deleteMovieHandler)
|
router.HandlerFunc(http.MethodDelete, "/v1/movies/:id", app.deleteMovieHandler)
|
||||||
|
|
||||||
router.HandlerFunc(http.MethodPost, "/v1/users", app.registerUserHandler)
|
router.HandlerFunc(http.MethodPost, "/v1/users", app.registerUserHandler)
|
||||||
|
router.HandlerFunc(http.MethodPut, "/v1/users/activated", app.activateUserHandler)
|
||||||
|
|
||||||
return app.recoverPanic(app.rateLimit(router))
|
return app.recoverPanic(app.rateLimit(router))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,3 +75,58 @@ func (app *application) registerUserHandler(w http.ResponseWriter, r *http.Reque
|
|||||||
app.serverErrorResponse(w, r, err)
|
app.serverErrorResponse(w, r, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (app *application) activateUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var input struct {
|
||||||
|
TokenPlaintext string `json:"token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := app.readJSON(w, r, &input)
|
||||||
|
if err != nil {
|
||||||
|
app.badRequestResponse(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
v := validator.New()
|
||||||
|
|
||||||
|
if data.ValidateTokenPlaintext(v, input.TokenPlaintext); !v.Valid() {
|
||||||
|
app.failedValidationResponse(w, r, v.Errors)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := app.models.Users.GetForToken(data.ScopeActivation, input.TokenPlaintext)
|
||||||
|
if err != nil {
|
||||||
|
switch {
|
||||||
|
case errors.Is(err, data.ErrRecordNotFound):
|
||||||
|
v.AddError("token", "invalid or expired activation token")
|
||||||
|
app.failedValidationResponse(w, r, v.Errors)
|
||||||
|
default:
|
||||||
|
app.serverErrorResponse(w, r, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user.Activated = true
|
||||||
|
|
||||||
|
err = app.models.Users.Update(user)
|
||||||
|
if err != nil {
|
||||||
|
switch {
|
||||||
|
case errors.Is(err, data.ErrEditConflict):
|
||||||
|
app.editConflictResponse(w, r)
|
||||||
|
default:
|
||||||
|
app.serverErrorResponse(w, r, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = app.models.Tokens.DeleteForUser(data.ScopeActivation, user.ID)
|
||||||
|
if err != nil {
|
||||||
|
app.serverErrorResponse(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = app.writeJSON(w, http.StatusOK, envelope{"user": user}, nil)
|
||||||
|
if err != nil {
|
||||||
|
app.serverErrorResponse(w, r, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package data
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/sha256"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
@@ -169,3 +170,44 @@ func (m UserModel) Update(user *User) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m UserModel) GetForToken(tokenScope, tokenPlaintext string) (*User, error) {
|
||||||
|
tokenHash := sha256.Sum256([]byte(tokenPlaintext))
|
||||||
|
|
||||||
|
query := `
|
||||||
|
SELECT users.id, users.created_at, users.name, users.email, users.password_hash, users.activated, users.version
|
||||||
|
FROM users
|
||||||
|
INNER JOIN tokens
|
||||||
|
ON users.id = tokens.user_id
|
||||||
|
WHERE tokens.hash = $1
|
||||||
|
AND tokens.scope = $2
|
||||||
|
AND tokens.expiry > $3`
|
||||||
|
|
||||||
|
args := []any{tokenHash[:], tokenScope, time.Now()}
|
||||||
|
|
||||||
|
var user User
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
err := m.DB.QueryRowContext(ctx, query, args...).Scan(
|
||||||
|
&user.ID,
|
||||||
|
&user.CreatedAt,
|
||||||
|
&user.Name,
|
||||||
|
&user.Email,
|
||||||
|
&user.Password.hash,
|
||||||
|
&user.Activated,
|
||||||
|
&user.Version,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
switch {
|
||||||
|
case errors.Is(err, sql.ErrNoRows):
|
||||||
|
return nil, ErrRecordNotFound
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &user, nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user