124 lines
3.4 KiB
Go
124 lines
3.4 KiB
Go
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/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/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})
|
||
}
|