Files
backend/internal/server/handlers_auth.go

54 lines
1.5 KiB
Go

package server
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"code.littlelan.cn/CarrotAssistant/backend/internal/security"
"code.littlelan.cn/CarrotAssistant/backend/internal/store"
)
type loginReq struct {
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"`
}
// POST /admin/login — authenticates an admin and returns a JWT.
func (d Deps) handleLogin(c *gin.Context) {
var req loginReq
if !bind(c, &req) {
return
}
var u store.AdminUser
err := d.DB.Where("username = ?", req.Username).First(&u).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
c.JSON(http.StatusUnauthorized, gin.H{"error": "用户名或密码错误"})
return
}
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "查询失败"})
return
}
if !security.VerifyPassword(u.PasswordHash, req.Password) {
c.JSON(http.StatusUnauthorized, gin.H{"error": "用户名或密码错误"})
return
}
token, err := d.issueToken(u.Username)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "签发令牌失败"})
return
}
d.auditBy(u.Username, nil, nil, "admin.login", nil)
c.JSON(http.StatusOK, gin.H{"token": token, "username": u.Username})
}
// GET /admin/me — returns the currently authenticated admin (for the SPA to
// confirm a stored token is still valid after reload).
func (d Deps) handleMe(c *gin.Context) {
u := adminCtx(c)
c.JSON(http.StatusOK, gin.H{"id": u.ID, "username": u.Username})
}