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}) }