From 9bb7858d425a10b7f6934c65f4b47a4cc005185f Mon Sep 17 00:00:00 2001 From: riglen Date: Thu, 28 May 2026 17:11:57 +0800 Subject: [PATCH] =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 4 ++++ README.md | 13 +++++++++++++ app/config.py | 18 ++++++++++++++++-- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index b97d29e..c915080 100644 --- a/.env.example +++ b/.env.example @@ -4,6 +4,10 @@ HOST=0.0.0.0 PORT=18080 LOG_LEVEL=info +# 上游订阅抓取时使用的 User-Agent。 +# 如果机场对默认 UA 返回 406,可改成你客户端实际使用的 UA,例如 Clash Party 抓包得到的值。 +UPSTREAM_USER_AGENT=sub-provider/0.2 + # 对外访问前缀,尽量改成足够长的随机字符串 PUBLIC_PATH=change-me-random-hash-path diff --git a/README.md b/README.md index b733281..556e029 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ cp .env.example .env - `PUBLIC_PATH` 改成足够长的随机字符串 - `PUBLIC_BASE_URL` 建议填写你反代后的最终访问地址,例如 `https://sub.example.com` +- 如果上游订阅地址对默认 UA 返回 `406`,可在 `.env` 里设置 `UPSTREAM_USER_AGENT`,填成你客户端实际使用的 UA,例如 Clash Party 抓包出来的值 - `AIRPORT_A_URL` / `AIRPORT_B_URL` / `AIRPORT_C_URL` 都可以直接填订阅地址,项目会自动判断是 YAML 还是 URI 订阅 - 也可以直接填本地文件路径,例如 `/app/data/sources/airport-b.txt` 或 `file:///app/data/sources/airport-b.txt` - 允许把其中一个留空;留空时这个机场会自动跳过 @@ -271,3 +272,15 @@ python scripts/compare_conf_outputs.py --sample-source C:\path\to\sample.yaml - `sources` 填多个机场源:**只取第一个**机场源的 `Subscription-Userinfo` 这样 Stash、Clash Party 这类客户端读取配置订阅头时,行为是稳定可预期的。 + +--- + +## 上游 `406` 处理 + +上游订阅抓取默认使用 `sub-provider/0.2` 作为 `User-Agent`。如果机场对这个 UA 返回 `406 Not Acceptable`,可以直接在 `.env` 里覆盖: + +```env +UPSTREAM_USER_AGENT=Clash Party/<你的版本号或抓包值> +``` + +代码里也兼容旧变量名 `DEFAULT_USER_AGENT`,但建议后续统一用 `UPSTREAM_USER_AGENT`。 diff --git a/app/config.py b/app/config.py index c88f9c0..8070b15 100644 --- a/app/config.py +++ b/app/config.py @@ -1,9 +1,10 @@ from __future__ import annotations +import os from functools import lru_cache from pathlib import Path -from pydantic import Field +from pydantic import AliasChoices, Field, field_validator from pydantic_settings import BaseSettings, SettingsConfigDict @@ -25,7 +26,10 @@ class Settings(BaseSettings): cache_ttl_seconds: int = 900 bundle_cache_ttl_seconds: int = 600 max_proxy_name_length: int = 80 - default_user_agent: str = "sub-provider/0.2" + default_user_agent: str = Field( + default="sub-provider/0.2", + validation_alias=AliasChoices("UPSTREAM_USER_AGENT", "DEFAULT_USER_AGENT"), + ) sources_file: Path = CONFIG_DIR / "sources.yaml" conf_base_file: Path = CONFIG_DIR / "conf" / "base.conf" @@ -40,6 +44,16 @@ class Settings(BaseSettings): extra="ignore", ) + @field_validator("default_user_agent", mode="before") + @classmethod + def _coalesce_default_user_agent(cls, value: str) -> str: + if isinstance(value, str) and value.strip(): + return value + legacy = os.getenv("DEFAULT_USER_AGENT", "").strip() + if legacy: + return legacy + return "sub-provider/0.2" + @lru_cache(maxsize=1) def get_settings() -> Settings: