Files
backend_v2/internal/handler/auth_handler.go
2026-09-20 01:06:13 +08:00

124 lines
3.4 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package handler
import (
"net/http"
"fashionapi/internal/dto"
"fashionapi/internal/middleware"
"fashionapi/internal/pkg/response"
"fashionapi/internal/service"
"github.com/gin-gonic/gin"
)
// AuthHandler 账号体系接口(JWT 无状态)。
type AuthHandler struct {
auth service.AuthService
}
// NewAuthHandler 创建账号 handler。
func NewAuthHandler(auth service.AuthService) *AuthHandler {
return &AuthHandler{auth: auth}
}
// Me 当前登录用户,需 Authorization: Bearer <token>。
//
// GET /api/v1/me(旧别名 /api/v1/auth/me 已废弃)
// 成功:200 { user }
func (h *AuthHandler) Me(c *gin.Context) {
userID, ok := middleware.UserIDFrom(c)
if !ok {
response.Error(c, http.StatusUnauthorized, "unauthorized")
return
}
user, err := h.auth.Me(c.Request.Context(), userID)
if err != nil {
fail(c, err)
return
}
c.JSON(http.StatusOK, gin.H{"user": user})
}
// Login 账号登录,校验账号密码后签发 access + refresh 双令牌。
//
// POST /api/v1/auth/login
// body: { account, password }
// 成功:200 { access_token, refresh_token, expires_in, user }
func (h *AuthHandler) Login(c *gin.Context) {
var req dto.LoginRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, http.StatusBadRequest, "invalid request")
return
}
access, refresh, expiresIn, user, err := h.auth.Login(c.Request.Context(), req.Account, req.Password)
if err != nil {
fail(c, err)
return
}
c.JSON(http.StatusOK, dto.LoginResponse{
AccessToken: access,
RefreshToken: refresh,
ExpiresIn: expiresIn,
User: *user,
})
}
// Refresh 用 refresh token 换取新的 access token。
//
// POST /api/v1/auth/refresh
// body: { refresh_token }
// 成功:200 { access_token, expires_in }
func (h *AuthHandler) Refresh(c *gin.Context) {
var req dto.RefreshRequest
if err := c.ShouldBindJSON(&req); err != nil || req.RefreshToken == "" {
response.Error(c, http.StatusBadRequest, "invalid request")
return
}
access, expiresIn, err := h.auth.Refresh(c.Request.Context(), req.RefreshToken)
if err != nil {
fail(c, err)
return
}
c.JSON(http.StatusOK, dto.RefreshResponse{AccessToken: access, ExpiresIn: expiresIn})
}
// Logout 吊销当前 refresh token(单设备登出)。
//
// DELETE /api/v1/auth/sessions/current(旧别名 POST /api/v1/auth/logout 已废弃)
// body: { refresh_token }
// 成功:200 { ok: true }
func (h *AuthHandler) Logout(c *gin.Context) {
var req dto.RefreshRequest
_ = c.ShouldBindJSON(&req)
if req.RefreshToken == "" {
response.Error(c, http.StatusBadRequest, "invalid request")
return
}
if err := h.auth.Logout(c.Request.Context(), req.RefreshToken); err != nil {
fail(c, err)
return
}
response.Data(c, http.StatusOK, gin.H{"ok": true})
}
// LogoutAll 吊销当前用户全部 refresh token(踢下线 / 全设备登出)。
//
// DELETE /api/v1/auth/sessions(旧别名 POST /api/v1/auth/logout-all 已废弃)
// body: { refresh_token }
// 成功:200 { revoked: <受影响行数> }
func (h *AuthHandler) LogoutAll(c *gin.Context) {
var req dto.RefreshRequest
if err := c.ShouldBindJSON(&req); err != nil || req.RefreshToken == "" {
response.Error(c, http.StatusBadRequest, "invalid request")
return
}
n, err := h.auth.RevokeAllByRefresh(c.Request.Context(), req.RefreshToken)
if err != nil {
fail(c, err)
return
}
response.Data(c, http.StatusOK, gin.H{"revoked": n})
}