Files
vision-tool/internal/handler/auth_handler.go
Geliebte a3bbf33358 feat: 重构为 Go Web 脚手架——在线图片识别 Web 服务 + vision-cli 独立工具
- Gin + config.yaml + SQLite 后端骨架,JWT 登录 + 管理员初始化
- web/ 原生单页:拖拽多图上传 → Agnes 免费识别 → 结果复制
- HTTP API:/api/v1/vision/analyze 免鉴权 + 令牌桶限流,支持程序直调
- cmd/vision-cli 独立 CLI 工具(复用 service 层,不依赖 Web 服务)
- build.bat / build.sh 构建脚本,移除 mcp-server
- 统一响应格式 {code, message, data},CORS/JWT/限流中间件

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-12 23:59:17 +08:00

89 lines
2.6 KiB
Go
Raw 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 (
"errors"
"net/http"
"github.com/gin-gonic/gin"
"vision-tool/internal/model"
"vision-tool/internal/service"
)
// AuthHandler HTTP 处理器:管理员初始化 / 登录 / 用户信息。
type AuthHandler struct {
svc *service.AuthService
}
func NewAuthHandler(svc *service.AuthService) *AuthHandler {
return &AuthHandler{svc: svc}
}
type initRequest struct {
Username string `json:"username" binding:"required,min=2,max=32"`
Password string `json:"password" binding:"required,min=6,max=64"`
}
type loginRequest struct {
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"`
}
// Check GET /api/v1/admin/check — 管理员是否已初始化
func (h *AuthHandler) Check(c *gin.Context) {
initialized, err := h.svc.CheckAdmin()
if err != nil {
c.JSON(http.StatusInternalServerError, model.Err(model.CodeServerErr, err.Error()))
return
}
c.JSON(http.StatusOK, model.OK(gin.H{"initialized": initialized}))
}
// Init POST /api/v1/admin/init — 初始化管理员(仅首次)
func (h *AuthHandler) Init(c *gin.Context) {
var req initRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, model.Err(model.CodeBadParam, "用户名 2-32 位,密码至少 6 位"))
return
}
if err := h.svc.InitAdmin(req.Username, req.Password); err != nil {
if errors.Is(err, service.ErrAdminExists) {
c.JSON(http.StatusConflict, model.Err(model.CodeConflict, err.Error()))
return
}
c.JSON(http.StatusInternalServerError, model.Err(model.CodeServerErr, err.Error()))
return
}
c.JSON(http.StatusOK, model.OK(gin.H{"message": "管理员初始化成功"}))
}
// Login POST /api/v1/auth/login — 登录获取 JWT token
func (h *AuthHandler) Login(c *gin.Context) {
var req loginRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, model.Err(model.CodeBadParam, "请输入用户名和密码"))
return
}
token, err := h.svc.Login(req.Username, req.Password)
if err != nil {
if errors.Is(err, service.ErrInvalidCredentials) {
c.JSON(http.StatusUnauthorized, model.Err(model.CodeUnauthorized, err.Error()))
return
}
c.JSON(http.StatusInternalServerError, model.Err(model.CodeServerErr, err.Error()))
return
}
c.JSON(http.StatusOK, model.OK(gin.H{"token": token}))
}
// Profile GET /api/v1/user/profile — 当前用户信息(JWT 保护)
func (h *AuthHandler) Profile(c *gin.Context) {
userID := c.GetUint("user_id")
user, err := h.svc.Profile(userID)
if err != nil {
c.JSON(http.StatusInternalServerError, model.Err(model.CodeServerErr, err.Error()))
return
}
c.JSON(http.StatusOK, model.OK(user))
}