Files
sub2api-overdraft/frontend/src/api/admin/settings.ts
T
DaydreamCodingandClaude Opus 4.8 819fda34d9 feat(codex-detect): codex_cli_only 检测加固 + 引擎指纹统一信号列表 + 账号级 app-server
将 codex_cli_only 客户端识别从「单一 strict 开关 + 固定 OR 头集合」重构为
可逐项管理的引擎指纹信号列表,加固整条判定链,并补齐账号级 app-server 控制、
对齐前端设置文案。

判定链(每步可短路):
- 账号未开 codex_cli_only → 不限制;gateway.force_codex_cli → 旁路放行
- 全局黑名单命中(OR 宽 deny)→ 立即拒
- 身份候选:官方 UA(strict,仅前缀)/ 官方 originator(OR)/ 全局白名单(双因子 AND)
  / 全局 app-server 开关 OR 账号 app-server 开关;均不命中 → 拒
- 版本门(仅官方候选):UA 须可解析引擎版本,再校验 [min,max] 区间
- 引擎指纹 AND 硬门:按信号列表逐条勾选 AND、每条行内变体 OR;无 Required 信号 → 放行

引擎指纹信号列表(唯一真源)
- 新增 openai.EngineFingerprintSignal 类型 + EvaluateEngineFingerprint 求值器
  (勾选 AND / 行内变体 OR / 无勾选 → 放行)
- CodexRestrictionPolicy 增 EngineFingerprintSignals;信号列表单一决定是否启用指纹门,
  不再保留独立「要求引擎指纹」总开关(与「信号全不选」语义重复)
- 新设置键 codex_cli_only_engine_fingerprint_signals(默认只勾 x-codex- 前缀);
  旧 body 指纹开关幂等迁移并入信号列表;wire 接线
- 黑/白名单自由条目、命名预设、版本区间 全局设置管线
- gateway 缺 settingService(仅测试/误配可达)时指纹门回退默认种子信号、失败关闭,
  不再因零值 policy(nil 信号)失败开放

账号级 Codex app-server(替换已失效的 ClaudeCode 放行机制)
- account.IsCodexCLIOnlyAppServerAllowed() 读 extra.codex_cli_only_allow_app_server,
  仅在 codex_cli_only 开启时生效;候选身份门「全局 OR 账号」,与旧系统双层控制对齐
- 移除已无入口的 claude_code 预设机制(allowedClientRegistry / MatchAllowedClients /
  账号 GetCodexCLIOnlyAllowedClients / reason);白名单 AllowedClientEntry / IsAllowedClientMatch 保留

门加固(反伪 + 写入校验)
- 官方 UA 访问门改 strict:IsCodexOfficialClientRequestStrict 仅前缀匹配,收窄「浏览器前缀 +
  中段 codex token」伪造面(strict 仍保留 Codex 家族前缀与 UA 尾部兜底,故对「任意前缀 +
  官方尾部 (name;ver)」仍放行——与 UA 可伪造、真正反伪靠引擎指纹门的设计一致)
- 官方客户端识别扩展:新增 codex-tui/、codex_vscode_copilot/ 前缀 + UA 尾部 (name;ver) 兜底
  (恢复 CODEX_INTERNAL_ORIGINATOR_OVERRIDE 的真实 client,如 cccc→codex-tui),originator 改
  精确集。该识别经 IsCodexOfficialClientByHeaders 被 passthrough 复用,故透传的官方判定一并
  修正(codex-tui 等不再被误改写 UA)——非「行为不变」,属有意修正
- 白名单写入校验 ValidateCodexWhitelistEntriesJSON + AllowedClientEntry.IsWhitelistable:
  双因子 AND 条目须可命中(非空 originator + 非空 ua_contains),拒绝写入会静默失效的死规则;
  黑名单(OR 宽 deny,允许 originator-only)不受约束

管理端 / 前端
- handler / DTO / settings_view / 契约测试;gateway 接入判定链
- 信号列表编辑器(替换 body 开关)、api 类型、SettingsView;无勾选给常驻警告
- Create/Edit/Bulk 三弹窗「Codex Only」下新增 app-server 开关(OR 合并全局)
- 文案:UA/Originator → User-Agent/Originator;黑/白名单重命名为 User-Agent/Originator 黑/白名单;
  「允许 App Server 第三方客户端」→「Codex app-server」+ 简介示例;i18n zh/en 同步
- 移除死代码 HasCodex*Fingerprint helper

测试:引擎指纹求值器 / 账号 app-server(OR 语义)/ detector(含 N1 strict、失败关闭)/
白名单写入校验 / BulkEdit spec 等;后端 build + service/openai/admin 单测全绿,前端 vue-tsc + vitest 全绿。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 16:19:41 +08:00

