feat: initial commit - Go + CGO captcha recognition service
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:
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user