feat: initial commit - Go + CGO captcha recognition service
Build and Deploy / build-frontend (push) Failing after 2s
Build and Deploy / build-backend (push) Has been skipped
Build and Deploy / build-docker (push) Has been skipped
Build and Deploy / deploy (push) Has been skipped

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
This commit is contained in:
2026-07-16 08:56:38 +00:00
commit 524c404194
32 changed files with 2971 additions and 0 deletions
+183
View File
@@ -0,0 +1,183 @@
package main
import (
"fmt"
"log"
"os"
"anticaptcha/internal/captcha"
"anticaptcha/internal/config"
"anticaptcha/internal/database"
"anticaptcha/internal/handler"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
)
func main() {
// 检查是否已安装
if !config.IsInstalled() {
fmt.Println("系统未安装,请先访问 /install 进行安装配置")
}
// 加载配置
if err := config.Load(""); err != nil {
log.Printf("警告: %v", err)
}
// 初始化数据库
if config.Cfg != nil {
if err := database.Init(&config.Cfg.Database); err != nil {
log.Fatalf("数据库初始化失败: %v", err)
}
defer database.Close()
// 确保 admin 用户存在
if err := database.EnsureAdminUser(); err != nil {
log.Printf("警告: 创建管理员用户失败: %v", err)
}
}
// 创建 Gin 引擎
gin.SetMode(gin.ReleaseMode)
r := gin.New()
r.Use(gin.Recovery())
// CORS 配置
r.Use(cors.New(cors.Config{
AllowAllOrigins: true,
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowHeaders: []string{"Origin", "Content-Type", "Authorization"},
ExposeHeaders: []string{"Content-Length"},
AllowCredentials: true,
}))
// 静态文件服务(前端)
r.Static("/assets", "./web/dist/assets")
r.StaticFile("/", "./web/dist/index.html")
r.StaticFile("/favicon.ico", "./web/dist/favicon.ico")
// API 路由
captchaHandler := handler.NewCaptchaHandler(captcha.NewHandler("./models"))
captchaHandler.RegisterRoutes(r.Group(""), true)
// 安装路由
r.GET("/install", func(c *gin.Context) {
c.File("./web/dist/index.html")
})
r.POST("/api/install", handleInstall)
// 健康检查
r.GET("/health", func(c *gin.Context) {
c.JSON(200, gin.H{"status": "ok"})
})
// 启动服务
addr := ":6688"
if config.Cfg != nil {
addr = fmt.Sprintf("%s:%d", config.Cfg.Server.Host, config.Cfg.Server.Port)
}
fmt.Printf(`
╔════════════════════════════════════════════════════════════════╗
║ AntiCaptcha Server v1.0.0 ║
║ https://git.viaeon.com/admin/AntiCaptcha ║
╠════════════════════════════════════════════════════════════════╣
║ 服务已启动: http://%s ║
║ 默认账号: admin / admin ║
╚════════════════════════════════════════════════════════════════╝
`, addr)
if err := r.Run(addr); err != nil {
log.Fatalf("服务启动失败: %v", err)
}
}
func handleInstall(c *gin.Context) {
var req struct {
Database struct {
Type string `json:"type"`
Host string `json:"host"`
Port int `json:"port"`
User string `json:"user"`
Password string `json:"password"`
Database string `json:"database"`
SQLite struct {
Path string `json:"path"`
} `json:"sqlite"`
} `json:"database"`
Redis struct {
Enabled bool `json:"enabled"`
Host string `json:"host"`
Port int `json:"port"`
Password string `json:"password"`
DB int `json:"db"`
} `json:"redis"`
Admin struct {
Username string `json:"username"`
Password string `json:"password"`
} `json:"admin"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
// 生成 JWT Secret
jwtSecret := generateRandomSecret()
// 构建配置
cfg := &config.Config{
Server: config.ServerConfig{
Host: "0.0.0.0",
Port: 6688,
Mode: "release",
},
Database: config.DatabaseConfig{
Type: req.Database.Type,
Host: req.Database.Host,
Port: req.Database.Port,
User: req.Database.User,
Password: req.Database.Password,
Database: req.Database.Database,
SQLite: config.SQLiteConfig{
Path: req.Database.SQLite.Path,
},
},
Redis: config.RedisConfig{
Enabled: req.Redis.Enabled,
Host: req.Redis.Host,
Port: req.Redis.Port,
Password: req.Redis.Password,
DB: req.Redis.DB,
},
JWT: config.JWTConfig{
Secret: jwtSecret,
ExpireTime: 1440,
},
Captcha: config.CaptchaConfig{
ModelPath: "./models",
},
}
// 保存配置
if err := config.Save(cfg); err != nil {
c.JSON(500, gin.H{"error": "保存配置失败"})
return
}
c.JSON(200, gin.H{
"message": "安装成功",
"config": cfg,
})
}
func generateRandomSecret() string {
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
b := make([]byte, 64)
for i := range b {
b[i] = charset[os.New(0).UnixNano()%int64(len(charset))]
}
return string(b)
}