1391 lines
43 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Admin Settings API endpoints
* Handles system settings management for administrators
*/
import { apiClient } from "../client";
import type {
CustomEndpoint,
CustomMenuItem,
LoginAgreementDocument,
NotifyEmailEntry,
} from "@/types";
export interface DefaultSubscriptionSetting {
group_id: number;
validity_days: number;
}
// ── 平台限额类型 ──────────────────────────────────────────────────
export type PlatformType = "anthropic" | "openai" | "gemini" | "antigravity" | "grok"
export type QuotaWindowType = "daily" | "weekly" | "monthly"
/** 单平台三档限额;null = 不限制,undefined = 未填(等价 null) */
export interface PlatformQuotaLimits {
daily: number | null
weekly: number | null
monthly: number | null
}
/** 全平台默认限额 map(key = PlatformType) */
export type DefaultPlatformQuotasMap = Partial<Record<PlatformType, PlatformQuotaLimits>>
const PLATFORMS: PlatformType[] = ["anthropic", "openai", "gemini", "antigravity", "grok"]
/** 归一化为全 4 平台 × 3 窗口(缺失填 null),供模板非空绑定 */
export function normalizePlatformQuotasMap(input?: DefaultPlatformQuotasMap | null): DefaultPlatformQuotasMap {
const result: DefaultPlatformQuotasMap = {}
for (const p of PLATFORMS) {
const src = input?.[p]
result[p] = {
daily: typeof src?.daily === "number" ? src.daily : null,
weekly: typeof src?.weekly === "number" ? src.weekly : null,
monthly: typeof src?.monthly === "number" ? src.monthly : null,
}
}
return result
}
/** 提交前清洗:非有限数/负数/空字符串 → null(保留 0 = 显式禁用),返回全 4 平台嵌套 map */
export function sanitizePlatformQuotasMap(input?: DefaultPlatformQuotasMap | null): DefaultPlatformQuotasMap {
const clean = (v: unknown): number | null => (typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : null)
const result: DefaultPlatformQuotasMap = {}
for (const p of PLATFORMS) {
const src = input?.[p]
result[p] = { daily: clean(src?.daily), weekly: clean(src?.weekly), monthly: clean(src?.monthly) }
}
return result
}
export type AuthSourceType =
| "email"
| "linuxdo"
| "oidc"
| "wechat"
| "github"
| "google"
| "dingtalk";
export interface AuthSourceDefaultsValue {
balance: number;
concurrency: number;
subscriptions: DefaultSubscriptionSetting[];
grant_on_signup: boolean;
grant_on_first_bind: boolean;
// ★ 新增:平台限额覆盖(key = PlatformType)
platform_quotas: DefaultPlatformQuotasMap;
}
export type AuthSourceDefaultsState = Record<
AuthSourceType,
AuthSourceDefaultsValue
>;
export type PaymentVisibleMethod = "alipay" | "wxpay";
export type PaymentVisibleMethodSource =
| ""
| "official_alipay"
| "easypay_alipay"
| "official_wxpay"
| "easypay_wxpay";
export type WeChatConnectMode = "open" | "mp" | "mobile";
export interface PaymentVisibleMethodSourceOption {
value: PaymentVisibleMethodSource;
labelZh: string;
labelEn: string;
}
export interface WeChatConnectModeOption {
value: WeChatConnectMode;
labelZh: string;
labelEn: string;
}
const AUTH_SOURCE_TYPES: AuthSourceType[] = [
"email",
"linuxdo",
"oidc",
"wechat",
"github",
"google",
"dingtalk",
];
const AUTH_SOURCE_DEFAULT_BALANCE = 0;
const AUTH_SOURCE_DEFAULT_CONCURRENCY = 5;
const PAYMENT_VISIBLE_METHOD_SOURCE_OPTIONS: Record<
PaymentVisibleMethod,
PaymentVisibleMethodSourceOption[]
> = {
alipay: [
{ value: "", labelZh: "未配置", labelEn: "Not configured" },
{
value: "official_alipay",
labelZh: "支付宝官方",
labelEn: "Official Alipay",
},
{
value: "easypay_alipay",
labelZh: "易支付支付宝",
labelEn: "EasyPay Alipay",
},
],
wxpay: [
{ value: "", labelZh: "未配置", labelEn: "Not configured" },
{
value: "official_wxpay",
labelZh: "微信官方",
labelEn: "Official WeChat Pay",
},
{
value: "easypay_wxpay",
labelZh: "易支付微信",
labelEn: "EasyPay WeChat Pay",
},
],
};
const PAYMENT_VISIBLE_METHOD_SOURCE_ALIASES: Record<
PaymentVisibleMethod,
Record<string, PaymentVisibleMethodSource>
> = {
alipay: {
official_alipay: "official_alipay",
alipay: "official_alipay",
alipay_direct: "official_alipay",
official: "official_alipay",
easypay_alipay: "easypay_alipay",
easypay: "easypay_alipay",
},
wxpay: {
official_wxpay: "official_wxpay",
wxpay: "official_wxpay",
wxpay_direct: "official_wxpay",
wechat: "official_wxpay",
official: "official_wxpay",
easypay_wxpay: "easypay_wxpay",
easypay: "easypay_wxpay",
},
};
const WECHAT_CONNECT_MODE_OPTIONS: WeChatConnectModeOption[] = [
{ value: "open", labelZh: "PC 应用", labelEn: "PC App" },
{
value: "mp",
labelZh: "公众号",
labelEn: "Official Account",
},
{
value: "mobile",
labelZh: "移动应用",
labelEn: "Mobile App",
},
];
const WECHAT_CONNECT_MODE_ALIASES: Record<string, WeChatConnectMode> = {
open: "open",
open_platform: "open",
official: "open",
wx_open: "open",
mp: "mp",
official_account: "mp",
wechat_mp: "mp",
mini_program: "mp",
mobile: "mobile",
mobile_app: "mobile",
native_app: "mobile",
};
export function normalizeDefaultSubscriptionSettings(
subscriptions: DefaultSubscriptionSetting[] | null | undefined,
): DefaultSubscriptionSetting[] {
if (!Array.isArray(subscriptions)) return [];
return subscriptions
.filter((item) => item.group_id > 0 && item.validity_days > 0)
.map((item) => ({
group_id: Math.floor(item.group_id),
validity_days: Math.min(
36500,
Math.max(1, Math.floor(item.validity_days)),
),
}));
}
export function buildAuthSourceDefaultsState(
settings: Partial<SystemSettings>,
): AuthSourceDefaultsState {
const raw = settings as Record<string, unknown>;
return AUTH_SOURCE_TYPES.reduce((acc, source) => {
const subscriptions = raw[`auth_source_default_${source}_subscriptions`];
acc[source] = {
balance: Number(
raw[`auth_source_default_${source}_balance`] ??
AUTH_SOURCE_DEFAULT_BALANCE,
),
concurrency: Math.max(
1,
Number(
raw[`auth_source_default_${source}_concurrency`] ??
AUTH_SOURCE_DEFAULT_CONCURRENCY,
),
),
subscriptions: normalizeDefaultSubscriptionSettings(
Array.isArray(subscriptions)
? (subscriptions as DefaultSubscriptionSetting[])
: [],
),
grant_on_signup:
raw[`auth_source_default_${source}_grant_on_signup`] === true,
grant_on_first_bind:
raw[`auth_source_default_${source}_grant_on_first_bind`] === true,
platform_quotas: normalizePlatformQuotasMap(raw[`auth_source_default_${source}_platform_quotas`] as DefaultPlatformQuotasMap | undefined),
};
return acc;
}, {} as AuthSourceDefaultsState);
}
export function appendAuthSourceDefaultsToUpdateRequest(
payload: UpdateSettingsRequest,
authSourceDefaults: AuthSourceDefaultsState,
): UpdateSettingsRequest {
const target = payload as Record<string, unknown>;
for (const source of AUTH_SOURCE_TYPES) {
const current = authSourceDefaults[source];
target[`auth_source_default_${source}_balance`] =
Number(current.balance) || 0;
target[`auth_source_default_${source}_concurrency`] = Math.max(
1,
Math.floor(
Number(current.concurrency) || AUTH_SOURCE_DEFAULT_CONCURRENCY,
),
);
target[`auth_source_default_${source}_subscriptions`] =
normalizeDefaultSubscriptionSettings(current.subscriptions);
target[`auth_source_default_${source}_grant_on_signup`] =
current.grant_on_signup;
target[`auth_source_default_${source}_grant_on_first_bind`] =
current.grant_on_first_bind;
target[`auth_source_default_${source}_platform_quotas`] = sanitizePlatformQuotasMap(current.platform_quotas)
}
return payload;
}
export function getPaymentVisibleMethodSourceOptions(
method: PaymentVisibleMethod,
): PaymentVisibleMethodSourceOption[] {
return PAYMENT_VISIBLE_METHOD_SOURCE_OPTIONS[method];
}
export function normalizePaymentVisibleMethodSource(
method: PaymentVisibleMethod,
source: unknown,
): PaymentVisibleMethodSource {
if (typeof source !== "string") return "";
const normalized = source.trim().toLowerCase();
if (!normalized) return "";
return PAYMENT_VISIBLE_METHOD_SOURCE_ALIASES[method][normalized] ?? "";
}
export function getWeChatConnectModeOptions(): WeChatConnectModeOption[] {
return WECHAT_CONNECT_MODE_OPTIONS;
}
export function normalizeWeChatConnectMode(source: unknown): WeChatConnectMode {
if (typeof source !== "string") return "open";
const normalized = source.trim().toLowerCase();
if (!normalized) return "open";
return WECHAT_CONNECT_MODE_ALIASES[normalized] ?? "open";
}
export function defaultWeChatConnectScopesForMode(mode: unknown): string {
switch (normalizeWeChatConnectMode(mode)) {
case "mp":
return "snsapi_userinfo";
case "mobile":
return "";
default:
return "snsapi_login";
}
}
export function resolveWeChatConnectModeCapabilities(
openEnabled: unknown,
mpEnabled: unknown,
mobileEnabled: unknown,
legacyMode: unknown,
): { openEnabled: boolean; mpEnabled: boolean; mobileEnabled: boolean } {
if (
typeof openEnabled === "boolean" ||
typeof mpEnabled === "boolean" ||
typeof mobileEnabled === "boolean"
) {
return {
openEnabled: openEnabled === true,
mpEnabled: mpEnabled === true,
mobileEnabled: mobileEnabled === true,
};
}
switch (normalizeWeChatConnectMode(legacyMode)) {
case "mp":
return { openEnabled: false, mpEnabled: true, mobileEnabled: false };
case "mobile":
return { openEnabled: false, mpEnabled: false, mobileEnabled: true };
default:
return { openEnabled: true, mpEnabled: false, mobileEnabled: false };
}
}
export function deriveWeChatConnectStoredMode(
openEnabled: boolean,
mpEnabled: boolean,
mobileEnabled: boolean,
legacyMode: unknown,
): WeChatConnectMode {
if (mpEnabled) return "mp";
if (mobileEnabled) return "mobile";
if (openEnabled) return "open";
return normalizeWeChatConnectMode(legacyMode);
}
/**
* System settings interface
*/
export interface SystemSettings {
// Registration settings
registration_enabled: boolean;
email_verify_enabled: boolean;
registration_email_suffix_whitelist: string[];
promo_code_enabled: boolean;
password_reset_enabled: boolean;
frontend_url: string;
invitation_code_enabled: boolean;
totp_enabled: boolean; // TOTP 双因素认证
totp_encryption_key_configured: boolean; // TOTP 加密密钥是否已配置
login_agreement_enabled: boolean;
login_agreement_mode: "modal" | "checkbox" | string;
login_agreement_updated_at: string;
login_agreement_documents: LoginAgreementDocument[];
// Default settings
default_balance: number;
affiliate_rebate_rate: number;
affiliate_rebate_freeze_hours: number;
affiliate_rebate_duration_days: number;
affiliate_rebate_per_invitee_cap: number;
default_concurrency: number;
default_user_rpm_limit: number;
default_subscriptions: DefaultSubscriptionSetting[];
auth_source_default_email_balance?: number;
auth_source_default_email_concurrency?: number;
auth_source_default_email_subscriptions?: DefaultSubscriptionSetting[];
auth_source_default_email_grant_on_signup?: boolean;
auth_source_default_email_grant_on_first_bind?: boolean;
auth_source_default_linuxdo_balance?: number;
auth_source_default_linuxdo_concurrency?: number;
auth_source_default_linuxdo_subscriptions?: DefaultSubscriptionSetting[];
auth_source_default_linuxdo_grant_on_signup?: boolean;
auth_source_default_linuxdo_grant_on_first_bind?: boolean;
auth_source_default_oidc_balance?: number;
auth_source_default_oidc_concurrency?: number;
auth_source_default_oidc_subscriptions?: DefaultSubscriptionSetting[];
auth_source_default_oidc_grant_on_signup?: boolean;
auth_source_default_oidc_grant_on_first_bind?: boolean;
auth_source_default_wechat_balance?: number;
auth_source_default_wechat_concurrency?: number;
auth_source_default_wechat_subscriptions?: DefaultSubscriptionSetting[];
auth_source_default_wechat_grant_on_signup?: boolean;
auth_source_default_wechat_grant_on_first_bind?: boolean;
auth_source_default_dingtalk_balance?: number;
auth_source_default_dingtalk_concurrency?: number;
auth_source_default_dingtalk_subscriptions?: DefaultSubscriptionSetting[];
auth_source_default_dingtalk_grant_on_signup?: boolean;
auth_source_default_dingtalk_grant_on_first_bind?: boolean;
auth_source_default_github_balance?: number;
auth_source_default_github_concurrency?: number;
auth_source_default_github_subscriptions?: DefaultSubscriptionSetting[];
auth_source_default_github_grant_on_signup?: boolean;
auth_source_default_github_grant_on_first_bind?: boolean;
auth_source_default_google_balance?: number;
auth_source_default_google_concurrency?: number;
auth_source_default_google_subscriptions?: DefaultSubscriptionSetting[];
auth_source_default_google_grant_on_signup?: boolean;
auth_source_default_google_grant_on_first_bind?: boolean;
force_email_on_third_party_signup?: boolean;
// ── 平台限额(嵌套 JSON,系统层 + 7 auth-source 层)────────────────────────────────
default_platform_quotas?: DefaultPlatformQuotasMap;
auth_source_default_email_platform_quotas?: DefaultPlatformQuotasMap;
auth_source_default_linuxdo_platform_quotas?: DefaultPlatformQuotasMap;
auth_source_default_oidc_platform_quotas?: DefaultPlatformQuotasMap;
auth_source_default_wechat_platform_quotas?: DefaultPlatformQuotasMap;
auth_source_default_github_platform_quotas?: DefaultPlatformQuotasMap;
auth_source_default_google_platform_quotas?: DefaultPlatformQuotasMap;
auth_source_default_dingtalk_platform_quotas?: DefaultPlatformQuotasMap;
// OEM settings
site_name: string;
site_logo: string;
site_subtitle: string;
api_base_url: string;
contact_info: string;
doc_url: string;
home_content: string;
hide_ccs_import_button: boolean;
table_default_page_size: number;
table_page_size_options: number[];
backend_mode_enabled: boolean;
custom_menu_items: CustomMenuItem[];
custom_endpoints: CustomEndpoint[];
// SMTP settings
smtp_host: string;
smtp_port: number;
smtp_username: string;
smtp_password_configured: boolean;
smtp_from_email: string;
smtp_from_name: string;
smtp_use_tls: boolean;
// Cloudflare Turnstile settings
turnstile_enabled: boolean;
turnstile_site_key: string;
turnstile_secret_key_configured: boolean;
api_key_acl_trust_forwarded_ip: boolean;
// LinuxDo Connect OAuth settings
linuxdo_connect_enabled: boolean;
linuxdo_connect_client_id: string;
linuxdo_connect_client_secret_configured: boolean;
linuxdo_connect_redirect_url: string;
// DingTalk Connect OAuth settings
dingtalk_connect_enabled: boolean;
dingtalk_connect_client_id: string;
dingtalk_connect_client_secret_configured: boolean;
dingtalk_connect_redirect_url: string;
dingtalk_connect_corp_restriction_policy: string;
dingtalk_connect_internal_corp_id: string;
dingtalk_connect_bypass_registration: boolean;
dingtalk_connect_sync_corp_email: boolean;
dingtalk_connect_sync_display_name: boolean;
dingtalk_connect_sync_dept: boolean;
dingtalk_connect_sync_corp_email_attr_key: string;
dingtalk_connect_sync_display_name_attr_key: string;
dingtalk_connect_sync_dept_attr_key: string;
dingtalk_connect_sync_corp_email_attr_name: string;
dingtalk_connect_sync_display_name_attr_name: string;
dingtalk_connect_sync_dept_attr_name: string;
// WeChat Connect OAuth settings
wechat_connect_enabled: boolean;
wechat_connect_app_id: string;
wechat_connect_app_secret_configured: boolean;
wechat_connect_open_app_id?: string;
wechat_connect_open_app_secret_configured?: boolean;
wechat_connect_mp_app_id?: string;
wechat_connect_mp_app_secret_configured?: boolean;
wechat_connect_mobile_app_id?: string;
wechat_connect_mobile_app_secret_configured?: boolean;
wechat_connect_open_enabled?: boolean;
wechat_connect_mp_enabled?: boolean;
wechat_connect_mobile_enabled?: boolean;
wechat_connect_mode: string;
wechat_connect_scopes: string;
wechat_connect_redirect_url: string;
wechat_connect_frontend_redirect_url: string;
// Generic OIDC OAuth settings
oidc_connect_enabled: boolean;
oidc_connect_provider_name: string;
oidc_connect_client_id: string;
oidc_connect_client_secret_configured: boolean;
oidc_connect_issuer_url: string;
oidc_connect_discovery_url: string;
oidc_connect_authorize_url: string;
oidc_connect_token_url: string;
oidc_connect_userinfo_url: string;
oidc_connect_jwks_url: string;
oidc_connect_scopes: string;
oidc_connect_redirect_url: string;
oidc_connect_frontend_redirect_url: string;
oidc_connect_token_auth_method: string;
oidc_connect_use_pkce: boolean;
oidc_connect_validate_id_token: boolean;
oidc_connect_allowed_signing_algs: string;
oidc_connect_clock_skew_seconds: number;
oidc_connect_require_email_verified: boolean;
oidc_connect_userinfo_email_path: string;
oidc_connect_userinfo_id_path: string;
oidc_connect_userinfo_username_path: string;
github_oauth_enabled: boolean;
github_oauth_client_id: string;
github_oauth_client_secret_configured: boolean;
github_oauth_redirect_url: string;
github_oauth_frontend_redirect_url: string;
google_oauth_enabled: boolean;
google_oauth_client_id: string;
google_oauth_client_secret_configured: boolean;
google_oauth_redirect_url: string;
google_oauth_frontend_redirect_url: string;
// Model fallback configuration
enable_model_fallback: boolean;
fallback_model_anthropic: string;
fallback_model_openai: string;
fallback_model_gemini: string;
fallback_model_antigravity: string;
// Identity patch configuration (Claude -> Gemini)
enable_identity_patch: boolean;
identity_patch_prompt: string;
// Ops Monitoring (vNext)
ops_monitoring_enabled: boolean;
ops_realtime_monitoring_enabled: boolean;
ops_query_mode_default: "auto" | "raw" | "preagg" | string;
ops_metrics_interval_seconds: number;
// Claude Code version check
min_claude_code_version: string;
max_claude_code_version: string;
// 分组隔离
allow_ungrouped_key_scheduling: boolean;
// Gateway forwarding behavior
enable_fingerprint_unification: boolean;
enable_metadata_passthrough: boolean;
enable_cch_signing: boolean;
enable_claude_oauth_system_prompt_injection: boolean;
claude_oauth_system_prompt: string;
claude_oauth_system_prompt_blocks: string;
enable_anthropic_cache_ttl_1h_injection: boolean;
rewrite_message_cache_control: boolean;
antigravity_user_agent_version: string;
openai_codex_user_agent: string;
// codex_cli_only 加固
min_codex_version: string;
max_codex_version: string;
codex_cli_only_blacklist: string;
codex_cli_only_whitelist: string;
codex_cli_only_allow_app_server_clients: boolean;
codex_cli_only_engine_fingerprint_signals: string;
web_search_emulation_enabled?: boolean;
// Payment configuration
payment_enabled: boolean;
risk_control_enabled: boolean;
// Cyber session block
cyber_session_block_enabled: boolean;
cyber_session_block_ttl_seconds: number;
payment_min_amount: number;
payment_max_amount: number;
payment_daily_limit: number;
payment_order_timeout_minutes: number;
payment_max_pending_orders: number;
payment_enabled_types: string[];
payment_balance_disabled: boolean;
payment_balance_recharge_multiplier: number;
payment_recharge_fee_rate: number;
payment_load_balance_strategy: string;
payment_product_name_prefix: string;
payment_product_name_suffix: string;
payment_help_image_url: string;
payment_help_text: string;
payment_cancel_rate_limit_enabled: boolean;
payment_cancel_rate_limit_max: number;
payment_cancel_rate_limit_window: number;
payment_cancel_rate_limit_unit: string;
payment_cancel_rate_limit_window_mode: string;
payment_alipay_force_qrcode?: boolean;
payment_visible_method_alipay_source?: string;
payment_visible_method_wxpay_source?: string;
payment_visible_method_alipay_enabled?: boolean;
payment_visible_method_wxpay_enabled?: boolean;
openai_advanced_scheduler_enabled?: boolean;
// 余额、订阅到期与账号限额通知
balance_low_notify_enabled: boolean;
balance_low_notify_threshold: number;
balance_low_notify_recharge_url: string;
subscription_expiry_notify_enabled: boolean;
account_quota_notify_enabled: boolean;
account_quota_notify_emails: NotifyEmailEntry[];
// Channel Monitor feature switch
channel_monitor_enabled: boolean;
channel_monitor_default_interval_seconds: number;
// Available Channels feature switch
available_channels_enabled: boolean;
// Affiliate (邀请返利) feature switch
affiliate_enabled: boolean;
// OpenAI fast/flex policy
openai_fast_policy_settings?: OpenAIFastPolicySettings;
// Allow user view error requests
allow_user_view_error_requests: boolean;
}
export interface UpdateSettingsRequest {
registration_enabled?: boolean;
email_verify_enabled?: boolean;
registration_email_suffix_whitelist?: string[];
promo_code_enabled?: boolean;
password_reset_enabled?: boolean;
frontend_url?: string;
invitation_code_enabled?: boolean;
totp_enabled?: boolean; // TOTP 双因素认证
login_agreement_enabled?: boolean;
login_agreement_mode?: "modal" | "checkbox" | string;
login_agreement_updated_at?: string;
login_agreement_documents?: LoginAgreementDocument[];
default_balance?: number;
affiliate_rebate_rate?: number;
affiliate_rebate_freeze_hours?: number;
affiliate_rebate_duration_days?: number;
affiliate_rebate_per_invitee_cap?: number;
default_concurrency?: number;
default_user_rpm_limit?: number;
default_subscriptions?: DefaultSubscriptionSetting[];
auth_source_default_email_balance?: number;
auth_source_default_email_concurrency?: number;
auth_source_default_email_subscriptions?: DefaultSubscriptionSetting[];
auth_source_default_email_grant_on_signup?: boolean;
auth_source_default_email_grant_on_first_bind?: boolean;
auth_source_default_linuxdo_balance?: number;
auth_source_default_linuxdo_concurrency?: number;
auth_source_default_linuxdo_subscriptions?: DefaultSubscriptionSetting[];
auth_source_default_linuxdo_grant_on_signup?: boolean;
auth_source_default_linuxdo_grant_on_first_bind?: boolean;
auth_source_default_oidc_balance?: number;
auth_source_default_oidc_concurrency?: number;
auth_source_default_oidc_subscriptions?: DefaultSubscriptionSetting[];
auth_source_default_oidc_grant_on_signup?: boolean;
auth_source_default_oidc_grant_on_first_bind?: boolean;
auth_source_default_wechat_balance?: number;
auth_source_default_wechat_concurrency?: number;
auth_source_default_wechat_subscriptions?: DefaultSubscriptionSetting[];
auth_source_default_wechat_grant_on_signup?: boolean;
auth_source_default_wechat_grant_on_first_bind?: boolean;
auth_source_default_dingtalk_balance?: number;
auth_source_default_dingtalk_concurrency?: number;
auth_source_default_dingtalk_subscriptions?: DefaultSubscriptionSetting[];
auth_source_default_dingtalk_grant_on_signup?: boolean;
auth_source_default_dingtalk_grant_on_first_bind?: boolean;
auth_source_default_github_balance?: number;
auth_source_default_github_concurrency?: number;
auth_source_default_github_subscriptions?: DefaultSubscriptionSetting[];
auth_source_default_github_grant_on_signup?: boolean;
auth_source_default_github_grant_on_first_bind?: boolean;
auth_source_default_google_balance?: number;
auth_source_default_google_concurrency?: number;
auth_source_default_google_subscriptions?: DefaultSubscriptionSetting[];
auth_source_default_google_grant_on_signup?: boolean;
auth_source_default_google_grant_on_first_bind?: boolean;
force_email_on_third_party_signup?: boolean;
// ── 平台限额(嵌套 JSON,系统层 + 7 auth-source 层)────────────────────────────────
default_platform_quotas?: DefaultPlatformQuotasMap;
auth_source_default_email_platform_quotas?: DefaultPlatformQuotasMap;
auth_source_default_linuxdo_platform_quotas?: DefaultPlatformQuotasMap;
auth_source_default_oidc_platform_quotas?: DefaultPlatformQuotasMap;
auth_source_default_wechat_platform_quotas?: DefaultPlatformQuotasMap;
auth_source_default_github_platform_quotas?: DefaultPlatformQuotasMap;
auth_source_default_google_platform_quotas?: DefaultPlatformQuotasMap;
auth_source_default_dingtalk_platform_quotas?: DefaultPlatformQuotasMap;
site_name?: string;
site_logo?: string;
site_subtitle?: string;
api_base_url?: string;
contact_info?: string;
doc_url?: string;
home_content?: string;
hide_ccs_import_button?: boolean;
table_default_page_size?: number;
table_page_size_options?: number[];
backend_mode_enabled?: boolean;
custom_menu_items?: CustomMenuItem[];
custom_endpoints?: CustomEndpoint[];
smtp_host?: string;
smtp_port?: number;
smtp_username?: string;
smtp_password?: string;
smtp_from_email?: string;
smtp_from_name?: string;
smtp_use_tls?: boolean;
turnstile_enabled?: boolean;
turnstile_site_key?: string;
turnstile_secret_key?: string;
api_key_acl_trust_forwarded_ip?: boolean;
linuxdo_connect_enabled?: boolean;
linuxdo_connect_client_id?: string;
linuxdo_connect_client_secret?: string;
linuxdo_connect_redirect_url?: string;
dingtalk_connect_enabled?: boolean;
dingtalk_connect_client_id?: string;
dingtalk_connect_client_secret?: string;
dingtalk_connect_redirect_url?: string;
dingtalk_connect_corp_restriction_policy?: string;
dingtalk_connect_internal_corp_id?: string;
dingtalk_connect_bypass_registration?: boolean;
dingtalk_connect_sync_corp_email?: boolean;
dingtalk_connect_sync_display_name?: boolean;
dingtalk_connect_sync_dept?: boolean;
dingtalk_connect_sync_corp_email_attr_key?: string;
dingtalk_connect_sync_display_name_attr_key?: string;
dingtalk_connect_sync_dept_attr_key?: string;
dingtalk_connect_sync_corp_email_attr_name?: string;
dingtalk_connect_sync_display_name_attr_name?: string;
dingtalk_connect_sync_dept_attr_name?: string;
wechat_connect_enabled?: boolean;
wechat_connect_app_id?: string;
wechat_connect_app_secret?: string;
wechat_connect_open_app_id?: string;
wechat_connect_open_app_secret?: string;
wechat_connect_mp_app_id?: string;
wechat_connect_mp_app_secret?: string;
wechat_connect_mobile_app_id?: string;
wechat_connect_mobile_app_secret?: string;
wechat_connect_open_enabled?: boolean;
wechat_connect_mp_enabled?: boolean;
wechat_connect_mobile_enabled?: boolean;
wechat_connect_mode?: string;
wechat_connect_scopes?: string;
wechat_connect_redirect_url?: string;
wechat_connect_frontend_redirect_url?: string;
oidc_connect_enabled?: boolean;
oidc_connect_provider_name?: string;
oidc_connect_client_id?: string;
oidc_connect_client_secret?: string;
oidc_connect_issuer_url?: string;
oidc_connect_discovery_url?: string;
oidc_connect_authorize_url?: string;
oidc_connect_token_url?: string;
oidc_connect_userinfo_url?: string;
oidc_connect_jwks_url?: string;
oidc_connect_scopes?: string;
oidc_connect_redirect_url?: string;
oidc_connect_frontend_redirect_url?: string;
oidc_connect_token_auth_method?: string;
oidc_connect_use_pkce?: boolean;
oidc_connect_validate_id_token?: boolean;
oidc_connect_allowed_signing_algs?: string;
oidc_connect_clock_skew_seconds?: number;
oidc_connect_require_email_verified?: boolean;
oidc_connect_userinfo_email_path?: string;
oidc_connect_userinfo_id_path?: string;
oidc_connect_userinfo_username_path?: string;
github_oauth_enabled?: boolean;
github_oauth_client_id?: string;
github_oauth_client_secret?: string;
github_oauth_redirect_url?: string;
github_oauth_frontend_redirect_url?: string;
google_oauth_enabled?: boolean;
google_oauth_client_id?: string;
google_oauth_client_secret?: string;
google_oauth_redirect_url?: string;
google_oauth_frontend_redirect_url?: string;
enable_model_fallback?: boolean;
fallback_model_anthropic?: string;
fallback_model_openai?: string;
fallback_model_gemini?: string;
fallback_model_antigravity?: string;
enable_identity_patch?: boolean;
identity_patch_prompt?: string;
ops_monitoring_enabled?: boolean;
ops_realtime_monitoring_enabled?: boolean;
ops_query_mode_default?: "auto" | "raw" | "preagg" | string;
ops_metrics_interval_seconds?: number;
min_claude_code_version?: string;
max_claude_code_version?: string;
allow_ungrouped_key_scheduling?: boolean;
enable_fingerprint_unification?: boolean;
enable_metadata_passthrough?: boolean;
enable_cch_signing?: boolean;
enable_claude_oauth_system_prompt_injection?: boolean;
claude_oauth_system_prompt?: string;
claude_oauth_system_prompt_blocks?: string;
enable_anthropic_cache_ttl_1h_injection?: boolean;
rewrite_message_cache_control?: boolean;
antigravity_user_agent_version?: string;
openai_codex_user_agent?: string;
// codex_cli_only 加固
min_codex_version?: string;
max_codex_version?: string;
codex_cli_only_blacklist?: string;
codex_cli_only_whitelist?: string;
codex_cli_only_allow_app_server_clients?: boolean;
codex_cli_only_engine_fingerprint_signals?: string;
// Payment configuration
payment_enabled?: boolean;
risk_control_enabled?: boolean;
// Cyber session block
cyber_session_block_enabled?: boolean;
cyber_session_block_ttl_seconds?: number;
payment_min_amount?: number;
payment_max_amount?: number;
payment_daily_limit?: number;
payment_order_timeout_minutes?: number;
payment_max_pending_orders?: number;
payment_enabled_types?: string[];
payment_balance_disabled?: boolean;
payment_balance_recharge_multiplier?: number;
payment_recharge_fee_rate?: number;
payment_load_balance_strategy?: string;
payment_product_name_prefix?: string;
payment_product_name_suffix?: string;
payment_help_image_url?: string;
payment_help_text?: string;
payment_cancel_rate_limit_enabled?: boolean;
payment_cancel_rate_limit_max?: number;
payment_cancel_rate_limit_window?: number;
payment_cancel_rate_limit_unit?: string;
payment_cancel_rate_limit_window_mode?: string;
payment_alipay_force_qrcode?: boolean;
payment_visible_method_alipay_source?: string;
payment_visible_method_wxpay_source?: string;
payment_visible_method_alipay_enabled?: boolean;
payment_visible_method_wxpay_enabled?: boolean;
openai_advanced_scheduler_enabled?: boolean;
// 余额、订阅到期与账号限额通知
balance_low_notify_enabled?: boolean;
balance_low_notify_threshold?: number;
balance_low_notify_recharge_url?: string;
subscription_expiry_notify_enabled?: boolean;
account_quota_notify_enabled?: boolean;
account_quota_notify_emails?: NotifyEmailEntry[];
// Channel Monitor feature switch
channel_monitor_enabled?: boolean;
channel_monitor_default_interval_seconds?: number;
// Available Channels feature switch
available_channels_enabled?: boolean;
// Affiliate (邀请返利) feature switch
affiliate_enabled?: boolean;
// OpenAI fast/flex policy
openai_fast_policy_settings?: OpenAIFastPolicySettings;
allow_user_view_error_requests?: boolean;
}
/**
* Get all system settings
* @returns System settings
*/
export async function getSettings(): Promise<SystemSettings> {
const { data } = await apiClient.get<SystemSettings>("/admin/settings");
return data;
}
/**
* Update system settings
* @param settings - Partial settings to update
* @returns Updated settings
*/
export async function updateSettings(
settings: UpdateSettingsRequest,
): Promise<SystemSettings> {
const { data } = await apiClient.put<SystemSettings>(
"/admin/settings",
settings,
);
return data;
}
/**
* Test SMTP connection request
*/
export interface TestSmtpRequest {
smtp_host: string;
smtp_port: number;
smtp_username: string;
smtp_password: string;
smtp_use_tls: boolean;
}
/**
* Test SMTP connection with provided config
* @param config - SMTP configuration to test
* @returns Test result message
*/
export async function testSmtpConnection(
config: TestSmtpRequest,
): Promise<{ message: string }> {
const { data } = await apiClient.post<{ message: string }>(
"/admin/settings/test-smtp",
config,
);
return data;
}
/**
* Send test email request
*/
export interface SendTestEmailRequest {
email: string;
smtp_host: string;
smtp_port: number;
smtp_username: string;
smtp_password: string;
smtp_from_email: string;
smtp_from_name: string;
smtp_use_tls: boolean;
}
/**
* Send test email with provided SMTP config
* @param request - Email address and SMTP config
* @returns Test result message
*/
export async function sendTestEmail(
request: SendTestEmailRequest,
): Promise<{ message: string }> {
const { data } = await apiClient.post<{ message: string }>(
"/admin/settings/send-test-email",
request,
);
return data;
}
// ==================== Email Template Settings ====================
export interface EmailTemplateOption {
value: string;
label?: string;
description?: string;
category?: string;
optional?: boolean;
}
export type EmailTemplateEventOption = string | EmailTemplateOption;
export interface EmailTemplateSummary {
event: string;
locale: string;
subject: string;
is_custom?: boolean;
updated_at?: string;
}
export interface EmailTemplateListResponse {
events: EmailTemplateEventOption[];
locales: string[];
templates?: EmailTemplateSummary[];
placeholders?: string[];
}
export interface EmailTemplateDetail {
event: string;
locale: string;
subject: string;
html: string;
is_custom?: boolean;
updated_at?: string;
placeholders?: string[];
}
export interface UpdateEmailTemplateRequest {
subject: string;
html: string;
}
export interface PreviewEmailTemplateRequest extends UpdateEmailTemplateRequest {
event: string;
locale: string;
}
export interface EmailTemplatePreviewResponse {
subject: string;
html: string;
}
export async function getEmailTemplates(): Promise<EmailTemplateListResponse> {
const { data } = await apiClient.get<EmailTemplateListResponse>(
"/admin/settings/email-templates",
);
return data;
}
export async function getEmailTemplate(
event: string,
locale: string,
): Promise<EmailTemplateDetail> {
const { data } = await apiClient.get<EmailTemplateDetail>(
`/admin/settings/email-templates/${encodeURIComponent(event)}/${encodeURIComponent(locale)}`,
);
return data;
}
export async function updateEmailTemplate(
event: string,
locale: string,
request: UpdateEmailTemplateRequest,
): Promise<EmailTemplateDetail> {
const { data } = await apiClient.put<EmailTemplateDetail>(
`/admin/settings/email-templates/${encodeURIComponent(event)}/${encodeURIComponent(locale)}`,
request,
);
return data;
}
export async function restoreOfficialEmailTemplate(
event: string,
locale: string,
): Promise<EmailTemplateDetail> {
const { data } = await apiClient.post<EmailTemplateDetail>(
`/admin/settings/email-templates/${encodeURIComponent(event)}/${encodeURIComponent(locale)}/restore-official`,
);
return data;
}
export async function previewEmailTemplate(
request: PreviewEmailTemplateRequest,
): Promise<EmailTemplatePreviewResponse> {
const { data } = await apiClient.post<EmailTemplatePreviewResponse>(
"/admin/settings/email-template-preview",
request,
);
return data;
}
/**
* Admin API Key status response
*/
export interface AdminApiKeyStatus {
exists: boolean;
masked_key: string;
}
/**
* Get admin API key status
* @returns Status indicating if key exists and masked version
*/
export async function getAdminApiKey(): Promise<AdminApiKeyStatus> {
const { data } = await apiClient.get<AdminApiKeyStatus>(
"/admin/settings/admin-api-key",
);
return data;
}
/**
* Regenerate admin API key
* @returns The new full API key (only shown once)
*/
export async function regenerateAdminApiKey(): Promise<{ key: string }> {
const { data } = await apiClient.post<{ key: string }>(
"/admin/settings/admin-api-key/regenerate",
);
return data;
}
/**
* Delete admin API key
* @returns Success message
*/
export async function deleteAdminApiKey(): Promise<{ message: string }> {
const { data } = await apiClient.delete<{ message: string }>(
"/admin/settings/admin-api-key",
);
return data;
}
// ==================== Overload Cooldown Settings ====================
/**
* Overload cooldown settings interface (529 handling)
*/
export interface OverloadCooldownSettings {
enabled: boolean;
cooldown_minutes: number;
}
export async function getOverloadCooldownSettings(): Promise<OverloadCooldownSettings> {
const { data } = await apiClient.get<OverloadCooldownSettings>(
"/admin/settings/overload-cooldown",
);
return data;
}
export async function updateOverloadCooldownSettings(
settings: OverloadCooldownSettings,
): Promise<OverloadCooldownSettings> {
const { data } = await apiClient.put<OverloadCooldownSettings>(
"/admin/settings/overload-cooldown",
settings,
);
return data;
}
// ==================== 429 Rate Limit Cooldown Settings ====================
export interface RateLimit429CooldownSettings {
enabled: boolean;
cooldown_seconds: number;
}
export async function getRateLimit429CooldownSettings(): Promise<RateLimit429CooldownSettings> {
const { data } = await apiClient.get<RateLimit429CooldownSettings>(
"/admin/settings/rate-limit-429-cooldown",
);
return data;
}
export async function updateRateLimit429CooldownSettings(
settings: RateLimit429CooldownSettings,
): Promise<RateLimit429CooldownSettings> {
const { data } = await apiClient.put<RateLimit429CooldownSettings>(
"/admin/settings/rate-limit-429-cooldown",
settings,
);
return data;
}
// ==================== Stream Timeout Settings ====================
/**
* Stream timeout settings interface
*/
export interface StreamTimeoutSettings {
enabled: boolean;
action: "temp_unsched" | "error" | "none";
temp_unsched_minutes: number;
threshold_count: number;
threshold_window_minutes: number;
}
/**
* Get stream timeout settings
* @returns Stream timeout settings
*/
export async function getStreamTimeoutSettings(): Promise<StreamTimeoutSettings> {
const { data } = await apiClient.get<StreamTimeoutSettings>(
"/admin/settings/stream-timeout",
);
return data;
}
/**
* Update stream timeout settings
* @param settings - Stream timeout settings to update
* @returns Updated settings
*/
export async function updateStreamTimeoutSettings(
settings: StreamTimeoutSettings,
): Promise<StreamTimeoutSettings> {
const { data } = await apiClient.put<StreamTimeoutSettings>(
"/admin/settings/stream-timeout",
settings,
);
return data;
}
// ==================== Rectifier Settings ====================
/**
* Rectifier settings interface
*/
export interface RectifierSettings {
enabled: boolean;
thinking_signature_enabled: boolean;
thinking_budget_enabled: boolean;
apikey_signature_enabled: boolean;
apikey_signature_patterns: string[];
}
/**
* Get rectifier settings
* @returns Rectifier settings
*/
export async function getRectifierSettings(): Promise<RectifierSettings> {
const { data } = await apiClient.get<RectifierSettings>(
"/admin/settings/rectifier",
);
return data;
}
/**
* Update rectifier settings
* @param settings - Rectifier settings to update
* @returns Updated settings
*/
export async function updateRectifierSettings(
settings: RectifierSettings,
): Promise<RectifierSettings> {
const { data } = await apiClient.put<RectifierSettings>(
"/admin/settings/rectifier",
settings,
);
return data;
}
// ==================== OpenAI Fast Policy Settings ====================
/**
* OpenAI fast/flex policy rule interface.
* Matches backend dto.OpenAIFastPolicyRule.
*/
export interface OpenAIFastPolicyRule {
service_tier: "all" | "priority" | "flex";
action: "pass" | "filter" | "block";
scope: "all" | "oauth" | "apikey" | "bedrock";
error_message?: string;
model_whitelist?: string[];
fallback_action?: "pass" | "filter" | "block";
fallback_error_message?: string;
}
/**
* OpenAI fast/flex policy settings interface.
*/
export interface OpenAIFastPolicySettings {
rules: OpenAIFastPolicyRule[];
}
// ==================== Beta Policy Settings ====================
/**
* Beta policy rule interface
*/
export interface BetaPolicyRule {
beta_token: string;
action: "pass" | "filter" | "block";
scope: "all" | "oauth" | "apikey" | "bedrock";
error_message?: string;
model_whitelist?: string[];
fallback_action?: "pass" | "filter" | "block";
fallback_error_message?: string;
}
/**
* Beta policy settings interface
*/
export interface BetaPolicySettings {
rules: BetaPolicyRule[];
}
/**
* Get beta policy settings
* @returns Beta policy settings
*/
export async function getBetaPolicySettings(): Promise<BetaPolicySettings> {
const { data } = await apiClient.get<BetaPolicySettings>(
"/admin/settings/beta-policy",
);
return data;
}
/**
* Update beta policy settings
* @param settings - Beta policy settings to update
* @returns Updated settings
*/
export async function updateBetaPolicySettings(
settings: BetaPolicySettings,
): Promise<BetaPolicySettings> {
const { data } = await apiClient.put<BetaPolicySettings>(
"/admin/settings/beta-policy",
settings,
);
return data;
}
// --- Web Search Emulation Config ---
export interface WebSearchProviderConfig {
type: "brave" | "tavily";
api_key: string;
api_key_configured: boolean;
quota_limit: number | null;
subscribed_at: number | null;
quota_used?: number;
proxy_id: number | null;
expires_at: number | null;
}
export interface WebSearchEmulationConfig {
enabled: boolean;
providers: WebSearchProviderConfig[];
}
export interface WebSearchTestResult {
provider: string;
results: { url: string; title: string; snippet: string; page_age?: string }[];
query: string;
}
export async function getWebSearchEmulationConfig(): Promise<WebSearchEmulationConfig> {
const { data } = await apiClient.get<WebSearchEmulationConfig>(
"/admin/settings/web-search-emulation",
);
return data;
}
export async function updateWebSearchEmulationConfig(
config: WebSearchEmulationConfig,
): Promise<WebSearchEmulationConfig> {
const { data } = await apiClient.put<WebSearchEmulationConfig>(
"/admin/settings/web-search-emulation",
config,
);
return data;
}
export async function testWebSearchEmulation(
query: string,
): Promise<WebSearchTestResult> {
const { data } = await apiClient.post<WebSearchTestResult>(
"/admin/settings/web-search-emulation/test",
{ query },
);
return data;
}
export async function resetWebSearchUsage(payload: {
provider_type: string;
}): Promise<void> {
await apiClient.post(
"/admin/settings/web-search-emulation/reset-usage",
payload,
);
}
export const settingsAPI = {
getSettings,
updateSettings,
testSmtpConnection,
sendTestEmail,
getEmailTemplates,
getEmailTemplate,
updateEmailTemplate,
restoreOfficialEmailTemplate,
previewEmailTemplate,
getAdminApiKey,
regenerateAdminApiKey,
deleteAdminApiKey,
getOverloadCooldownSettings,
updateOverloadCooldownSettings,
getRateLimit429CooldownSettings,
updateRateLimit429CooldownSettings,
getStreamTimeoutSettings,
updateStreamTimeoutSettings,
getRectifierSettings,
updateRectifierSettings,
getBetaPolicySettings,
updateBetaPolicySettings,
getWebSearchEmulationConfig,
updateWebSearchEmulationConfig,
testWebSearchEmulation,
resetWebSearchUsage,
};
export default settingsAPI;