# sub-provider 一个可部署的订阅聚合后端模板,目标是同时支持两种输出方式: - **薄壳模式**:客户端拉取 `/clients/*.yaml`,配置内继续引用远程 `proxy-providers` / `rule-providers` - **打包模式**:客户端拉取 `/bundle/*.yaml`,服务端把节点、策略组、规则全部铺开成单文件 YAML 这一版已经补上: - 多机场源选择:`?sources=airport-a,airport-b` - 单机场或多机场时,**始终取第一个源**的 `Subscription-Userinfo` 返回给客户端 - `GET` 和 `HEAD` 都支持 - provider 单源输出、merged provider 输出、thin client 输出、bundle 输出 - 服务端内部继续解耦:抓取、配额头解析、provider 构建、规则加载、profile 组装分层处理 > 当前版本会自动识别上游输入格式,支持: > 1. 已经能返回 Clash/Mihomo YAML `proxies:` 文件的地址 > 2. base64 编码的 URI 订阅 > 3. 明文 URI 订阅 > > 当前已兼容常见的 `anytls://`、`vless://`、`trojan://`、`ss://`、`vmess://`。 --- ## 目录结构 ```text sub-provider/ app/ config.py main.py models.py services/ cache.py headers.py loader.py profiles.py rules.py subscriptions.py config/ sources.yaml rules/ reject.yaml direct.yaml proxy.yaml cn-ip.yaml data/ cache/ .env.example Dockerfile docker-compose.yaml requirements.txt ``` --- ## 快速开始 1. 复制环境变量文件: ```bash cp .env.example .env ``` 2. 编辑 `.env`: - `PUBLIC_PATH` 改成足够长的随机字符串 - `PUBLIC_BASE_URL` 建议填写你反代后的最终访问地址,例如 `https://sub.example.com` - `AIRPORT_A_URL` / `AIRPORT_B_URL` 都可以直接填订阅地址,项目会自动判断是 YAML 还是 URI 订阅 - 允许把其中一个留空;留空时这个机场会自动跳过 3. 启动: ```bash docker compose up -d --build ``` 4. 访问检查: - 健康检查:`http://YOUR_HOST:18080/healthz` - 单 provider: `https://YOUR_DOMAIN//providers/airport-a.yaml` - merged provider: `https://YOUR_DOMAIN//providers/merged.yaml?sources=airport-a,airport-b` - Mihomo/OpenClash 薄壳入口: `https://YOUR_DOMAIN//clients/mihomo.yaml?sources=airport-a,airport-b` - Stash 薄壳入口: `https://YOUR_DOMAIN//clients/stash.yaml?sources=airport-a,airport-b` - Mihomo/OpenClash bundle: `https://YOUR_DOMAIN//bundle/mihomo.yaml?sources=airport-a,airport-b` - Stash bundle: `https://YOUR_DOMAIN//bundle/stash.yaml?sources=airport-a,airport-b` --- ## 接口说明 ### 1. 单 provider ```text GET //providers/{name}.yaml HEAD //providers/{name}.yaml ``` 返回指定机场源的 provider 文件,并携带这个源的 `Subscription-Userinfo`(如果上游有)。 ### 2. merged provider ```text GET //providers/merged.yaml?sources=airport-a,airport-b HEAD //providers/merged.yaml?sources=airport-a,airport-b ``` 把多个 provider 合并成一个 `proxies:` 文件返回。响应头只取 `sources` 参数里**第一个源**的配额信息。 ### 3. 薄壳客户端配置 ```text GET //clients/mihomo.yaml?sources=airport-a,airport-b GET //clients/stash.yaml?sources=airport-a,airport-b HEAD //clients/mihomo.yaml?sources=airport-a,airport-b HEAD //clients/stash.yaml?sources=airport-a,airport-b ``` 特点: - 客户端收到的是轻量入口配置 - 节点更新依赖远程 `proxy-providers` - 规则更新依赖远程 `rule-providers` - 响应头同样只取第一个源的 `Subscription-Userinfo` ### 4. bundle 单文件配置 ```text GET //bundle/mihomo.yaml?sources=airport-a,airport-b GET //bundle/stash.yaml?sources=airport-a,airport-b HEAD //bundle/mihomo.yaml?sources=airport-a,airport-b HEAD //bundle/stash.yaml?sources=airport-a,airport-b ``` 特点: - 服务端把节点、策略组、规则全部展开到一个 YAML 里 - 适合想直接给 Mihomo Party / Clash Party / Stash 一个最终链接的场景 - 响应头同样只取第一个源的 `Subscription-Userinfo` - 生成后的完整 YAML 会缓存到 `output/bundle-cache/`,默认 600 秒内直接返回缓存 - 可用 `force_refresh=true` 强制跳过缓存并覆盖旧缓存文件 --- ## 默认策略结构 当前默认生成的策略组是一个基础版: - `☁️ 机场选择` - `♻️ 自动选择` - `🚀 手动切换` - `🇭🇰 香港自动` - `🇸🇬 新加坡自动` - `🇯🇵 日本自动` - `🇺🇸 美国自动` - `节点选择` 其中: - `☁️ 机场选择` 允许在“混合自动”和各机场单独自动组之间切换 - `节点选择` 是最终主策略组 - bundle 模式会把节点名全部展开 - thin 模式会保留 provider 引用关系 你后面要继续进阶的话,最值得加的是: - `policies.yaml`:把 Telegram / AI / YouTube / Netflix 这类业务组模板化 - `regions.yaml`:把更多地区从 `sources.yaml` 独立出去 - URI/base64 原始订阅解析 - 鉴权层(例如前置 Caddy/Nginx Basic Auth 或仅 Tailscale 可访问) --- ## 配额头策略 为了避免聚合多个机场后“总流量怎么显示”语义混乱,这个版本统一采用: - `sources` 只填一个机场源:返回这个机场源的配额信息 - `sources` 填多个机场源:**只取第一个**机场源的 `Subscription-Userinfo` 这样 Stash、Clash Party 这类客户端读取配置订阅头时,行为是稳定可预期的。