112 lines
3.5 KiB
Go
112 lines
3.5 KiB
Go
package handler
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"juhe-factory/api/internal/model"
|
|
"juhe-factory/api/internal/service"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
const adminContextKey = "admin_user"
|
|
|
|
type AdminAuth struct{ service *service.Auth }
|
|
|
|
func NewAdminAuth(service *service.Auth) *AdminAuth { return &AdminAuth{service: service} }
|
|
|
|
func (h *AdminAuth) Login(c *gin.Context) {
|
|
var body struct {
|
|
Username string `json:"username" binding:"required"`
|
|
Password string `json:"password" binding:"required"`
|
|
}
|
|
if err := c.ShouldBindJSON(&body); err != nil {
|
|
fail(c, http.StatusBadRequest, "invalid_request", "请输入管理员账号和密码")
|
|
return
|
|
}
|
|
pair, err := h.service.Login(body.Username, body.Password)
|
|
if err != nil {
|
|
fail(c, http.StatusUnauthorized, "invalid_credentials", err.Error())
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"data": pair})
|
|
}
|
|
|
|
func (h *AdminAuth) Refresh(c *gin.Context) {
|
|
var body struct {
|
|
RefreshToken string `json:"refresh_token" binding:"required"`
|
|
}
|
|
if err := c.ShouldBindJSON(&body); err != nil {
|
|
fail(c, http.StatusBadRequest, "invalid_request", "缺少刷新令牌")
|
|
return
|
|
}
|
|
pair, err := h.service.Refresh(body.RefreshToken)
|
|
if err != nil {
|
|
fail(c, http.StatusUnauthorized, "invalid_refresh_token", err.Error())
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"data": pair})
|
|
}
|
|
|
|
func (h *AdminAuth) Logout(c *gin.Context) {
|
|
var body struct {
|
|
RefreshToken string `json:"refresh_token"`
|
|
}
|
|
_ = c.ShouldBindJSON(&body)
|
|
if err := h.service.Logout(body.RefreshToken); err != nil {
|
|
fail(c, http.StatusInternalServerError, "logout_failed", "退出登录失败")
|
|
return
|
|
}
|
|
c.Status(http.StatusNoContent)
|
|
}
|
|
|
|
func (h *AdminAuth) ChangePassword(c *gin.Context) {
|
|
var body struct {
|
|
CurrentPassword string `json:"current_password" binding:"required"`
|
|
NewPassword string `json:"new_password" binding:"required"`
|
|
ConfirmPassword string `json:"confirm_password" binding:"required"`
|
|
}
|
|
if err := c.ShouldBindJSON(&body); err != nil {
|
|
fail(c, http.StatusBadRequest, "invalid_request", "请完整填写密码")
|
|
return
|
|
}
|
|
admin := currentAdmin(c)
|
|
if err := h.service.ChangePassword(admin.ID, body.CurrentPassword, body.NewPassword, body.ConfirmPassword); err != nil {
|
|
if errors.Is(err, service.ErrReauthFailed) || errors.Is(err, service.ErrAdminPasswordConfirmation) || errors.Is(err, service.ErrAdminPasswordLength) {
|
|
_ = h.service.Audit(admin, "password_change_failed", "admin-auth", admin.ID.String(), err.Error(), c.ClientIP(), c.GetString("trace_id"), nil)
|
|
fail(c, http.StatusBadRequest, "password_invalid", err.Error())
|
|
return
|
|
}
|
|
fail(c, http.StatusInternalServerError, "password_update_failed", "密码修改失败,请稍后重试")
|
|
return
|
|
}
|
|
_ = h.service.Audit(admin, "password_changed", "admin-auth", admin.ID.String(), "", c.ClientIP(), c.GetString("trace_id"), nil)
|
|
c.Status(http.StatusNoContent)
|
|
}
|
|
|
|
func (h *AdminAuth) Middleware() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
raw := strings.TrimSpace(strings.TrimPrefix(c.GetHeader("Authorization"), "Bearer "))
|
|
admin, err := h.service.Authenticate(raw)
|
|
if err != nil {
|
|
fail(c, http.StatusUnauthorized, "unauthorized", err.Error())
|
|
c.Abort()
|
|
return
|
|
}
|
|
c.Set(adminContextKey, admin)
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
func currentAdmin(c *gin.Context) *model.AdminUser {
|
|
value, _ := c.Get(adminContextKey)
|
|
admin, _ := value.(*model.AdminUser)
|
|
return admin
|
|
}
|
|
|
|
func fail(c *gin.Context, status int, code, message string) {
|
|
c.JSON(status, gin.H{"code": code, "message": message, "trace_id": c.GetString("trace_id")})
|
|
}
|