fix: /logout routes; improved error handling for invites
This commit is contained in:
@@ -89,6 +89,7 @@ func (h *AuthHandler) Refresh(w http.ResponseWriter, r *http.Request) {
|
|||||||
// @Tags Authorization
|
// @Tags Authorization
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
|
// @Param payload body request.LogoutPayload true "Logout payload"
|
||||||
// @Success 200 {object} nil
|
// @Success 200 {object} nil
|
||||||
// @Error 400 {object} response.ErrorResponse "failed to decode request body"
|
// @Error 400 {object} response.ErrorResponse "failed to decode request body"
|
||||||
// @Error 500 {object} response.ErrorResponse "failed to logout"
|
// @Error 500 {object} response.ErrorResponse "failed to logout"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package handler
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
@@ -29,6 +30,8 @@ func NewUserHandler(userService *domain.UserService, authService *domain.AuthSer
|
|||||||
// @Param payload body request.CreateUserPayload true "Create user payload"
|
// @Param payload body request.CreateUserPayload true "Create user payload"
|
||||||
// @Success 201 {object} domain.User
|
// @Success 201 {object} domain.User
|
||||||
// @Error 400 {object} response.ErrorResponse "failed to decode request body"
|
// @Error 400 {object} response.ErrorResponse "failed to decode request body"
|
||||||
|
// @Error 400 {object} response.ErrorResponse "invite is expired"
|
||||||
|
// @Error 409 {object} response.ErrorResponse "invite is already consumed"
|
||||||
// @Error 400 {object} response.ErrorResponse "invite is invalid"
|
// @Error 400 {object} response.ErrorResponse "invite is invalid"
|
||||||
// @Error 500 {object} response.ErrorResponse "failed to create user"
|
// @Error 500 {object} response.ErrorResponse "failed to create user"
|
||||||
// @Router /users [post]
|
// @Router /users [post]
|
||||||
@@ -47,7 +50,16 @@ func (h *UserHandler) CreateUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
slog.ErrorContext(ctx, "failed to register user",
|
slog.ErrorContext(ctx, "failed to register user",
|
||||||
slog.Any("error", err),
|
slog.Any("error", err),
|
||||||
slog.Any("payload", payload))
|
slog.Any("payload", payload))
|
||||||
|
|
||||||
|
if errors.Is(err, domain.ErrInviteExpired) {
|
||||||
|
response.Error(ctx, w, http.StatusBadRequest, "invite is expired")
|
||||||
|
} else if errors.Is(err, domain.ErrInviteConsumed) {
|
||||||
|
response.Error(ctx, w, http.StatusConflict, "invite is already consumed")
|
||||||
|
} else if errors.Is(err, domain.ErrInviteInvalid) {
|
||||||
|
response.Error(ctx, w, http.StatusBadRequest, "invite is invalid")
|
||||||
|
} else {
|
||||||
response.Error(ctx, w, http.StatusInternalServerError, "failed to register")
|
response.Error(ctx, w, http.StatusInternalServerError, "failed to register")
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ func NewMux(cfg Config) http.Handler {
|
|||||||
apiMux.HandleFunc("POST /login", authHandler.Login)
|
apiMux.HandleFunc("POST /login", authHandler.Login)
|
||||||
apiMux.HandleFunc("POST /login/refresh", authHandler.Refresh)
|
apiMux.HandleFunc("POST /login/refresh", authHandler.Refresh)
|
||||||
apiMux.HandleFunc("POST /logout", authHandler.Logout)
|
apiMux.HandleFunc("POST /logout", authHandler.Logout)
|
||||||
apiMux.HandleFunc("POST /logout/all", authHandler.LogoutAllDevices)
|
apiMux.HandleFunc("POST /logout/all", protected(authHandler.LogoutAllDevices))
|
||||||
|
|
||||||
// Users
|
// Users
|
||||||
apiMux.HandleFunc("POST /users", userHandler.CreateUser)
|
apiMux.HandleFunc("POST /users", userHandler.CreateUser)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
var (
|
var (
|
||||||
ErrInviteIDMissing = errors.New("invite id is required")
|
ErrInviteIDMissing = errors.New("invite id is required")
|
||||||
ErrCodeMissing = errors.New("invite code is required")
|
ErrCodeMissing = errors.New("invite code is required")
|
||||||
|
ErrInviteInvalid = errors.New("invite is invalid")
|
||||||
ErrInviteExpired = errors.New("invite is expired")
|
ErrInviteExpired = errors.New("invite is expired")
|
||||||
ErrInviteConsumed = errors.New("invite is already consumed")
|
ErrInviteConsumed = errors.New("invite is already consumed")
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package domain
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RegistrationService struct {
|
type RegistrationService struct {
|
||||||
@@ -22,6 +23,13 @@ func (s *RegistrationService) Register(ctx context.Context, inviteCode string, e
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if invite.ConsumedAt != nil {
|
||||||
|
return nil, ErrInviteConsumed
|
||||||
|
}
|
||||||
|
if invite.ExpiresAt.Before(time.Now()) {
|
||||||
|
return nil, ErrInviteExpired
|
||||||
|
}
|
||||||
|
|
||||||
var user *User
|
var user *User
|
||||||
err = s.tx.WithinTx(ctx, func(ctx context.Context) error {
|
err = s.tx.WithinTx(ctx, func(ctx context.Context) error {
|
||||||
user, err = s.UserService.CreateUser(ctx, email, username, password)
|
user, err = s.UserService.CreateUser(ctx, email, username, password)
|
||||||
|
|||||||
@@ -33,13 +33,22 @@ func (r *InviteRepo) CreateInvite(ctx context.Context, inviterUserID string, cod
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *InviteRepo) DeleteInvite(ctx context.Context, userID string, inviteID string) error {
|
func (r *InviteRepo) DeleteInvite(ctx context.Context, userID string, inviteID string) error {
|
||||||
_, err := r.conn(ctx).ExecContext(ctx,
|
res, err := r.conn(ctx).ExecContext(ctx,
|
||||||
"DELETE FROM invites WHERE id = $1 AND inviter_user_id = $2 AND consumed_at IS NULL",
|
"DELETE FROM invites WHERE id = $1 AND inviter_user_id = $2 AND consumed_at IS NULL",
|
||||||
inviteID, userID,
|
inviteID, userID,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to delete invite: %w", err)
|
return fmt.Errorf("failed to delete invite: %w", err)
|
||||||
}
|
}
|
||||||
|
affected, err := res.RowsAffected()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not get rows affected: %w", err)
|
||||||
|
}
|
||||||
|
if affected < 1 {
|
||||||
|
// It's more of an assumption,
|
||||||
|
// but unless I encounter this being wrong, I'll keep it.
|
||||||
|
return domain.ErrInviteConsumed
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -57,7 +66,7 @@ func (r *InviteRepo) ConsumeInvite(ctx context.Context, inviteID string, invitee
|
|||||||
return fmt.Errorf("could not get rows affected: %w", err)
|
return fmt.Errorf("could not get rows affected: %w", err)
|
||||||
}
|
}
|
||||||
if affected < 1 {
|
if affected < 1 {
|
||||||
return fmt.Errorf("invite not found or expired")
|
return domain.ErrInviteInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
Reference in New Issue
Block a user