524c404194
Features: - Go + CGO ONNX/OpenCV wrapper for high performance - SQLite (default) / MySQL database support - Optional Redis caching - JWT authentication system - Multiple captcha recognition APIs: - OCR text recognition - Slider captcha matching - Image similarity comparison - Rotation captcha detection - Object detection - React frontend with install wizard - Docker and docker-compose support - Gitea CI/CD pipeline Project structure: - cmd/server: Main entry point - internal/: Core business logic - pkg/onnx: ONNX Runtime CGO wrapper - pkg/opencv: OpenCV CGO wrapper - web/: React frontend - deploy/: Deployment configs - scripts/: Utility scripts
332 lines
8.8 KiB
Go
332 lines
8.8 KiB
Go
package handler
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"anticaptcha/internal/captcha"
|
|
"anticaptcha/internal/middleware"
|
|
"anticaptcha/internal/model"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
type CaptchaHandler struct {
|
|
handler *captcha.Handler
|
|
}
|
|
|
|
func NewCaptchaHandler(h *captcha.Handler) *CaptchaHandler {
|
|
return &CaptchaHandler{handler: h}
|
|
}
|
|
|
|
func (h *CaptchaHandler) RegisterRoutes(r *gin.RouterGroup, authRequired bool) {
|
|
captcha := r.Group("/api")
|
|
|
|
// 公共接口
|
|
captcha.POST("/register", h.Register)
|
|
captcha.POST("/login", h.Login)
|
|
captcha.GET("/tokens/verification", middleware.JWTAuth(), h.VerifyToken)
|
|
|
|
// 需要认证的接口
|
|
auth := captcha.Group("")
|
|
if authRequired {
|
|
auth.Use(middleware.JWTAuth())
|
|
}
|
|
|
|
// 验证码识别接口
|
|
auth.POST("/ocr", h.OCR)
|
|
auth.POST("/math", h.Math)
|
|
auth.POST("/slider/match", h.SliderMatch)
|
|
auth.POST("/slider/comparison", h.SliderComparison)
|
|
auth.POST("/compare/similarity", h.CompareSimilarity)
|
|
auth.POST("/rotate/single/rotate", h.SingleRotate)
|
|
auth.POST("/rotate/double/rotate", h.DoubleRotate)
|
|
auth.POST("/detection/icon", h.DetectionIcon)
|
|
auth.POST("/detection/text", h.DetectionText)
|
|
auth.POST("/detection/icon/order", h.DetectionIconOrder)
|
|
auth.POST("/detection/text/order", h.DetectionTextOrder)
|
|
|
|
// 管理接口
|
|
admin := captcha.Group("/admin")
|
|
admin.Use(middleware.JWTAuth(), middleware.AdminOnly())
|
|
admin.POST("/generate_code", h.GenerateCode)
|
|
admin.GET("/regcodes", h.GetRegCodes)
|
|
admin.DELETE("/regcodes/:id", h.DeleteRegCode)
|
|
admin.GET("/users", h.GetUsers)
|
|
admin.PUT("/users/:username", h.UpdateUser)
|
|
admin.GET("/costs", h.GetEndpointCosts)
|
|
admin.POST("/costs", h.SetEndpointCost)
|
|
}
|
|
|
|
// 注册
|
|
func (h *CaptchaHandler) Register(c *gin.Context) {
|
|
var req struct {
|
|
Username string `json:"username" binding:"required"`
|
|
Password string `json:"password" binding:"required"`
|
|
RegistrationCode string `json:"registration_code" binding:"required"`
|
|
}
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
// TODO: 实现注册逻辑
|
|
c.JSON(http.StatusOK, gin.H{"message": "注册成功"})
|
|
}
|
|
|
|
// 登录
|
|
func (h *CaptchaHandler) Login(c *gin.Context) {
|
|
var req struct {
|
|
Username string `json:"username" binding:"required"`
|
|
Password string `json:"password" binding:"required"`
|
|
}
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
// TODO: 验证用户密码
|
|
if req.Username == "admin" && req.Password == "admin" {
|
|
token, err := middleware.GenerateToken(1, "admin", "admin")
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "生成令牌失败"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, model.LoginResponse{
|
|
AccessToken: token,
|
|
TokenType: "bearer",
|
|
Role: "admin",
|
|
Balance: 1000000,
|
|
})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "用户名或密码错误"})
|
|
}
|
|
|
|
// 验证令牌
|
|
func (h *CaptchaHandler) VerifyToken(c *gin.Context) {
|
|
userID := middleware.GetCurrentUserID(c)
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"user_id": userID,
|
|
"valid": true,
|
|
})
|
|
}
|
|
|
|
// OCR 识别
|
|
func (h *CaptchaHandler) OCR(c *gin.Context) {
|
|
var req model.OCRRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
result, err := h.handler.OCR(req.ImageBase64)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, model.CaptchaResult{Result: result})
|
|
}
|
|
|
|
// 数学计算
|
|
func (h *CaptchaHandler) Math(c *gin.Context) {
|
|
var req model.MathRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
result, err := h.handler.Math(req.ImageBase64)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, model.CaptchaResult{Result: result})
|
|
}
|
|
|
|
// 滑块匹配
|
|
func (h *CaptchaHandler) SliderMatch(c *gin.Context) {
|
|
var req model.SliderMatchRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
x, err := h.handler.SliderMatch(req.TargetBase64, req.BackgroundBase64)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, model.CaptchaResult{Result: x})
|
|
}
|
|
|
|
// 阴影滑块
|
|
func (h *CaptchaHandler) SliderComparison(c *gin.Context) {
|
|
var req model.SliderMatchRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
x, err := h.handler.SliderComparison(req.TargetBase64, req.BackgroundBase64)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, model.CaptchaResult{Result: x})
|
|
}
|
|
|
|
// 图片相似度
|
|
func (h *CaptchaHandler) CompareSimilarity(c *gin.Context) {
|
|
var req model.CompareImageRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
similarity, err := h.handler.CompareSimilarity(req.Image1Base64, req.Image2Base64)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, model.CaptchaResult{Result: similarity})
|
|
}
|
|
|
|
// 单图旋转
|
|
func (h *CaptchaHandler) SingleRotate(c *gin.Context) {
|
|
var req model.RotateRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
angle, err := h.handler.SingleRotate(req.ImageBase64)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, model.CaptchaResult{Result: angle})
|
|
}
|
|
|
|
// 双图旋转
|
|
func (h *CaptchaHandler) DoubleRotate(c *gin.Context) {
|
|
var req model.DoubleRotateRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
angle, err := h.handler.DoubleRotate(req.InsideBase64, req.OutsideBase64)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, model.CaptchaResult{Result: angle})
|
|
}
|
|
|
|
// 图标检测
|
|
func (h *CaptchaHandler) DetectionIcon(c *gin.Context) {
|
|
var req model.DetectIconRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
result, err := h.handler.DetectionIcon(req.ImageBase64)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, model.CaptchaResult{Result: result})
|
|
}
|
|
|
|
// 文字检测
|
|
func (h *CaptchaHandler) DetectionText(c *gin.Context) {
|
|
var req model.DetectIconRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
result, err := h.handler.DetectionText(req.ImageBase64)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, model.CaptchaResult{Result: result})
|
|
}
|
|
|
|
// 按序图标检测
|
|
func (h *CaptchaHandler) DetectionIconOrder(c *gin.Context) {
|
|
var req model.DetectIconOrderRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
result, err := h.handler.DetectionIconOrder(req.OrderImgBase64, req.TargetImgBase64)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, model.CaptchaResult{Result: result})
|
|
}
|
|
|
|
// 按序文字检测
|
|
func (h *CaptchaHandler) DetectionTextOrder(c *gin.Context) {
|
|
var req model.DetectIconOrderRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
result, err := h.handler.DetectionTextOrder(req.OrderImgBase64, req.TargetImgBase64)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, model.CaptchaResult{Result: result})
|
|
}
|
|
|
|
// 管理接口占位
|
|
func (h *CaptchaHandler) GenerateCode(c *gin.Context) {
|
|
c.JSON(http.StatusOK, gin.H{"code": "placeholder"})
|
|
}
|
|
|
|
func (h *CaptchaHandler) GetRegCodes(c *gin.Context) {
|
|
c.JSON(http.StatusOK, []interface{}{})
|
|
}
|
|
|
|
func (h *CaptchaHandler) DeleteRegCode(c *gin.Context) {
|
|
c.JSON(http.StatusOK, gin.H{"message": "删除成功"})
|
|
}
|
|
|
|
func (h *CaptchaHandler) GetUsers(c *gin.Context) {
|
|
c.JSON(http.StatusOK, []interface{}{})
|
|
}
|
|
|
|
func (h *CaptchaHandler) UpdateUser(c *gin.Context) {
|
|
c.JSON(http.StatusOK, gin.H{"message": "更新成功"})
|
|
}
|
|
|
|
func (h *CaptchaHandler) GetEndpointCosts(c *gin.Context) {
|
|
c.JSON(http.StatusOK, []interface{}{})
|
|
}
|
|
|
|
func (h *CaptchaHandler) SetEndpointCost(c *gin.Context) {
|
|
c.JSON(http.StatusOK, gin.H{"message": "设置成功"})
|
|
} |