# GoProxy 架构设计

## 系统概览

GoProxy 是一个单二进制的智能代理池系统，由多个协作的 goroutine 组成。核心设计理念：**免费池自动运转 + 订阅池按需导入，两池独立管理但统一对外服务**。

```
                         ┌─────────────────────────┐
                         │       WebUI (:7778)      │
                         │  管理面板 / 订阅管理 / 设置 │
                         └──────────┬──────────────┘
                                    │
  ┌──────────────────┐   ┌──────────┴──────────────┐   ┌──────────────────┐
  │  免费代理源 (20+) │──▶│       SQLite 代理池       │◀──│  订阅源 (Clash/   │
  │  公开列表自动抓取  │   │  proxies / subscriptions  │   │  V2ray/Base64)   │
  └──────────────────┘   └──────────┬──────────────┘   └──────────────────┘
                                    │                           │
                         ┌──────────┴──────────────┐   ┌───────┴────────┐
                         │      对外代理服务          │   │   sing-box     │
                         │  HTTP  (:7776 / :7777)    │   │  协议转换进程   │
                         │  SOCKS5 (:7779 / :7780)   │   │  vmess/trojan  │
                         └───────────────────────────┘   │  → local socks5│
                                                         └────────────────┘
```

## 模块依赖

```
main.go (orchestrator)
  ├── config/       配置管理（环境变量 + config.json），sync.RWMutex 线程安全
  ├── storage/      SQLite 持久化（proxies + subscriptions + source_status）
  ├── fetcher/      免费代理抓取（20+ 源，断路器保护）
  ├── validator/    代理验证（连接 + 出口 IP + 地理位置 + 延迟 + HTTPS 隧道）
  ├── pool/         池子管理（slot 准入、替换逻辑、状态机）
  ├── checker/      健康检查（批次验证，free 删除 / custom 禁用）
  ├── optimizer/    质量优化（替换慢代理，仅作用于免费池）
  ├── custom/       订阅管理
  │   ├── parser.go     格式自动识别（Clash YAML / V2ray 链接 / Base64 / 纯文本）
  │   ├── singbox.go    sing-box 进程管理（配置生成 + 启停 + 端口映射）
  │   └── manager.go    刷新循环 + 探测唤醒 + 过期清理
  ├── proxy/        对外代理服务
  │   ├── server.go       HTTP 代理（支持 CONNECT 隧道）
  │   └── socks5_server.go SOCKS5 代理（原生协议实现）
  ├── webui/        管理面板（嵌入式 HTML + REST API）
  └── logger/       内存日志收集（供 WebUI 展示）
```

## 数据模型

### proxies 表

| 字段 | 类型 | 说明 |
|------|------|------|
| address | TEXT UNIQUE | 代理地址 (host:port) |
| protocol | TEXT | http / socks5 |
| source | TEXT | free / custom |
| subscription_id | INTEGER | 所属订阅 ID（0=免费） |
| exit_ip | TEXT | 出口 IP |
| exit_location | TEXT | "国家代码 城市" |
| latency | INTEGER | 延迟 (ms) |
| quality_grade | TEXT | S(<=500ms) / A(<=1000) / B(<=2000) / C(>2000) |
| status | TEXT | active / degraded / disabled / candidate_replace |
| fail_count | INTEGER | 连续失败次数 |
| use_count / success_count | INTEGER | 使用统计 |

### subscriptions 表

| 字段 | 类型 | 说明 |
|------|------|------|
| url | TEXT | 订阅 URL |
| file_path | TEXT | 本地文件路径 |
| format | TEXT | auto（全自动识别） |
| status | TEXT | active / paused |
| last_fetch | DATETIME | 最后拉取时间 |
| last_success | DATETIME | 最后有可用节点的时间 |
| contributed | INTEGER | 是否为访客贡献 |

## 免费池状态机

```
                   总量>95%
  ┌─────────────────────────────────────┐
  │                                     ▼
┌──────┐  总量<95%  ┌─────────┐  协议<20%  ┌──────────┐  缺失协议或<10%  ┌───────────┐
│HEALTHY│──────────▶│ WARNING │───────────▶│ CRITICAL │─────────────────▶│ EMERGENCY │
└──────┘           └─────────┘            └──────────┘                  └───────────┘
```

各状态对应的行为：

