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>
This commit is contained in:
2026-08-12 23:59:17 +08:00
parent 8bf77301bd
commit a3bbf33358
25 changed files with 1902 additions and 798 deletions

91
internal/config/config.go Normal file
View File

@@ -0,0 +1,91 @@
package config
import (
"fmt"
"os"
"gopkg.in/yaml.v3"
)
// Config 是应用统一配置,对应 config.yaml。
type Config struct {
Server ServerConfig `yaml:"server"`
Database DatabaseConfig `yaml:"database"`
JWT JWTConfig `yaml:"jwt"`
Upload UploadConfig `yaml:"upload"`
AI AIConfig `yaml:"ai"`
Env EnvConfig `yaml:"env"`
}
type ServerConfig struct {
Port int `yaml:"port"`
}
type DatabaseConfig struct {
Driver string `yaml:"driver"`
Path string `yaml:"path"`
AutoMigrate bool `yaml:"auto_migrate"`
}
type JWTConfig struct {
Secret string `yaml:"secret"`
ExpireHours int `yaml:"expire_hours"`
}
type UploadConfig struct {
Path string `yaml:"path"`
MaxSize int64 `yaml:"max_size"`
}
type AIConfig struct {
APIKey string `yaml:"api_key"`
BaseURL string `yaml:"base_url"`
Model string `yaml:"model"`
}
type EnvConfig struct {
Name string `yaml:"name"`
}
// hardcodedAPIKey 内置默认免费 Key(与 config.yaml 一致),
// 保证 CLI 工具在无配置文件时也能开箱即用。
const hardcodedAPIKey = "sk-68FL9xXRrJhxcY5TCGxoaFlQ94oCBioIJhyGfeDMCkCvA0SV"
// DefaultConfig 返回开箱即用的默认配置。
func DefaultConfig() *Config {
return &Config{
Server: ServerConfig{Port: 8080},
Database: DatabaseConfig{Driver: "sqlite", Path: "./data/app.db", AutoMigrate: true},
JWT: JWTConfig{Secret: "change-me-in-production", ExpireHours: 720},
Upload: UploadConfig{Path: "uploads", MaxSize: 10 * 1024 * 1024},
AI: AIConfig{
APIKey: hardcodedAPIKey,
Model: "agnes-2.0-flash",
BaseURL: "https://apihub.agnes-ai.com/v1/chat/completions",
},
Env: EnvConfig{Name: "dev"},
}
}
// Load 从 yaml 文件加载配置;文件不存在时使用默认值(便于快速上手)。
// 环境变量覆盖:AGNES_API_KEY → ai.api_key。
func Load(path string) (*Config, error) {
cfg := DefaultConfig()
data, err := os.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
return cfg, nil
}
return nil, fmt.Errorf("读取 %s: %w", path, err)
}
if err := yaml.Unmarshal(data, cfg); err != nil {
return nil, fmt.Errorf("解析 %s: %w", path, err)
}
// 环境变量覆盖
if key := os.Getenv("AGNES_API_KEY"); key != "" {
cfg.AI.APIKey = key
}
return cfg, nil
}