fix(codex): 强制统一出站身份并让客户端版本号跟随官方发布

上游 /backend-api/codex 在容量紧张时按客户端身份分优先级降载,被降载的请求
HTTP 200 后立刻推流内 server_is_overloaded。此前网关对配不出官方身份的客户端
整体回退到硬编码的 codex_cli_rs/0.144.1(落后官方 4 个发布),这些请求稳定
落在被优先丢弃的一侧。

- 强制统一出口:所有 OAuth 出站的 User-Agent / originator / version 一律改写
  为网关规范身份,客户端自报身份不参与构造;HTTP / 透传 / WS / alpha-search /
  探针全覆盖。compat 桥接故意删除 originator 的路径保持 no-op。
- 版本号收敛为单一来源,运行时优先级为面板覆写 → 自动同步值 → 内置常量;
  UA 与 version 头同源派生,不再各自硬编码。
- 新增 3 小时自动同步官方客户端最新稳定版,面板可关闭,无需为跟版本而发版。
- 流内 server_is_overloaded / slow_down 改为先在同账号有界重试再切号,并标记为
  请求级瞬时故障,不再据此临时封禁账号。
- 移除被取代的降载身份黑名单、浏览器 UA 兜底及其辅助函数。
This commit is contained in:
zhiyu
2026-08-03 20:14:58 +08:00
parent 825ca7b1fc
commit 2eb24814fe
37 changed files with 1273 additions and 439 deletions
+5
View File
@@ -574,6 +574,9 @@ export interface SystemSettings {
enable_client_dateline_normalization: boolean;
antigravity_user_agent_version: string;
openai_codex_user_agent: string;
openai_codex_client_version: string;
openai_codex_client_version_synced: string;
openai_codex_version_auto_sync_enabled: boolean;
// codex_cli_only 加固
min_codex_version: string;
max_codex_version: string;
@@ -865,6 +868,8 @@ export interface UpdateSettingsRequest {
enable_client_dateline_normalization?: boolean;
antigravity_user_agent_version?: string;
openai_codex_user_agent?: string;
openai_codex_client_version?: string;
openai_codex_version_auto_sync_enabled?: boolean;
// codex_cli_only 加固
min_codex_version?: string;
max_codex_version?: string;
@@ -423,8 +423,14 @@ export default {
antigravityUserAgentVersionPlaceholder: '1.23.2',
antigravityUserAgentVersionHint: 'Leave empty to use ANTIGRAVITY_USER_AGENT_VERSION or the built-in default 1.23.2; when set, the admin setting takes precedence.',
openaiCodexUserAgent: 'OpenAI Codex UA',
openaiCodexUserAgentPlaceholder: 'codex_cli_rs/0.144.1 (Ubuntu 22.4.0; x86_64) xterm-256color',
openaiCodexUserAgentHint: 'Used to bypass Cloudflare browser-UA challenges on the OpenAI upstream. Only applies when the client User-Agent is detected as a browser (Mozilla/...). Leave empty to use the built-in default. Prefer a codex_cli_rs identity: the upstream schedules capacity per originator, and identities in a load-shed bucket get server_is_overloaded, which puts the account into cooldown.',
openaiCodexUserAgentPlaceholder: 'codex_cli_rs/0.146.0 (Ubuntu 22.4.0; x86_64) xterm-256color',
openaiCodexUserAgentHint: 'The full Codex User-Agent used for all outbound requests. Leave empty to build the standard codex_cli_rs identity from the version below (recommended). Under capacity pressure the upstream sheds load by client identity and drops stale or non-official identities first with server_is_overloaded.',
openaiCodexClientVersion: 'Codex client version',
openaiCodexClientVersionPlaceholder: 'Leave empty to follow auto-sync',
openaiCodexClientVersionHint: 'The Codex client version this gateway declares upstream, shared by the User-Agent and the version header. Leave empty to use the auto-synced latest stable release; setting a value pins it and stops following auto-sync.',
openaiCodexVersionAutoSync: 'Auto-sync Codex version',
openaiCodexVersionAutoSyncHint: 'Fetches the latest stable client version from the official repository every 3 hours, so you never need to upgrade this service just to keep the version current. When disabled, only the version above or the built-in default is used.',
openaiCodexVersionSyncedValue: 'Currently synced: {version}',
codexHardeningTitle: "Codex Settings",
codexClientRestrictionTitle: "Codex client restriction",
codexHardeningDesc:
@@ -416,8 +416,14 @@ export default {
antigravityUserAgentVersionPlaceholder: '1.23.2',
antigravityUserAgentVersionHint: '留空时使用 ANTIGRAVITY_USER_AGENT_VERSION 或内置默认值 1.23.2;填写后后台设置优先。',
openaiCodexUserAgent: 'OpenAI Codex UA',
openaiCodexUserAgentPlaceholder: 'codex_cli_rs/0.144.1 (Ubuntu 22.4.0; x86_64) xterm-256color',
openaiCodexUserAgentHint: '用于规避 OpenAI 上游 Cloudflare 对浏览器 UA 的访问质询。仅在检测到客户端 User-Agent 为浏览器(Mozilla/...)时生效,其他客户端原样透传。留空使用内置默认值。建议填写 codex_cli_rs 形态:上游按 originator 分桶调度容量,落在降载桶的身份会被回 server_is_overloaded 并触发账号冷却。',
openaiCodexUserAgentPlaceholder: 'codex_cli_rs/0.146.0 (Ubuntu 22.4.0; x86_64) xterm-256color',
openaiCodexUserAgentHint: '出站统一使用的完整 Codex User-Agent。留空则按下方版本号自动拼出标准 codex_cli_rs 形态(推荐)。上游在容量紧张时按客户端身份分优先级降载,陈旧或非官方形态的身份会被优先丢弃并回 server_is_overloaded。',
openaiCodexClientVersion: 'Codex 客户端版本号',
openaiCodexClientVersionPlaceholder: '留空则跟随自动同步',
openaiCodexClientVersionHint: '网关对上游声明的 Codex 客户端版本号,User-Agent 与 version 头同源使用。留空表示使用自动同步到的官方最新稳定版;填写后固定为该版本,不再跟随同步。',
openaiCodexVersionAutoSync: '自动同步 Codex 版本号',
openaiCodexVersionAutoSyncHint: '每 3 小时从官方仓库获取最新稳定版客户端版本号,无需为了跟版本而升级本服务。关闭后仅使用上方手填版本或内置版本。',
openaiCodexVersionSyncedValue: '当前同步到:{version}',
codexHardeningTitle: 'Codex 设置',
codexClientRestrictionTitle: 'Codex 客户端限制',
codexHardeningDesc:
+75
View File
@@ -5168,6 +5168,65 @@
</p>
</div>
<!-- Codex 客户端版本号 -->
<div>
<label
class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
>
{{
t(
"admin.settings.gatewayForwarding.openaiCodexClientVersion",
)
}}
</label>
<input
v-model="form.openai_codex_client_version"
type="text"
class="input w-full font-mono text-sm"
:placeholder="
t(
'admin.settings.gatewayForwarding.openaiCodexClientVersionPlaceholder',
)
"
/>
<p class="mt-1.5 text-xs text-gray-500 dark:text-gray-400">
{{
t(
"admin.settings.gatewayForwarding.openaiCodexClientVersionHint",
)
}}
</p>
</div>
<!-- Codex 版本号自动同步 -->
<div class="flex items-center justify-between">
<div>
<label
class="text-sm font-medium text-gray-700 dark:text-gray-300"
>
{{
t(
"admin.settings.gatewayForwarding.openaiCodexVersionAutoSync",
)
}}
</label>
<p class="mt-0.5 text-xs text-gray-500 dark:text-gray-400">
{{
t(
"admin.settings.gatewayForwarding.openaiCodexVersionAutoSyncHint",
)
}}
</p>
<p
v-if="codexSyncedVersionLabel"
class="mt-0.5 text-xs text-gray-500 dark:text-gray-400"
>
{{ codexSyncedVersionLabel }}
</p>
</div>
<Toggle v-model="form.openai_codex_version_auto_sync_enabled" />
</div>
</div>
</div>
@@ -9038,6 +9097,10 @@ const form = reactive<SettingsForm>({
enable_client_dateline_normalization: true,
antigravity_user_agent_version: "",
openai_codex_user_agent: "",
openai_codex_client_version: "",
// 只读展示:自动同步任务写入的官方最新稳定版,不参与提交(提交载荷按字段显式构造)
openai_codex_client_version_synced: "",
openai_codex_version_auto_sync_enabled: true,
// codex_cli_only 加固
min_codex_version: "",
max_codex_version: "",
@@ -9957,6 +10020,14 @@ function removeCodexWhitelistRow(i: number): void {
codexWhitelistRows.value.splice(i, 1);
}
const codexSyncedVersionLabel = computed(() => {
const synced = form.openai_codex_client_version_synced?.trim();
if (!synced) return "";
return t("admin.settings.gatewayForwarding.openaiCodexVersionSyncedValue", {
version: synced,
});
});
async function loadSettings() {
loading.value = true;
loadFailed.value = false;
@@ -10510,6 +10581,10 @@ async function saveSettings() {
form.antigravity_user_agent_version?.trim() || "",
openai_codex_user_agent:
form.openai_codex_user_agent?.trim() || "",
openai_codex_client_version:
form.openai_codex_client_version?.trim() || "",
openai_codex_version_auto_sync_enabled:
form.openai_codex_version_auto_sync_enabled,
min_codex_version: form.min_codex_version?.trim() || "",
max_codex_version: form.max_codex_version?.trim() || "",
codex_cli_only_allow_app_server_clients: