- 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>
89 lines
2.6 KiB
Go
89 lines
2.6 KiB
Go
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))
|
||
}
|