| 状态 | 延迟阈值 | 抓取模式 | 说明 |
|------|---------|---------|------|
| healthy | 2000ms | 不抓取 | 优化器定期替换慢代理 |
| warning | 4000ms | refill (快源) | 补充到 95% |
| critical | 4000ms | refill (快源) | 优先补充缺失协议 |
| emergency | 4000ms | emergency (全源) | 忽略断路器，全力抓取 |

## 订阅池生命周期

```
添加订阅 → 验证(拉取+解析) → 入库
                                │
        ┌───────────────────────┘
        ▼
  刷新订阅（定时/手动）
        │
        ├── 拉取内容（直连 → 代理 fallback）
        ├── 自动识别格式
        ├── 删除该订阅旧代理
        ├── 分类节点
        │   ├── HTTP/SOCKS5 → 直接入池
        │   └── vmess/trojan/... → sing-box 转换 → 本地 SOCKS5 入池
        ├── 验证入池代理（含地理过滤）
        │   ├── 通过 → status=active, 更新 last_success
        │   └── 失败/被过滤 → status=disabled
        └── 更新订阅 proxy_count
```

### 探测唤醒

```
每 N 分钟（默认 10）
    │
    ├── 获取所有 source=custom AND status=disabled 的代理
    ├── 逐个验证
    │   ├── 通过 + 未被地理过滤 → EnableProxy → 更新 last_success
    │   └── 失败 或 被地理过滤 → 保持 disabled
    └── 日志输出恢复数量
```

### 自动清理

每分钟检查：创建超过 7 天且 `last_success` 距今超过 7 天的订阅 → 删除订阅 + 关联代理 + 重建 sing-box。

## 代理选择策略

5 种模式通过 `CustomProxyMode` + `CustomPriority` + `CustomFreePriority` 三个配置组合：

| UI 选项 | Mode | Priority | FreePriority | 选择逻辑 |
|---------|------|----------|-------------|---------|
| 混合·订阅优先 | mixed | true | false | 先 custom，无可用 fallback 全部 |
| 混合·免费优先 | mixed | false | true | 先 free，无可用 fallback 全部 |
| 混合·平等 | mixed | false | false | sourceFilter=""，不区分来源 |
| 仅订阅 | custom_only | - | - | sourceFilter="custom" |
| 仅免费 | free_only | - | - | sourceFilter="free" |

HTTP 代理服务可使用 HTTP 或 SOCKS5 上游代理。SOCKS5 代理服务仅使用 SOCKS5 上游代理。

## sing-box 集成

```
订阅节点 (vmess/vless/trojan/ss/hysteria2/anytls)
    │
    ▼
生成 sing-box JSON 配置：
  inbounds:  每个节点一个本地 SOCKS5 端口 (20001, 20002, ...)
  outbounds: 每个节点对应一个加密协议出站
  route:     inbound → outbound 一一映射
    │
    ▼
启动 sing-box 子进程 (sing-box run -c config.json)
    │
    ▼
本地 socks5://127.0.0.1:20001 ... 入池为 source=custom 代理
```

Docker 镜像自带 sing-box 二进制，支持 amd64/arm64。本地运行需手动安装。

## 后台 Goroutine

| Goroutine | 间隔 | 职责 |
|-----------|------|------|
| 状态监控 | 30s | 检查免费池状态，触发 smartFetchAndFill |
| 健康检查 | 5min | 批量验证代理，free 删除 / custom 禁用 |
| 优化轮换 | 30min | 替换免费池中的慢代理 |
| 订阅刷新 | 1min tick | 检查到期订阅，执行刷新 |
| 探测唤醒 | 10min | 探测 disabled 的订阅代理 |
| 配置监听 | event | WebUI 配置变更后调整 slot |

## 地理过滤

全局生效，对两个池子行为不同：

| 操作 | 免费代理 | 订阅代理 |
|------|---------|---------|
| 启动清理 | DELETE | status → disabled |
| 验证时不通过 | 不入池 | 入池但 disabled |
| 探测唤醒 | - | 被过滤的不启用 |
| 配置变更 | 下次清理生效 | 下次清理生效 |

白名单（`AllowedCountries`）优先于黑名单（`BlockedCountries`）。

## 端口映射

| 端口 | 服务 | 模式 |
|------|------|------|
| 7776 | HTTP 代理 | 最低延迟 |
| 7777 | HTTP 代理 | 随机轮换 |
| 7778 | WebUI | 管理面板 |
| 7779 | SOCKS5 代理 | 随机轮换 |
| 7780 | SOCKS5 代理 | 最低延迟 |
| 20001+ | sing-box 本地 | 仅 127.0.0.1 |
