feat(auth): add user profile, phone login, and account deletion
This commit is contained in:
@@ -18,7 +18,8 @@ type Handler struct {
|
||||
func NewHandler(svc *Service) *Handler { return &Handler{svc: svc} }
|
||||
|
||||
type registerRequest struct {
|
||||
Email string `json:"email" binding:"required"`
|
||||
Email string `json:"email"`
|
||||
Phone string `json:"phone"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
Name string `json:"name" binding:"required"`
|
||||
}
|
||||
@@ -26,34 +27,34 @@ type registerRequest struct {
|
||||
func (h *Handler) Register(c *gin.Context) {
|
||||
var req registerRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.RespondError(c, httpx.BadRequest("invalid_request", "email, password and name are required"))
|
||||
httpx.RespondError(c, httpx.BadRequest("invalid_request", "name and password are required"))
|
||||
return
|
||||
}
|
||||
u, pair, err := h.svc.Register(c.Request.Context(), req.Email, req.Password, req.Name)
|
||||
u, pair, err := h.svc.Register(c.Request.Context(), req.Email, req.Phone, req.Password, req.Name)
|
||||
if err != nil {
|
||||
httpx.RespondError(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusCreated, gin.H{"user": u.Public(), "tokens": pair})
|
||||
c.JSON(http.StatusCreated, gin.H{"user": u.Self(), "tokens": pair})
|
||||
}
|
||||
|
||||
type loginRequest struct {
|
||||
Email string `json:"email" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
Identifier string `json:"identifier" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
}
|
||||
|
||||
func (h *Handler) Login(c *gin.Context) {
|
||||
var req loginRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.RespondError(c, httpx.BadRequest("invalid_request", "email and password are required"))
|
||||
httpx.RespondError(c, httpx.BadRequest("invalid_request", "identifier and password are required"))
|
||||
return
|
||||
}
|
||||
u, pair, err := h.svc.Login(c.Request.Context(), req.Email, req.Password)
|
||||
u, pair, err := h.svc.Login(c.Request.Context(), req.Identifier, req.Password)
|
||||
if err != nil {
|
||||
httpx.RespondError(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"user": u.Public(), "tokens": pair})
|
||||
c.JSON(http.StatusOK, gin.H{"user": u.Self(), "tokens": pair})
|
||||
}
|
||||
|
||||
type refreshRequest struct {
|
||||
@@ -96,7 +97,83 @@ func (h *Handler) Me(c *gin.Context) {
|
||||
if prefs == nil {
|
||||
prefs = map[string]any{}
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"user": u.Public(), "preferences": prefs})
|
||||
c.JSON(http.StatusOK, gin.H{"user": u.Self(), "preferences": prefs})
|
||||
}
|
||||
|
||||
type updateMeRequest struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// UpdateMe changes the display name of the authenticated user.
|
||||
func (h *Handler) UpdateMe(c *gin.Context) {
|
||||
var req updateMeRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.RespondError(c, httpx.BadRequest("invalid_request", "name is required"))
|
||||
return
|
||||
}
|
||||
u, err := h.svc.UpdateName(c.Request.Context(), UserID(c), req.Name)
|
||||
if err != nil {
|
||||
httpx.RespondError(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"user": u.Self()})
|
||||
}
|
||||
|
||||
type changePasswordRequest struct {
|
||||
CurrentPassword string `json:"current_password" binding:"required"`
|
||||
NewPassword string `json:"new_password" binding:"required"`
|
||||
}
|
||||
|
||||
// ChangePassword verifies the current password and revokes all refresh tokens.
|
||||
func (h *Handler) ChangePassword(c *gin.Context) {
|
||||
var req changePasswordRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.RespondError(c, httpx.BadRequest("invalid_request", "current_password and new_password are required"))
|
||||
return
|
||||
}
|
||||
if err := h.svc.ChangePassword(c.Request.Context(), UserID(c), req.CurrentPassword, req.NewPassword); err != nil {
|
||||
httpx.RespondError(c, err)
|
||||
return
|
||||
}
|
||||
c.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
type addIdentifierRequest struct {
|
||||
Email string `json:"email"`
|
||||
Phone string `json:"phone"`
|
||||
}
|
||||
|
||||
// AddIdentifier adds the one missing login identifier.
|
||||
func (h *Handler) AddIdentifier(c *gin.Context) {
|
||||
var req addIdentifierRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.RespondError(c, httpx.BadRequest("invalid_request", "email or phone is required"))
|
||||
return
|
||||
}
|
||||
u, err := h.svc.AddIdentifier(c.Request.Context(), UserID(c), req.Email, req.Phone)
|
||||
if err != nil {
|
||||
httpx.RespondError(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"user": u.Self()})
|
||||
}
|
||||
|
||||
type deleteMeRequest struct {
|
||||
Password string `json:"password" binding:"required"`
|
||||
}
|
||||
|
||||
// DeleteMe deletes the authenticated account and all cascading content.
|
||||
func (h *Handler) DeleteMe(c *gin.Context) {
|
||||
var req deleteMeRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.RespondError(c, httpx.BadRequest("invalid_request", "password is required"))
|
||||
return
|
||||
}
|
||||
if err := h.svc.DeleteAccount(c.Request.Context(), UserID(c), req.Password); err != nil {
|
||||
httpx.RespondError(c, err)
|
||||
return
|
||||
}
|
||||
c.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
Reference in New Issue
Block a user