2025-12-18 13:50:39 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* Admin Settings API endpoints
|
|
|
|
|
|
* Handles system settings management for administrators
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2026-04-21 17:35:12 +08:00
|
|
|
|
import { apiClient } from "../client";
|
2026-05-07 17:35:05 +08:00
|
|
|
|
import type {
|
|
|
|
|
|
CustomEndpoint,
|
|
|
|
|
|
CustomMenuItem,
|
|
|
|
|
|
LoginAgreementDocument,
|
|
|
|
|
|
NotifyEmailEntry,
|
|
|
|
|
|
} from "@/types";
|
2025-12-18 13:50:39 +08:00
|
|
|
|
|
2026-03-02 03:41:50 +08:00
|
|
|
|
export interface DefaultSubscriptionSetting {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
group_id: number;
|
|
|
|
|
|
validity_days: number;
|
2026-03-02 03:41:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-25 14:26:11 +08:00
|
|
|
|
// ── 平台限额类型 ──────────────────────────────────────────────────
|
2026-06-16 18:38:07 +08:00
|
|
|
|
export type PlatformType = "anthropic" | "openai" | "gemini" | "antigravity" | "grok"
|
2026-05-25 14:26:11 +08:00
|
|
|
|
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>>
|
|
|
|
|
|
|
2026-06-16 18:38:07 +08:00
|
|
|
|
const PLATFORMS: PlatformType[] = ["anthropic", "openai", "gemini", "antigravity", "grok"]
|
2026-05-25 14:26:11 +08:00
|
|
|
|
|
2026-08-07 14:53:18 +08:00
|
|
|
|
export type SchedulingThresholdPlatformType =
|
|
|
|
|
|
| "openai"
|
|
|
|
|
|
| "anthropic"
|
|
|
|
|
|
| "grok"
|
2026-08-15 07:14:19 +00:00
|
|
|
|
| "kimi"
|
|
|
|
|
|
| "zhipu"
|
2026-09-07 18:22:56 +08:00
|
|
|
|
| "minimax"
|
2026-08-07 14:53:18 +08:00
|
|
|
|
|
|
|
|
|
|
export type AccountSchedulingThresholdsMap = Record<SchedulingThresholdPlatformType, number>
|
|
|
|
|
|
|
2026-08-15 07:14:19 +00:00
|
|
|
|
// 与后端 AllowedSchedulingThresholdPlatforms 保持一致(deepseek 为余额型,
|
2026-09-07 18:22:56 +08:00
|
|
|
|
// 走余额检测而非用量阈值;minimax Coding/Token Plan 有 5h/weekly 窗口)。
|
2026-08-07 14:53:18 +08:00
|
|
|
|
export const SCHEDULING_THRESHOLD_PLATFORMS: SchedulingThresholdPlatformType[] = [
|
|
|
|
|
|
"openai",
|
|
|
|
|
|
"anthropic",
|
|
|
|
|
|
"grok",
|
2026-08-15 07:14:19 +00:00
|
|
|
|
"kimi",
|
|
|
|
|
|
"zhipu",
|
2026-09-07 18:22:56 +08:00
|
|
|
|
"minimax",
|
2026-08-07 14:53:18 +08:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
export function normalizeAccountSchedulingThresholdsMap(
|
|
|
|
|
|
input?: Partial<Record<SchedulingThresholdPlatformType, number>> | null,
|
|
|
|
|
|
): AccountSchedulingThresholdsMap {
|
|
|
|
|
|
const result = {} as AccountSchedulingThresholdsMap
|
|
|
|
|
|
for (const platform of SCHEDULING_THRESHOLD_PLATFORMS) {
|
|
|
|
|
|
const value = input?.[platform]
|
|
|
|
|
|
result[platform] = typeof value === "number" && Number.isFinite(value)
|
|
|
|
|
|
? Math.min(100, Math.max(1, Math.trunc(value)))
|
|
|
|
|
|
: 100
|
|
|
|
|
|
}
|
|
|
|
|
|
return result
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function sanitizeAccountSchedulingThresholdsMap(
|
|
|
|
|
|
input?: Partial<Record<SchedulingThresholdPlatformType, number>> | null,
|
|
|
|
|
|
): AccountSchedulingThresholdsMap {
|
|
|
|
|
|
return normalizeAccountSchedulingThresholdsMap(input)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-25 14:26:11 +08:00
|
|
|
|
/** 归一化为全 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
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-06 16:06:11 +08:00
|
|
|
|
export type AuthSourceType =
|
|
|
|
|
|
| "email"
|
|
|
|
|
|
| "linuxdo"
|
|
|
|
|
|
| "oidc"
|
|
|
|
|
|
| "wechat"
|
|
|
|
|
|
| "github"
|
2026-05-15 11:46:29 +08:00
|
|
|
|
| "google"
|
|
|
|
|
|
| "dingtalk";
|
2026-04-20 17:39:57 +08:00
|
|
|
|
|
|
|
|
|
|
export interface AuthSourceDefaultsValue {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
balance: number;
|
|
|
|
|
|
concurrency: number;
|
|
|
|
|
|
subscriptions: DefaultSubscriptionSetting[];
|
|
|
|
|
|
grant_on_signup: boolean;
|
|
|
|
|
|
grant_on_first_bind: boolean;
|
2026-05-25 14:26:11 +08:00
|
|
|
|
// ★ 新增:平台限额覆盖(key = PlatformType)
|
|
|
|
|
|
platform_quotas: DefaultPlatformQuotasMap;
|
2026-04-20 17:39:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-21 17:35:12 +08:00
|
|
|
|
export type AuthSourceDefaultsState = Record<
|
|
|
|
|
|
AuthSourceType,
|
|
|
|
|
|
AuthSourceDefaultsValue
|
|
|
|
|
|
>;
|
|
|
|
|
|
export type PaymentVisibleMethod = "alipay" | "wxpay";
|
2026-04-21 00:03:27 +08:00
|
|
|
|
export type PaymentVisibleMethodSource =
|
2026-04-21 17:35:12 +08:00
|
|
|
|
| ""
|
|
|
|
|
|
| "official_alipay"
|
|
|
|
|
|
| "easypay_alipay"
|
|
|
|
|
|
| "official_wxpay"
|
|
|
|
|
|
| "easypay_wxpay";
|
2026-04-21 07:48:42 -07:00
|
|
|
|
export type WeChatConnectMode = "open" | "mp" | "mobile";
|
2026-04-21 00:03:27 +08:00
|
|
|
|
|
|
|
|
|
|
export interface PaymentVisibleMethodSourceOption {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
value: PaymentVisibleMethodSource;
|
|
|
|
|
|
labelZh: string;
|
|
|
|
|
|
labelEn: string;
|
2026-04-21 00:03:27 +08:00
|
|
|
|
}
|
2026-04-20 17:39:57 +08:00
|
|
|
|
|
2026-04-21 17:35:12 +08:00
|
|
|
|
export interface WeChatConnectModeOption {
|
|
|
|
|
|
value: WeChatConnectMode;
|
|
|
|
|
|
labelZh: string;
|
|
|
|
|
|
labelEn: string;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const AUTH_SOURCE_TYPES: AuthSourceType[] = [
|
|
|
|
|
|
"email",
|
|
|
|
|
|
"linuxdo",
|
|
|
|
|
|
"oidc",
|
|
|
|
|
|
"wechat",
|
2026-05-06 16:06:11 +08:00
|
|
|
|
"github",
|
|
|
|
|
|
"google",
|
2026-05-15 11:46:29 +08:00
|
|
|
|
"dingtalk",
|
2026-04-21 17:35:12 +08:00
|
|
|
|
];
|
|
|
|
|
|
const AUTH_SOURCE_DEFAULT_BALANCE = 0;
|
|
|
|
|
|
const AUTH_SOURCE_DEFAULT_CONCURRENCY = 5;
|
2026-04-21 00:03:27 +08:00
|
|
|
|
const PAYMENT_VISIBLE_METHOD_SOURCE_OPTIONS: Record<
|
|
|
|
|
|
PaymentVisibleMethod,
|
|
|
|
|
|
PaymentVisibleMethodSourceOption[]
|
|
|
|
|
|
> = {
|
|
|
|
|
|
alipay: [
|
2026-04-21 17:35:12 +08:00
|
|
|
|
{ value: "", labelZh: "未配置", labelEn: "Not configured" },
|
|
|
|
|
|
{
|
|
|
|
|
|
value: "official_alipay",
|
|
|
|
|
|
labelZh: "支付宝官方",
|
|
|
|
|
|
labelEn: "Official Alipay",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
value: "easypay_alipay",
|
|
|
|
|
|
labelZh: "易支付支付宝",
|
|
|
|
|
|
labelEn: "EasyPay Alipay",
|
|
|
|
|
|
},
|
2026-04-21 00:03:27 +08:00
|
|
|
|
],
|
|
|
|
|
|
wxpay: [
|
2026-04-21 17:35:12 +08:00
|
|
|
|
{ value: "", labelZh: "未配置", labelEn: "Not configured" },
|
|
|
|
|
|
{
|
|
|
|
|
|
value: "official_wxpay",
|
|
|
|
|
|
labelZh: "微信官方",
|
|
|
|
|
|
labelEn: "Official WeChat Pay",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
value: "easypay_wxpay",
|
|
|
|
|
|
labelZh: "易支付微信",
|
|
|
|
|
|
labelEn: "EasyPay WeChat Pay",
|
|
|
|
|
|
},
|
2026-04-21 00:03:27 +08:00
|
|
|
|
],
|
2026-04-21 17:35:12 +08:00
|
|
|
|
};
|
2026-04-21 00:03:27 +08:00
|
|
|
|
const PAYMENT_VISIBLE_METHOD_SOURCE_ALIASES: Record<
|
|
|
|
|
|
PaymentVisibleMethod,
|
|
|
|
|
|
Record<string, PaymentVisibleMethodSource>
|
|
|
|
|
|
> = {
|
|
|
|
|
|
alipay: {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
official_alipay: "official_alipay",
|
|
|
|
|
|
alipay: "official_alipay",
|
|
|
|
|
|
alipay_direct: "official_alipay",
|
|
|
|
|
|
official: "official_alipay",
|
|
|
|
|
|
easypay_alipay: "easypay_alipay",
|
|
|
|
|
|
easypay: "easypay_alipay",
|
2026-04-21 00:03:27 +08:00
|
|
|
|
},
|
|
|
|
|
|
wxpay: {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
official_wxpay: "official_wxpay",
|
|
|
|
|
|
wxpay: "official_wxpay",
|
|
|
|
|
|
wxpay_direct: "official_wxpay",
|
|
|
|
|
|
wechat: "official_wxpay",
|
|
|
|
|
|
official: "official_wxpay",
|
|
|
|
|
|
easypay_wxpay: "easypay_wxpay",
|
|
|
|
|
|
easypay: "easypay_wxpay",
|
2026-04-21 00:03:27 +08:00
|
|
|
|
},
|
2026-04-21 17:35:12 +08:00
|
|
|
|
};
|
|
|
|
|
|
const WECHAT_CONNECT_MODE_OPTIONS: WeChatConnectModeOption[] = [
|
2026-04-21 07:48:42 -07:00
|
|
|
|
{ value: "open", labelZh: "PC 应用", labelEn: "PC App" },
|
2026-04-21 17:35:12 +08:00
|
|
|
|
{
|
|
|
|
|
|
value: "mp",
|
2026-04-21 07:48:42 -07:00
|
|
|
|
labelZh: "公众号",
|
|
|
|
|
|
labelEn: "Official Account",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
value: "mobile",
|
|
|
|
|
|
labelZh: "移动应用",
|
|
|
|
|
|
labelEn: "Mobile App",
|
2026-04-21 17:35:12 +08:00
|
|
|
|
},
|
|
|
|
|
|
];
|
|
|
|
|
|
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",
|
2026-04-21 07:48:42 -07:00
|
|
|
|
mobile: "mobile",
|
|
|
|
|
|
mobile_app: "mobile",
|
|
|
|
|
|
native_app: "mobile",
|
2026-04-21 17:35:12 +08:00
|
|
|
|
};
|
2026-04-20 17:39:57 +08:00
|
|
|
|
|
|
|
|
|
|
export function normalizeDefaultSubscriptionSettings(
|
2026-04-21 17:35:12 +08:00
|
|
|
|
subscriptions: DefaultSubscriptionSetting[] | null | undefined,
|
2026-04-20 17:39:57 +08:00
|
|
|
|
): DefaultSubscriptionSetting[] {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
if (!Array.isArray(subscriptions)) return [];
|
2026-04-20 17:39:57 +08:00
|
|
|
|
|
|
|
|
|
|
return subscriptions
|
|
|
|
|
|
.filter((item) => item.group_id > 0 && item.validity_days > 0)
|
|
|
|
|
|
.map((item) => ({
|
|
|
|
|
|
group_id: Math.floor(item.group_id),
|
2026-04-21 17:35:12 +08:00
|
|
|
|
validity_days: Math.min(
|
|
|
|
|
|
36500,
|
|
|
|
|
|
Math.max(1, Math.floor(item.validity_days)),
|
|
|
|
|
|
),
|
|
|
|
|
|
}));
|
2026-04-20 17:39:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function buildAuthSourceDefaultsState(
|
2026-04-21 17:35:12 +08:00
|
|
|
|
settings: Partial<SystemSettings>,
|
2026-04-20 17:39:57 +08:00
|
|
|
|
): AuthSourceDefaultsState {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
const raw = settings as Record<string, unknown>;
|
2026-04-20 17:39:57 +08:00
|
|
|
|
|
|
|
|
|
|
return AUTH_SOURCE_TYPES.reduce((acc, source) => {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
const subscriptions = raw[`auth_source_default_${source}_subscriptions`];
|
2026-04-20 17:39:57 +08:00
|
|
|
|
acc[source] = {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
balance: Number(
|
|
|
|
|
|
raw[`auth_source_default_${source}_balance`] ??
|
|
|
|
|
|
AUTH_SOURCE_DEFAULT_BALANCE,
|
|
|
|
|
|
),
|
2026-04-20 17:39:57 +08:00
|
|
|
|
concurrency: Math.max(
|
|
|
|
|
|
1,
|
2026-04-21 17:35:12 +08:00
|
|
|
|
Number(
|
|
|
|
|
|
raw[`auth_source_default_${source}_concurrency`] ??
|
|
|
|
|
|
AUTH_SOURCE_DEFAULT_CONCURRENCY,
|
|
|
|
|
|
),
|
2026-04-20 17:39:57 +08:00
|
|
|
|
),
|
|
|
|
|
|
subscriptions: normalizeDefaultSubscriptionSettings(
|
2026-04-21 17:35:12 +08:00
|
|
|
|
Array.isArray(subscriptions)
|
|
|
|
|
|
? (subscriptions as DefaultSubscriptionSetting[])
|
|
|
|
|
|
: [],
|
2026-04-20 17:39:57 +08:00
|
|
|
|
),
|
2026-04-21 17:35:12 +08:00
|
|
|
|
grant_on_signup:
|
2026-04-21 20:36:10 +08:00
|
|
|
|
raw[`auth_source_default_${source}_grant_on_signup`] === true,
|
2026-04-21 17:35:12 +08:00
|
|
|
|
grant_on_first_bind:
|
|
|
|
|
|
raw[`auth_source_default_${source}_grant_on_first_bind`] === true,
|
2026-05-25 14:26:11 +08:00
|
|
|
|
platform_quotas: normalizePlatformQuotasMap(raw[`auth_source_default_${source}_platform_quotas`] as DefaultPlatformQuotasMap | undefined),
|
2026-04-21 17:35:12 +08:00
|
|
|
|
};
|
|
|
|
|
|
return acc;
|
|
|
|
|
|
}, {} as AuthSourceDefaultsState);
|
2026-04-20 17:39:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function appendAuthSourceDefaultsToUpdateRequest(
|
|
|
|
|
|
payload: UpdateSettingsRequest,
|
2026-04-21 17:35:12 +08:00
|
|
|
|
authSourceDefaults: AuthSourceDefaultsState,
|
2026-04-20 17:39:57 +08:00
|
|
|
|
): UpdateSettingsRequest {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
const target = payload as Record<string, unknown>;
|
2026-04-20 17:39:57 +08:00
|
|
|
|
|
|
|
|
|
|
for (const source of AUTH_SOURCE_TYPES) {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
const current = authSourceDefaults[source];
|
|
|
|
|
|
target[`auth_source_default_${source}_balance`] =
|
|
|
|
|
|
Number(current.balance) || 0;
|
2026-04-20 17:39:57 +08:00
|
|
|
|
target[`auth_source_default_${source}_concurrency`] = Math.max(
|
|
|
|
|
|
1,
|
2026-04-21 17:35:12 +08:00
|
|
|
|
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;
|
2026-05-25 14:26:11 +08:00
|
|
|
|
target[`auth_source_default_${source}_platform_quotas`] = sanitizePlatformQuotasMap(current.platform_quotas)
|
2026-04-20 17:39:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-21 17:35:12 +08:00
|
|
|
|
return payload;
|
2026-04-20 17:39:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-21 00:03:27 +08:00
|
|
|
|
export function getPaymentVisibleMethodSourceOptions(
|
2026-04-21 17:35:12 +08:00
|
|
|
|
method: PaymentVisibleMethod,
|
2026-04-21 00:03:27 +08:00
|
|
|
|
): PaymentVisibleMethodSourceOption[] {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
return PAYMENT_VISIBLE_METHOD_SOURCE_OPTIONS[method];
|
2026-04-21 00:03:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function normalizePaymentVisibleMethodSource(
|
|
|
|
|
|
method: PaymentVisibleMethod,
|
2026-04-21 17:35:12 +08:00
|
|
|
|
source: unknown,
|
2026-04-21 00:03:27 +08:00
|
|
|
|
): PaymentVisibleMethodSource {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
if (typeof source !== "string") return "";
|
2026-04-21 00:03:27 +08:00
|
|
|
|
|
2026-04-21 17:35:12 +08:00
|
|
|
|
const normalized = source.trim().toLowerCase();
|
|
|
|
|
|
if (!normalized) return "";
|
2026-04-21 00:03:27 +08:00
|
|
|
|
|
2026-04-21 17:35:12 +08:00
|
|
|
|
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 {
|
2026-04-21 07:48:42 -07:00
|
|
|
|
switch (normalizeWeChatConnectMode(mode)) {
|
|
|
|
|
|
case "mp":
|
|
|
|
|
|
return "snsapi_userinfo";
|
|
|
|
|
|
case "mobile":
|
|
|
|
|
|
return "";
|
|
|
|
|
|
default:
|
|
|
|
|
|
return "snsapi_login";
|
|
|
|
|
|
}
|
2026-04-21 00:03:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-21 20:36:10 +08:00
|
|
|
|
export function resolveWeChatConnectModeCapabilities(
|
|
|
|
|
|
openEnabled: unknown,
|
|
|
|
|
|
mpEnabled: unknown,
|
2026-04-21 07:48:42 -07:00
|
|
|
|
mobileEnabled: unknown,
|
2026-04-21 20:36:10 +08:00
|
|
|
|
legacyMode: unknown,
|
2026-04-21 07:48:42 -07:00
|
|
|
|
): { openEnabled: boolean; mpEnabled: boolean; mobileEnabled: boolean } {
|
|
|
|
|
|
if (
|
|
|
|
|
|
typeof openEnabled === "boolean" ||
|
|
|
|
|
|
typeof mpEnabled === "boolean" ||
|
|
|
|
|
|
typeof mobileEnabled === "boolean"
|
|
|
|
|
|
) {
|
2026-04-21 20:36:10 +08:00
|
|
|
|
return {
|
|
|
|
|
|
openEnabled: openEnabled === true,
|
|
|
|
|
|
mpEnabled: mpEnabled === true,
|
2026-04-21 07:48:42 -07:00
|
|
|
|
mobileEnabled: mobileEnabled === true,
|
2026-04-21 20:36:10 +08:00
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-21 07:48:42 -07:00
|
|
|
|
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 };
|
|
|
|
|
|
}
|
2026-04-21 20:36:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function deriveWeChatConnectStoredMode(
|
|
|
|
|
|
openEnabled: boolean,
|
|
|
|
|
|
mpEnabled: boolean,
|
2026-04-21 07:48:42 -07:00
|
|
|
|
mobileEnabled: boolean,
|
2026-04-21 20:36:10 +08:00
|
|
|
|
legacyMode: unknown,
|
|
|
|
|
|
): WeChatConnectMode {
|
|
|
|
|
|
if (mpEnabled) return "mp";
|
2026-04-21 07:48:42 -07:00
|
|
|
|
if (mobileEnabled) return "mobile";
|
2026-04-21 20:36:10 +08:00
|
|
|
|
if (openEnabled) return "open";
|
|
|
|
|
|
return normalizeWeChatConnectMode(legacyMode);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-18 13:50:39 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* System settings interface
|
|
|
|
|
|
*/
|
|
|
|
|
|
export interface SystemSettings {
|
|
|
|
|
|
// Registration settings
|
2026-04-21 17:35:12 +08:00
|
|
|
|
registration_enabled: boolean;
|
|
|
|
|
|
email_verify_enabled: boolean;
|
|
|
|
|
|
registration_email_suffix_whitelist: string[];
|
2026-08-09 15:53:40 +08:00
|
|
|
|
registration_email_domain_quota_enabled: boolean;
|
2026-04-21 17:35:12 +08:00
|
|
|
|
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 加密密钥是否已配置
|
2026-07-26 11:07:12 -04:00
|
|
|
|
passkey_enabled: boolean;
|
|
|
|
|
|
passkey_configured: boolean;
|
|
|
|
|
|
passkey_rp_id: string;
|
|
|
|
|
|
passkey_rp_origins: string[];
|
2026-07-16 13:47:50 +08:00
|
|
|
|
session_binding_enabled: boolean; // 会话 IP/UA 绑定
|
2026-07-18 10:46:42 +08:00
|
|
|
|
step_up_enabled: boolean; // 敏感操作 step-up 2FA
|
2026-07-16 13:47:50 +08:00
|
|
|
|
audit_log_retention_days: number; // 审计日志保留天数
|
2026-05-07 17:35:05 +08:00
|
|
|
|
login_agreement_enabled: boolean;
|
|
|
|
|
|
login_agreement_mode: "modal" | "checkbox" | string;
|
|
|
|
|
|
login_agreement_updated_at: string;
|
|
|
|
|
|
login_agreement_documents: LoginAgreementDocument[];
|
2025-12-18 13:50:39 +08:00
|
|
|
|
// Default settings
|
2026-04-21 17:35:12 +08:00
|
|
|
|
default_balance: number;
|
2026-04-24 21:41:26 +08:00
|
|
|
|
affiliate_rebate_rate: number;
|
2026-04-26 12:31:52 +08:00
|
|
|
|
affiliate_rebate_freeze_hours: number;
|
|
|
|
|
|
affiliate_rebate_duration_days: number;
|
|
|
|
|
|
affiliate_rebate_per_invitee_cap: number;
|
2026-07-15 04:28:55 -05:00
|
|
|
|
affiliate_admin_recharge_enabled: boolean;
|
2026-04-21 17:35:12 +08:00
|
|
|
|
default_concurrency: number;
|
2026-04-23 03:33:52 +08:00
|
|
|
|
default_user_rpm_limit: number;
|
2026-04-21 17:35:12 +08:00
|
|
|
|
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;
|
2026-05-15 11:46:29 +08:00
|
|
|
|
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;
|
2026-05-06 16:06:11 +08:00
|
|
|
|
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;
|
2026-04-21 17:35:12 +08:00
|
|
|
|
force_email_on_third_party_signup?: boolean;
|
2026-05-25 14:26:11 +08:00
|
|
|
|
// ── 平台限额(嵌套 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;
|
2025-12-18 13:50:39 +08:00
|
|
|
|
// OEM settings
|
2026-04-21 17:35:12 +08:00
|
|
|
|
site_name: string;
|
|
|
|
|
|
site_logo: string;
|
|
|
|
|
|
site_subtitle: string;
|
|
|
|
|
|
api_base_url: string;
|
|
|
|
|
|
contact_info: string;
|
|
|
|
|
|
doc_url: string;
|
|
|
|
|
|
home_content: string;
|
2026-07-30 16:28:19 +08:00
|
|
|
|
compact_home_enabled: boolean;
|
2026-04-21 17:35:12 +08:00
|
|
|
|
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[];
|
2025-12-18 13:50:39 +08:00
|
|
|
|
// SMTP settings
|
2026-04-21 17:35:12 +08:00
|
|
|
|
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;
|
2025-12-18 13:50:39 +08:00
|
|
|
|
// Cloudflare Turnstile settings
|
2026-04-21 17:35:12 +08:00
|
|
|
|
turnstile_enabled: boolean;
|
|
|
|
|
|
turnstile_site_key: string;
|
|
|
|
|
|
turnstile_secret_key_configured: boolean;
|
2026-08-04 15:09:29 +08:00
|
|
|
|
tencent_captcha_enabled: boolean;
|
|
|
|
|
|
tencent_captcha_app_id: string;
|
|
|
|
|
|
tencent_captcha_app_secret_key_configured: boolean;
|
|
|
|
|
|
tencent_captcha_cloud_secret_id_configured: boolean;
|
|
|
|
|
|
tencent_captcha_cloud_secret_key_configured: boolean;
|
2026-08-06 20:34:37 +08:00
|
|
|
|
tencent_captcha_region: string;
|
2026-08-03 13:38:44 +08:00
|
|
|
|
aliyun_captcha_enabled: boolean;
|
|
|
|
|
|
aliyun_captcha_access_key_id: string;
|
|
|
|
|
|
aliyun_captcha_access_key_secret_configured: boolean;
|
|
|
|
|
|
aliyun_captcha_scene_id: string;
|
|
|
|
|
|
aliyun_captcha_prefix: string;
|
|
|
|
|
|
aliyun_captcha_region: string;
|
2026-05-20 22:51:46 +08:00
|
|
|
|
api_key_acl_trust_forwarded_ip: boolean;
|
2026-07-20 00:09:57 +08:00
|
|
|
|
forwarded_client_ip_headers: string[];
|
2026-01-09 20:58:33 +08:00
|
|
|
|
|
2026-01-12 09:14:32 +08:00
|
|
|
|
// LinuxDo Connect OAuth settings
|
2026-04-21 17:35:12 +08:00
|
|
|
|
linuxdo_connect_enabled: boolean;
|
|
|
|
|
|
linuxdo_connect_client_id: string;
|
|
|
|
|
|
linuxdo_connect_client_secret_configured: boolean;
|
|
|
|
|
|
linuxdo_connect_redirect_url: string;
|
|
|
|
|
|
|
2026-05-15 11:46:29 +08:00
|
|
|
|
// 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;
|
|
|
|
|
|
|
2026-04-21 17:35:12 +08:00
|
|
|
|
// WeChat Connect OAuth settings
|
|
|
|
|
|
wechat_connect_enabled: boolean;
|
|
|
|
|
|
wechat_connect_app_id: string;
|
|
|
|
|
|
wechat_connect_app_secret_configured: boolean;
|
2026-04-21 07:48:42 -07:00
|
|
|
|
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;
|
2026-04-21 20:36:10 +08:00
|
|
|
|
wechat_connect_open_enabled?: boolean;
|
|
|
|
|
|
wechat_connect_mp_enabled?: boolean;
|
2026-04-21 07:48:42 -07:00
|
|
|
|
wechat_connect_mobile_enabled?: boolean;
|
2026-04-21 17:35:12 +08:00
|
|
|
|
wechat_connect_mode: string;
|
|
|
|
|
|
wechat_connect_scopes: string;
|
|
|
|
|
|
wechat_connect_redirect_url: string;
|
|
|
|
|
|
wechat_connect_frontend_redirect_url: string;
|
2026-01-12 09:14:32 +08:00
|
|
|
|
|
2026-03-13 23:38:58 +08:00
|
|
|
|
// Generic OIDC OAuth settings
|
2026-04-21 17:35:12 +08:00
|
|
|
|
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;
|
2026-05-06 16:06:11 +08:00
|
|
|
|
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;
|
2026-03-13 23:38:58 +08:00
|
|
|
|
|
2026-01-09 20:58:33 +08:00
|
|
|
|
// Model fallback configuration
|
2026-04-21 17:35:12 +08:00
|
|
|
|
enable_model_fallback: boolean;
|
|
|
|
|
|
fallback_model_anthropic: string;
|
|
|
|
|
|
fallback_model_openai: string;
|
|
|
|
|
|
fallback_model_gemini: string;
|
|
|
|
|
|
fallback_model_antigravity: string;
|
2026-08-07 14:22:15 +08:00
|
|
|
|
grok_default_text_model: string;
|
|
|
|
|
|
grok_cross_client_model_map_enabled: boolean;
|
2026-08-08 01:07:19 +08:00
|
|
|
|
grok_default_base_url_mode: string;
|
2026-01-09 20:58:33 +08:00
|
|
|
|
|
2026-08-07 14:53:18 +08:00
|
|
|
|
// Per-platform account auto-pause thresholds (100 = disabled)
|
|
|
|
|
|
account_scheduling_thresholds: AccountSchedulingThresholdsMap;
|
2026-01-09 20:58:33 +08:00
|
|
|
|
|
2026-01-04 22:49:40 +08:00
|
|
|
|
// Identity patch configuration (Claude -> Gemini)
|
2026-04-21 17:35:12 +08:00
|
|
|
|
enable_identity_patch: boolean;
|
|
|
|
|
|
identity_patch_prompt: string;
|
2026-01-09 20:58:33 +08:00
|
|
|
|
|
|
|
|
|
|
// Ops Monitoring (vNext)
|
2026-04-21 17:35:12 +08:00
|
|
|
|
ops_monitoring_enabled: boolean;
|
|
|
|
|
|
ops_realtime_monitoring_enabled: boolean;
|
|
|
|
|
|
ops_query_mode_default: "auto" | "raw" | "preagg" | string;
|
|
|
|
|
|
ops_metrics_interval_seconds: number;
|
2026-03-01 15:35:46 +08:00
|
|
|
|
|
|
|
|
|
|
// Claude Code version check
|
2026-04-21 17:35:12 +08:00
|
|
|
|
min_claude_code_version: string;
|
|
|
|
|
|
max_claude_code_version: string;
|
2026-03-03 19:56:27 +08:00
|
|
|
|
|
|
|
|
|
|
// 分组隔离
|
2026-04-21 17:35:12 +08:00
|
|
|
|
allow_ungrouped_key_scheduling: boolean;
|
2026-03-26 10:22:03 +08:00
|
|
|
|
|
|
|
|
|
|
// Gateway forwarding behavior
|
2026-08-31 14:41:21 +08:00
|
|
|
|
openai_ttft_mode: string;
|
2026-04-21 17:35:12 +08:00
|
|
|
|
enable_fingerprint_unification: boolean;
|
|
|
|
|
|
enable_metadata_passthrough: boolean;
|
|
|
|
|
|
enable_cch_signing: boolean;
|
2026-06-13 04:12:13 +08:00
|
|
|
|
enable_claude_oauth_system_prompt_injection: boolean;
|
|
|
|
|
|
claude_oauth_system_prompt: string;
|
|
|
|
|
|
claude_oauth_system_prompt_blocks: string;
|
2026-04-30 13:38:22 +08:00
|
|
|
|
enable_anthropic_cache_ttl_1h_injection: boolean;
|
2026-05-11 21:26:41 +08:00
|
|
|
|
rewrite_message_cache_control: boolean;
|
2026-07-01 10:54:18 +08:00
|
|
|
|
enable_client_dateline_normalization: boolean;
|
2026-05-11 22:25:20 +08:00
|
|
|
|
antigravity_user_agent_version: string;
|
2026-05-20 14:33:51 +08:00
|
|
|
|
openai_codex_user_agent: string;
|
2026-08-03 20:14:58 +08:00
|
|
|
|
openai_codex_client_version: string;
|
|
|
|
|
|
openai_codex_client_version_synced: string;
|
|
|
|
|
|
openai_codex_version_auto_sync_enabled: boolean;
|
2026-06-26 14:38:46 +08:00
|
|
|
|
// 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;
|
2026-04-21 17:35:12 +08:00
|
|
|
|
web_search_emulation_enabled?: boolean;
|
2026-04-10 21:08:51 +08:00
|
|
|
|
|
|
|
|
|
|
// Payment configuration
|
2026-04-21 17:35:12 +08:00
|
|
|
|
payment_enabled: boolean;
|
2026-05-07 09:01:48 +08:00
|
|
|
|
risk_control_enabled: boolean;
|
2026-06-11 17:26:14 +08:00
|
|
|
|
|
|
|
|
|
|
// Cyber session block
|
|
|
|
|
|
cyber_session_block_enabled: boolean;
|
|
|
|
|
|
cyber_session_block_ttl_seconds: number;
|
|
|
|
|
|
|
2026-04-21 17:35:12 +08:00
|
|
|
|
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;
|
2026-07-06 14:34:17 +08:00
|
|
|
|
payment_subscription_usd_to_cny_rate: number;
|
2026-04-21 17:35:12 +08:00
|
|
|
|
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;
|
2026-05-19 18:22:12 +08:00
|
|
|
|
payment_alipay_force_qrcode?: boolean;
|
2026-07-22 16:57:49 +08:00
|
|
|
|
payment_alipay_mobile_precreate_deep_link?: boolean;
|
2026-04-21 17:35:12 +08:00
|
|
|
|
payment_visible_method_alipay_source?: string;
|
|
|
|
|
|
payment_visible_method_wxpay_source?: string;
|
|
|
|
|
|
payment_visible_method_alipay_enabled?: boolean;
|
|
|
|
|
|
payment_visible_method_wxpay_enabled?: boolean;
|
2026-07-15 22:42:54 +08:00
|
|
|
|
openai_low_upstream_rate_priority_enabled?: boolean;
|
|
|
|
|
|
openai_oauth_scheduling_rate_multiplier?: number;
|
2026-04-21 17:35:12 +08:00
|
|
|
|
openai_advanced_scheduler_enabled?: boolean;
|
2026-07-03 23:52:10 +08:00
|
|
|
|
openai_advanced_scheduler_sticky_weighted_enabled?: boolean;
|
|
|
|
|
|
openai_advanced_scheduler_subscription_priority_enabled?: boolean;
|
|
|
|
|
|
openai_advanced_scheduler_lb_top_k?: string;
|
|
|
|
|
|
openai_advanced_scheduler_weight_priority?: string;
|
|
|
|
|
|
openai_advanced_scheduler_weight_load?: string;
|
|
|
|
|
|
openai_advanced_scheduler_weight_queue?: string;
|
|
|
|
|
|
openai_advanced_scheduler_weight_error_rate?: string;
|
|
|
|
|
|
openai_advanced_scheduler_weight_ttft?: string;
|
|
|
|
|
|
openai_advanced_scheduler_weight_reset?: string;
|
|
|
|
|
|
openai_advanced_scheduler_weight_quota_headroom?: string;
|
2026-07-15 22:42:54 +08:00
|
|
|
|
openai_advanced_scheduler_weight_upstream_cost?: string;
|
2026-07-03 23:52:10 +08:00
|
|
|
|
openai_advanced_scheduler_weight_previous_response?: string;
|
|
|
|
|
|
openai_advanced_scheduler_weight_session_sticky?: string;
|
|
|
|
|
|
openai_advanced_scheduler_effective_lb_top_k?: string;
|
|
|
|
|
|
openai_advanced_scheduler_effective_weight_priority?: string;
|
|
|
|
|
|
openai_advanced_scheduler_effective_weight_load?: string;
|
|
|
|
|
|
openai_advanced_scheduler_effective_weight_queue?: string;
|
|
|
|
|
|
openai_advanced_scheduler_effective_weight_error_rate?: string;
|
|
|
|
|
|
openai_advanced_scheduler_effective_weight_ttft?: string;
|
|
|
|
|
|
openai_advanced_scheduler_effective_weight_reset?: string;
|
|
|
|
|
|
openai_advanced_scheduler_effective_weight_quota_headroom?: string;
|
2026-07-15 22:42:54 +08:00
|
|
|
|
openai_advanced_scheduler_effective_weight_upstream_cost?: string;
|
2026-07-03 23:52:10 +08:00
|
|
|
|
openai_advanced_scheduler_effective_weight_previous_response?: string;
|
|
|
|
|
|
openai_advanced_scheduler_effective_weight_session_sticky?: string;
|
2026-04-12 02:48:57 +08:00
|
|
|
|
|
2026-05-21 14:27:50 +08:00
|
|
|
|
// 余额、订阅到期与账号限额通知
|
2026-04-21 17:35:12 +08:00
|
|
|
|
balance_low_notify_enabled: boolean;
|
|
|
|
|
|
balance_low_notify_threshold: number;
|
|
|
|
|
|
balance_low_notify_recharge_url: string;
|
2026-05-21 14:27:50 +08:00
|
|
|
|
subscription_expiry_notify_enabled: boolean;
|
2026-04-21 17:35:12 +08:00
|
|
|
|
account_quota_notify_enabled: boolean;
|
|
|
|
|
|
account_quota_notify_emails: NotifyEmailEntry[];
|
2026-04-21 00:21:29 +08:00
|
|
|
|
|
|
|
|
|
|
// Channel Monitor feature switch
|
|
|
|
|
|
channel_monitor_enabled: boolean;
|
2026-08-07 11:01:22 +08:00
|
|
|
|
channel_monitor_mode?: 'v1' | 'v2';
|
2026-04-21 00:21:29 +08:00
|
|
|
|
channel_monitor_default_interval_seconds: number;
|
2026-08-07 11:01:22 +08:00
|
|
|
|
channel_monitor_hide_throughput?: boolean;
|
2026-08-18 05:09:58 +00:00
|
|
|
|
channel_monitor_show_quota?: boolean;
|
2026-09-07 18:31:03 +08:00
|
|
|
|
channel_monitor_hide_user_ranking?: boolean;
|
2026-04-21 21:59:23 +08:00
|
|
|
|
|
|
|
|
|
|
// Available Channels feature switch
|
|
|
|
|
|
available_channels_enabled: boolean;
|
2026-04-25 19:14:34 +08:00
|
|
|
|
|
2026-07-28 15:55:56 +08:00
|
|
|
|
// Model Plaza feature switches + description
|
|
|
|
|
|
model_plaza_enabled: boolean;
|
|
|
|
|
|
model_plaza_require_auth: boolean;
|
|
|
|
|
|
model_plaza_description: string;
|
2026-08-24 09:03:37 +08:00
|
|
|
|
plugin_management_enabled: boolean;
|
2026-07-28 15:55:56 +08:00
|
|
|
|
|
2026-04-25 19:14:34 +08:00
|
|
|
|
// Affiliate (邀请返利) feature switch
|
|
|
|
|
|
affiliate_enabled: boolean;
|
2026-04-28 00:34:23 +08:00
|
|
|
|
|
|
|
|
|
|
// OpenAI fast/flex policy
|
|
|
|
|
|
openai_fast_policy_settings?: OpenAIFastPolicySettings;
|
2026-06-04 19:06:24 +08:00
|
|
|
|
|
|
|
|
|
|
// Allow user view error requests
|
|
|
|
|
|
allow_user_view_error_requests: boolean;
|
2025-12-18 13:50:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-02 17:40:57 +08:00
|
|
|
|
export interface UpdateSettingsRequest {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
registration_enabled?: boolean;
|
|
|
|
|
|
email_verify_enabled?: boolean;
|
|
|
|
|
|
registration_email_suffix_whitelist?: string[];
|
2026-08-09 15:53:40 +08:00
|
|
|
|
registration_email_domain_quota_enabled?: boolean;
|
2026-04-21 17:35:12 +08:00
|
|
|
|
promo_code_enabled?: boolean;
|
|
|
|
|
|
password_reset_enabled?: boolean;
|
|
|
|
|
|
frontend_url?: string;
|
|
|
|
|
|
invitation_code_enabled?: boolean;
|
|
|
|
|
|
totp_enabled?: boolean; // TOTP 双因素认证
|
2026-07-26 11:07:12 -04:00
|
|
|
|
passkey_enabled?: boolean;
|
2026-07-16 13:47:50 +08:00
|
|
|
|
session_binding_enabled?: boolean; // 会话 IP/UA 绑定
|
2026-07-18 10:46:42 +08:00
|
|
|
|
step_up_enabled?: boolean; // 敏感操作 step-up 2FA
|
2026-07-16 13:47:50 +08:00
|
|
|
|
audit_log_retention_days?: number; // 审计日志保留天数
|
2026-05-07 17:35:05 +08:00
|
|
|
|
login_agreement_enabled?: boolean;
|
|
|
|
|
|
login_agreement_mode?: "modal" | "checkbox" | string;
|
|
|
|
|
|
login_agreement_updated_at?: string;
|
|
|
|
|
|
login_agreement_documents?: LoginAgreementDocument[];
|
2026-04-21 17:35:12 +08:00
|
|
|
|
default_balance?: number;
|
2026-04-24 21:41:26 +08:00
|
|
|
|
affiliate_rebate_rate?: number;
|
2026-04-26 12:31:52 +08:00
|
|
|
|
affiliate_rebate_freeze_hours?: number;
|
|
|
|
|
|
affiliate_rebate_duration_days?: number;
|
|
|
|
|
|
affiliate_rebate_per_invitee_cap?: number;
|
2026-07-15 04:28:55 -05:00
|
|
|
|
affiliate_admin_recharge_enabled?: boolean;
|
2026-04-21 17:35:12 +08:00
|
|
|
|
default_concurrency?: number;
|
2026-04-23 03:33:52 +08:00
|
|
|
|
default_user_rpm_limit?: number;
|
2026-04-21 17:35:12 +08:00
|
|
|
|
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;
|
2026-05-15 11:46:29 +08:00
|
|
|
|
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;
|
2026-05-06 16:06:11 +08:00
|
|
|
|
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;
|
2026-04-21 17:35:12 +08:00
|
|
|
|
force_email_on_third_party_signup?: boolean;
|
2026-05-25 14:26:11 +08:00
|
|
|
|
// ── 平台限额(嵌套 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;
|
2026-04-21 17:35:12 +08:00
|
|
|
|
site_name?: string;
|
|
|
|
|
|
site_logo?: string;
|
|
|
|
|
|
site_subtitle?: string;
|
|
|
|
|
|
api_base_url?: string;
|
|
|
|
|
|
contact_info?: string;
|
|
|
|
|
|
doc_url?: string;
|
|
|
|
|
|
home_content?: string;
|
2026-07-30 16:28:19 +08:00
|
|
|
|
compact_home_enabled?: boolean;
|
2026-04-21 17:35:12 +08:00
|
|
|
|
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;
|
2026-08-04 15:09:29 +08:00
|
|
|
|
tencent_captcha_enabled?: boolean;
|
|
|
|
|
|
tencent_captcha_app_id?: string;
|
|
|
|
|
|
tencent_captcha_app_secret_key?: string;
|
|
|
|
|
|
tencent_captcha_cloud_secret_id?: string;
|
|
|
|
|
|
tencent_captcha_cloud_secret_key?: string;
|
2026-08-06 20:34:37 +08:00
|
|
|
|
tencent_captcha_region?: string;
|
2026-08-03 13:38:44 +08:00
|
|
|
|
aliyun_captcha_enabled?: boolean;
|
|
|
|
|
|
aliyun_captcha_access_key_id?: string;
|
|
|
|
|
|
aliyun_captcha_access_key_secret?: string;
|
|
|
|
|
|
aliyun_captcha_scene_id?: string;
|
|
|
|
|
|
aliyun_captcha_prefix?: string;
|
|
|
|
|
|
aliyun_captcha_region?: string;
|
2026-05-20 22:51:46 +08:00
|
|
|
|
api_key_acl_trust_forwarded_ip?: boolean;
|
2026-07-20 00:09:57 +08:00
|
|
|
|
forwarded_client_ip_headers?: string[];
|
2026-04-21 17:35:12 +08:00
|
|
|
|
linuxdo_connect_enabled?: boolean;
|
|
|
|
|
|
linuxdo_connect_client_id?: string;
|
|
|
|
|
|
linuxdo_connect_client_secret?: string;
|
|
|
|
|
|
linuxdo_connect_redirect_url?: string;
|
2026-05-15 11:46:29 +08:00
|
|
|
|
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;
|
2026-04-21 17:35:12 +08:00
|
|
|
|
wechat_connect_enabled?: boolean;
|
|
|
|
|
|
wechat_connect_app_id?: string;
|
|
|
|
|
|
wechat_connect_app_secret?: string;
|
2026-04-21 07:48:42 -07:00
|
|
|
|
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;
|
2026-04-21 20:36:10 +08:00
|
|
|
|
wechat_connect_open_enabled?: boolean;
|
|
|
|
|
|
wechat_connect_mp_enabled?: boolean;
|
2026-04-21 07:48:42 -07:00
|
|
|
|
wechat_connect_mobile_enabled?: boolean;
|
2026-04-21 17:35:12 +08:00
|
|
|
|
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;
|
2026-05-06 16:06:11 +08:00
|
|
|
|
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;
|
2026-04-21 17:35:12 +08:00
|
|
|
|
enable_model_fallback?: boolean;
|
|
|
|
|
|
fallback_model_anthropic?: string;
|
|
|
|
|
|
fallback_model_openai?: string;
|
|
|
|
|
|
fallback_model_gemini?: string;
|
|
|
|
|
|
fallback_model_antigravity?: string;
|
2026-08-07 14:22:15 +08:00
|
|
|
|
grok_default_text_model?: string;
|
|
|
|
|
|
grok_cross_client_model_map_enabled?: boolean;
|
2026-08-08 01:07:19 +08:00
|
|
|
|
grok_default_base_url_mode?: string;
|
2026-08-07 14:53:18 +08:00
|
|
|
|
account_scheduling_thresholds?: AccountSchedulingThresholdsMap;
|
2026-04-21 17:35:12 +08:00
|
|
|
|
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;
|
2026-08-31 14:41:21 +08:00
|
|
|
|
openai_ttft_mode?: string;
|
2026-04-21 17:35:12 +08:00
|
|
|
|
enable_fingerprint_unification?: boolean;
|
|
|
|
|
|
enable_metadata_passthrough?: boolean;
|
|
|
|
|
|
enable_cch_signing?: boolean;
|
2026-06-13 04:12:13 +08:00
|
|
|
|
enable_claude_oauth_system_prompt_injection?: boolean;
|
|
|
|
|
|
claude_oauth_system_prompt?: string;
|
|
|
|
|
|
claude_oauth_system_prompt_blocks?: string;
|
2026-04-30 13:38:22 +08:00
|
|
|
|
enable_anthropic_cache_ttl_1h_injection?: boolean;
|
2026-05-11 21:26:41 +08:00
|
|
|
|
rewrite_message_cache_control?: boolean;
|
2026-07-01 10:54:18 +08:00
|
|
|
|
enable_client_dateline_normalization?: boolean;
|
2026-05-11 22:25:20 +08:00
|
|
|
|
antigravity_user_agent_version?: string;
|
2026-05-20 14:33:51 +08:00
|
|
|
|
openai_codex_user_agent?: string;
|
2026-08-03 20:14:58 +08:00
|
|
|
|
openai_codex_client_version?: string;
|
|
|
|
|
|
openai_codex_version_auto_sync_enabled?: boolean;
|
2026-06-26 14:38:46 +08:00
|
|
|
|
// 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;
|
2026-04-10 21:08:51 +08:00
|
|
|
|
// Payment configuration
|
2026-04-21 17:35:12 +08:00
|
|
|
|
payment_enabled?: boolean;
|
2026-05-07 09:01:48 +08:00
|
|
|
|
risk_control_enabled?: boolean;
|
2026-06-11 17:26:14 +08:00
|
|
|
|
|
|
|
|
|
|
// Cyber session block
|
|
|
|
|
|
cyber_session_block_enabled?: boolean;
|
|
|
|
|
|
cyber_session_block_ttl_seconds?: number;
|
|
|
|
|
|
|
2026-04-21 17:35:12 +08:00
|
|
|
|
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;
|
2026-07-06 14:34:17 +08:00
|
|
|
|
payment_subscription_usd_to_cny_rate?: number;
|
2026-04-21 17:35:12 +08:00
|
|
|
|
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;
|
2026-05-19 18:22:12 +08:00
|
|
|
|
payment_alipay_force_qrcode?: boolean;
|
2026-07-22 16:57:49 +08:00
|
|
|
|
payment_alipay_mobile_precreate_deep_link?: boolean;
|
2026-04-21 17:35:12 +08:00
|
|
|
|
payment_visible_method_alipay_source?: string;
|
|
|
|
|
|
payment_visible_method_wxpay_source?: string;
|
|
|
|
|
|
payment_visible_method_alipay_enabled?: boolean;
|
|
|
|
|
|
payment_visible_method_wxpay_enabled?: boolean;
|
2026-07-15 22:42:54 +08:00
|
|
|
|
openai_low_upstream_rate_priority_enabled?: boolean;
|
|
|
|
|
|
openai_oauth_scheduling_rate_multiplier?: number;
|
2026-04-21 17:35:12 +08:00
|
|
|
|
openai_advanced_scheduler_enabled?: boolean;
|
2026-07-03 23:52:10 +08:00
|
|
|
|
openai_advanced_scheduler_sticky_weighted_enabled?: boolean;
|
|
|
|
|
|
openai_advanced_scheduler_subscription_priority_enabled?: boolean;
|
|
|
|
|
|
openai_advanced_scheduler_lb_top_k?: string;
|
|
|
|
|
|
openai_advanced_scheduler_weight_priority?: string;
|
|
|
|
|
|
openai_advanced_scheduler_weight_load?: string;
|
|
|
|
|
|
openai_advanced_scheduler_weight_queue?: string;
|
|
|
|
|
|
openai_advanced_scheduler_weight_error_rate?: string;
|
|
|
|
|
|
openai_advanced_scheduler_weight_ttft?: string;
|
|
|
|
|
|
openai_advanced_scheduler_weight_reset?: string;
|
|
|
|
|
|
openai_advanced_scheduler_weight_quota_headroom?: string;
|
2026-07-15 22:42:54 +08:00
|
|
|
|
openai_advanced_scheduler_weight_upstream_cost?: string;
|
2026-07-03 23:52:10 +08:00
|
|
|
|
openai_advanced_scheduler_weight_previous_response?: string;
|
|
|
|
|
|
openai_advanced_scheduler_weight_session_sticky?: string;
|
2026-05-21 14:27:50 +08:00
|
|
|
|
// 余额、订阅到期与账号限额通知
|
2026-04-21 17:35:12 +08:00
|
|
|
|
balance_low_notify_enabled?: boolean;
|
|
|
|
|
|
balance_low_notify_threshold?: number;
|
|
|
|
|
|
balance_low_notify_recharge_url?: string;
|
2026-05-21 14:27:50 +08:00
|
|
|
|
subscription_expiry_notify_enabled?: boolean;
|
2026-04-21 17:35:12 +08:00
|
|
|
|
account_quota_notify_enabled?: boolean;
|
|
|
|
|
|
account_quota_notify_emails?: NotifyEmailEntry[];
|
2026-04-21 00:21:29 +08:00
|
|
|
|
|
|
|
|
|
|
// Channel Monitor feature switch
|
|
|
|
|
|
channel_monitor_enabled?: boolean;
|
2026-08-07 11:01:22 +08:00
|
|
|
|
channel_monitor_mode?: 'v1' | 'v2';
|
2026-04-21 00:21:29 +08:00
|
|
|
|
channel_monitor_default_interval_seconds?: number;
|
2026-08-07 11:01:22 +08:00
|
|
|
|
channel_monitor_hide_throughput?: boolean;
|
2026-08-18 05:09:58 +00:00
|
|
|
|
channel_monitor_show_quota?: boolean;
|
2026-09-07 18:31:03 +08:00
|
|
|
|
channel_monitor_hide_user_ranking?: boolean;
|
2026-04-21 21:59:23 +08:00
|
|
|
|
|
|
|
|
|
|
// Available Channels feature switch
|
|
|
|
|
|
available_channels_enabled?: boolean;
|
2026-04-25 19:14:34 +08:00
|
|
|
|
|
2026-07-28 15:55:56 +08:00
|
|
|
|
// Model Plaza feature switches + description
|
|
|
|
|
|
model_plaza_enabled?: boolean;
|
|
|
|
|
|
model_plaza_require_auth?: boolean;
|
|
|
|
|
|
model_plaza_description?: string;
|
2026-08-24 09:03:37 +08:00
|
|
|
|
plugin_management_enabled?: boolean;
|
2026-07-28 15:55:56 +08:00
|
|
|
|
|
2026-04-25 19:14:34 +08:00
|
|
|
|
// Affiliate (邀请返利) feature switch
|
|
|
|
|
|
affiliate_enabled?: boolean;
|
2026-04-28 00:34:23 +08:00
|
|
|
|
|
|
|
|
|
|
// OpenAI fast/flex policy
|
|
|
|
|
|
openai_fast_policy_settings?: OpenAIFastPolicySettings;
|
2026-06-04 19:06:24 +08:00
|
|
|
|
|
|
|
|
|
|
allow_user_view_error_requests?: boolean;
|
2025-12-18 13:50:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Get all system settings
|
|
|
|
|
|
* @returns System settings
|
|
|
|
|
|
*/
|
|
|
|
|
|
export async function getSettings(): Promise<SystemSettings> {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
const { data } = await apiClient.get<SystemSettings>("/admin/settings");
|
|
|
|
|
|
return data;
|
2025-12-18 13:50:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Update system settings
|
|
|
|
|
|
* @param settings - Partial settings to update
|
|
|
|
|
|
* @returns Updated settings
|
|
|
|
|
|
*/
|
2026-04-21 17:35:12 +08:00
|
|
|
|
export async function updateSettings(
|
|
|
|
|
|
settings: UpdateSettingsRequest,
|
|
|
|
|
|
): Promise<SystemSettings> {
|
|
|
|
|
|
const { data } = await apiClient.put<SystemSettings>(
|
|
|
|
|
|
"/admin/settings",
|
|
|
|
|
|
settings,
|
|
|
|
|
|
);
|
|
|
|
|
|
return data;
|
2025-12-18 13:50:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Test SMTP connection request
|
|
|
|
|
|
*/
|
|
|
|
|
|
export interface TestSmtpRequest {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
smtp_host: string;
|
|
|
|
|
|
smtp_port: number;
|
|
|
|
|
|
smtp_username: string;
|
|
|
|
|
|
smtp_password: string;
|
|
|
|
|
|
smtp_use_tls: boolean;
|
2025-12-18 13:50:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Test SMTP connection with provided config
|
|
|
|
|
|
* @param config - SMTP configuration to test
|
|
|
|
|
|
* @returns Test result message
|
|
|
|
|
|
*/
|
2026-04-21 17:35:12 +08:00
|
|
|
|
export async function testSmtpConnection(
|
|
|
|
|
|
config: TestSmtpRequest,
|
|
|
|
|
|
): Promise<{ message: string }> {
|
|
|
|
|
|
const { data } = await apiClient.post<{ message: string }>(
|
|
|
|
|
|
"/admin/settings/test-smtp",
|
|
|
|
|
|
config,
|
|
|
|
|
|
);
|
|
|
|
|
|
return data;
|
2025-12-18 13:50:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Send test email request
|
|
|
|
|
|
*/
|
|
|
|
|
|
export interface SendTestEmailRequest {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
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;
|
2025-12-18 13:50:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Send test email with provided SMTP config
|
|
|
|
|
|
* @param request - Email address and SMTP config
|
|
|
|
|
|
* @returns Test result message
|
|
|
|
|
|
*/
|
2026-04-21 17:35:12 +08:00
|
|
|
|
export async function sendTestEmail(
|
|
|
|
|
|
request: SendTestEmailRequest,
|
|
|
|
|
|
): Promise<{ message: string }> {
|
2025-12-25 08:41:30 -08:00
|
|
|
|
const { data } = await apiClient.post<{ message: string }>(
|
2026-04-21 17:35:12 +08:00
|
|
|
|
"/admin/settings/send-test-email",
|
|
|
|
|
|
request,
|
|
|
|
|
|
);
|
|
|
|
|
|
return data;
|
2025-12-18 13:50:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-20 11:07:51 +08:00
|
|
|
|
// ==================== Email Template Settings ====================
|
|
|
|
|
|
|
|
|
|
|
|
export interface EmailTemplateOption {
|
|
|
|
|
|
value: string;
|
|
|
|
|
|
label?: string;
|
|
|
|
|
|
description?: string;
|
2026-05-21 14:27:50 +08:00
|
|
|
|
category?: string;
|
|
|
|
|
|
optional?: boolean;
|
2026-05-20 11:07:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-20 15:11:43 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* Admin API Key status response
|
|
|
|
|
|
*/
|
|
|
|
|
|
export interface AdminApiKeyStatus {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
exists: boolean;
|
|
|
|
|
|
masked_key: string;
|
2025-12-20 15:11:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Get admin API key status
|
|
|
|
|
|
* @returns Status indicating if key exists and masked version
|
|
|
|
|
|
*/
|
|
|
|
|
|
export async function getAdminApiKey(): Promise<AdminApiKeyStatus> {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
const { data } = await apiClient.get<AdminApiKeyStatus>(
|
|
|
|
|
|
"/admin/settings/admin-api-key",
|
|
|
|
|
|
);
|
|
|
|
|
|
return data;
|
2025-12-20 15:11:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Regenerate admin API key
|
|
|
|
|
|
* @returns The new full API key (only shown once)
|
|
|
|
|
|
*/
|
|
|
|
|
|
export async function regenerateAdminApiKey(): Promise<{ key: string }> {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
const { data } = await apiClient.post<{ key: string }>(
|
|
|
|
|
|
"/admin/settings/admin-api-key/regenerate",
|
|
|
|
|
|
);
|
|
|
|
|
|
return data;
|
2025-12-20 15:11:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Delete admin API key
|
|
|
|
|
|
* @returns Success message
|
|
|
|
|
|
*/
|
|
|
|
|
|
export async function deleteAdminApiKey(): Promise<{ message: string }> {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
const { data } = await apiClient.delete<{ message: string }>(
|
|
|
|
|
|
"/admin/settings/admin-api-key",
|
|
|
|
|
|
);
|
|
|
|
|
|
return data;
|
2025-12-20 15:11:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-18 16:22:19 +08:00
|
|
|
|
// ==================== Overload Cooldown Settings ====================
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Overload cooldown settings interface (529 handling)
|
|
|
|
|
|
*/
|
|
|
|
|
|
export interface OverloadCooldownSettings {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
enabled: boolean;
|
|
|
|
|
|
cooldown_minutes: number;
|
2026-03-18 16:22:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export async function getOverloadCooldownSettings(): Promise<OverloadCooldownSettings> {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
const { data } = await apiClient.get<OverloadCooldownSettings>(
|
|
|
|
|
|
"/admin/settings/overload-cooldown",
|
|
|
|
|
|
);
|
|
|
|
|
|
return data;
|
2026-03-18 16:22:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export async function updateOverloadCooldownSettings(
|
2026-04-21 17:35:12 +08:00
|
|
|
|
settings: OverloadCooldownSettings,
|
2026-03-18 16:22:19 +08:00
|
|
|
|
): Promise<OverloadCooldownSettings> {
|
|
|
|
|
|
const { data } = await apiClient.put<OverloadCooldownSettings>(
|
2026-04-21 17:35:12 +08:00
|
|
|
|
"/admin/settings/overload-cooldown",
|
|
|
|
|
|
settings,
|
|
|
|
|
|
);
|
|
|
|
|
|
return data;
|
2026-03-18 16:22:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-30 03:01:39 +00:00
|
|
|
|
// ==================== 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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-27 15:12:51 +08:00
|
|
|
|
// ==================== Panel Rate Limit Settings ====================
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Panel API rate limit settings.
|
|
|
|
|
|
* Authenticated panel endpoints are limited per user account (reverse-proxy
|
|
|
|
|
|
* safe); public endpoints are limited per publicly routable client IP.
|
|
|
|
|
|
*/
|
|
|
|
|
|
export interface PanelRateLimitSettings {
|
|
|
|
|
|
enabled: boolean;
|
|
|
|
|
|
user_rpm: number;
|
|
|
|
|
|
heavy_rpm: number;
|
|
|
|
|
|
exempt_admin: boolean;
|
|
|
|
|
|
public_ip_rpm: number;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export async function getPanelRateLimitSettings(): Promise<PanelRateLimitSettings> {
|
|
|
|
|
|
const { data } = await apiClient.get<PanelRateLimitSettings>(
|
|
|
|
|
|
"/admin/settings/panel-rate-limit",
|
|
|
|
|
|
);
|
|
|
|
|
|
return data;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export async function updatePanelRateLimitSettings(
|
|
|
|
|
|
settings: PanelRateLimitSettings,
|
|
|
|
|
|
): Promise<PanelRateLimitSettings> {
|
|
|
|
|
|
const { data } = await apiClient.put<PanelRateLimitSettings>(
|
|
|
|
|
|
"/admin/settings/panel-rate-limit",
|
|
|
|
|
|
settings,
|
|
|
|
|
|
);
|
|
|
|
|
|
return data;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-18 16:22:19 +08:00
|
|
|
|
// ==================== Stream Timeout Settings ====================
|
|
|
|
|
|
|
2026-01-11 21:54:52 -08:00
|
|
|
|
/**
|
|
|
|
|
|
* Stream timeout settings interface
|
|
|
|
|
|
*/
|
|
|
|
|
|
export interface StreamTimeoutSettings {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
enabled: boolean;
|
|
|
|
|
|
action: "temp_unsched" | "error" | "none";
|
|
|
|
|
|
temp_unsched_minutes: number;
|
|
|
|
|
|
threshold_count: number;
|
|
|
|
|
|
threshold_window_minutes: number;
|
2026-01-11 21:54:52 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Get stream timeout settings
|
|
|
|
|
|
* @returns Stream timeout settings
|
|
|
|
|
|
*/
|
|
|
|
|
|
export async function getStreamTimeoutSettings(): Promise<StreamTimeoutSettings> {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
const { data } = await apiClient.get<StreamTimeoutSettings>(
|
|
|
|
|
|
"/admin/settings/stream-timeout",
|
|
|
|
|
|
);
|
|
|
|
|
|
return data;
|
2026-01-11 21:54:52 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Update stream timeout settings
|
|
|
|
|
|
* @param settings - Stream timeout settings to update
|
|
|
|
|
|
* @returns Updated settings
|
|
|
|
|
|
*/
|
|
|
|
|
|
export async function updateStreamTimeoutSettings(
|
2026-04-21 17:35:12 +08:00
|
|
|
|
settings: StreamTimeoutSettings,
|
2026-01-11 21:54:52 -08:00
|
|
|
|
): Promise<StreamTimeoutSettings> {
|
|
|
|
|
|
const { data } = await apiClient.put<StreamTimeoutSettings>(
|
2026-04-21 17:35:12 +08:00
|
|
|
|
"/admin/settings/stream-timeout",
|
|
|
|
|
|
settings,
|
|
|
|
|
|
);
|
|
|
|
|
|
return data;
|
2026-01-11 21:54:52 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-07 21:45:18 +08:00
|
|
|
|
// ==================== Rectifier Settings ====================
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Rectifier settings interface
|
|
|
|
|
|
*/
|
|
|
|
|
|
export interface RectifierSettings {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
enabled: boolean;
|
|
|
|
|
|
thinking_signature_enabled: boolean;
|
|
|
|
|
|
thinking_budget_enabled: boolean;
|
|
|
|
|
|
apikey_signature_enabled: boolean;
|
|
|
|
|
|
apikey_signature_patterns: string[];
|
2026-03-07 21:45:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Get rectifier settings
|
|
|
|
|
|
* @returns Rectifier settings
|
|
|
|
|
|
*/
|
|
|
|
|
|
export async function getRectifierSettings(): Promise<RectifierSettings> {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
const { data } = await apiClient.get<RectifierSettings>(
|
|
|
|
|
|
"/admin/settings/rectifier",
|
|
|
|
|
|
);
|
|
|
|
|
|
return data;
|
2026-03-07 21:45:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Update rectifier settings
|
|
|
|
|
|
* @param settings - Rectifier settings to update
|
|
|
|
|
|
* @returns Updated settings
|
|
|
|
|
|
*/
|
|
|
|
|
|
export async function updateRectifierSettings(
|
2026-04-21 17:35:12 +08:00
|
|
|
|
settings: RectifierSettings,
|
2026-03-07 21:45:18 +08:00
|
|
|
|
): Promise<RectifierSettings> {
|
|
|
|
|
|
const { data } = await apiClient.put<RectifierSettings>(
|
2026-04-21 17:35:12 +08:00
|
|
|
|
"/admin/settings/rectifier",
|
|
|
|
|
|
settings,
|
|
|
|
|
|
);
|
|
|
|
|
|
return data;
|
2026-03-07 21:45:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-28 00:34:23 +08:00
|
|
|
|
// ==================== OpenAI Fast Policy Settings ====================
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* OpenAI fast/flex policy rule interface.
|
|
|
|
|
|
* Matches backend dto.OpenAIFastPolicyRule.
|
|
|
|
|
|
*/
|
|
|
|
|
|
export interface OpenAIFastPolicyRule {
|
2026-09-04 10:14:29 +08:00
|
|
|
|
service_tier: "all" | "priority" | "flex" | "ultrafast";
|
2026-07-07 15:27:23 +08:00
|
|
|
|
action: "pass" | "filter" | "block" | "force_priority";
|
2026-04-28 00:34:23 +08:00
|
|
|
|
scope: "all" | "oauth" | "apikey" | "bedrock";
|
2026-07-10 15:16:09 +08:00
|
|
|
|
user_ids?: number[];
|
2026-04-28 00:34:23 +08:00
|
|
|
|
error_message?: string;
|
|
|
|
|
|
model_whitelist?: string[];
|
2026-07-07 15:27:23 +08:00
|
|
|
|
fallback_action?: "pass" | "filter" | "block" | "force_priority";
|
2026-04-28 00:34:23 +08:00
|
|
|
|
fallback_error_message?: string;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* OpenAI fast/flex policy settings interface.
|
|
|
|
|
|
*/
|
|
|
|
|
|
export interface OpenAIFastPolicySettings {
|
|
|
|
|
|
rules: OpenAIFastPolicyRule[];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-10 11:14:17 +08:00
|
|
|
|
// ==================== Beta Policy Settings ====================
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Beta policy rule interface
|
|
|
|
|
|
*/
|
|
|
|
|
|
export interface BetaPolicyRule {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
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;
|
2026-03-10 11:14:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Beta policy settings interface
|
|
|
|
|
|
*/
|
|
|
|
|
|
export interface BetaPolicySettings {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
rules: BetaPolicyRule[];
|
2026-03-10 11:14:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Get beta policy settings
|
|
|
|
|
|
* @returns Beta policy settings
|
|
|
|
|
|
*/
|
|
|
|
|
|
export async function getBetaPolicySettings(): Promise<BetaPolicySettings> {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
const { data } = await apiClient.get<BetaPolicySettings>(
|
|
|
|
|
|
"/admin/settings/beta-policy",
|
|
|
|
|
|
);
|
|
|
|
|
|
return data;
|
2026-03-10 11:14:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Update beta policy settings
|
|
|
|
|
|
* @param settings - Beta policy settings to update
|
|
|
|
|
|
* @returns Updated settings
|
|
|
|
|
|
*/
|
|
|
|
|
|
export async function updateBetaPolicySettings(
|
2026-04-21 17:35:12 +08:00
|
|
|
|
settings: BetaPolicySettings,
|
2026-03-10 11:14:17 +08:00
|
|
|
|
): Promise<BetaPolicySettings> {
|
|
|
|
|
|
const { data } = await apiClient.put<BetaPolicySettings>(
|
2026-04-21 17:35:12 +08:00
|
|
|
|
"/admin/settings/beta-policy",
|
|
|
|
|
|
settings,
|
|
|
|
|
|
);
|
|
|
|
|
|
return data;
|
2026-03-10 11:14:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-12 00:02:26 +08:00
|
|
|
|
// --- Web Search Emulation Config ---
|
|
|
|
|
|
|
|
|
|
|
|
export interface WebSearchProviderConfig {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
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;
|
2026-04-12 00:02:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export interface WebSearchEmulationConfig {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
enabled: boolean;
|
|
|
|
|
|
providers: WebSearchProviderConfig[];
|
2026-04-12 00:02:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-12 13:11:46 +08:00
|
|
|
|
export interface WebSearchTestResult {
|
2026-04-21 17:35:12 +08:00
|
|
|
|
provider: string;
|
|
|
|
|
|
results: { url: string; title: string; snippet: string; page_age?: string }[];
|
|
|
|
|
|
query: string;
|
2026-04-12 13:11:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-12 00:02:26 +08:00
|
|
|
|
export async function getWebSearchEmulationConfig(): Promise<WebSearchEmulationConfig> {
|
|
|
|
|
|
const { data } = await apiClient.get<WebSearchEmulationConfig>(
|
2026-04-21 17:35:12 +08:00
|
|
|
|
"/admin/settings/web-search-emulation",
|
|
|
|
|
|
);
|
|
|
|
|
|
return data;
|
2026-04-12 00:02:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export async function updateWebSearchEmulationConfig(
|
2026-04-21 17:35:12 +08:00
|
|
|
|
config: WebSearchEmulationConfig,
|
2026-04-12 00:02:26 +08:00
|
|
|
|
): Promise<WebSearchEmulationConfig> {
|
|
|
|
|
|
const { data } = await apiClient.put<WebSearchEmulationConfig>(
|
2026-04-21 17:35:12 +08:00
|
|
|
|
"/admin/settings/web-search-emulation",
|
|
|
|
|
|
config,
|
|
|
|
|
|
);
|
|
|
|
|
|
return data;
|
2026-04-12 00:02:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-12 13:11:46 +08:00
|
|
|
|
export async function testWebSearchEmulation(
|
2026-04-21 17:35:12 +08:00
|
|
|
|
query: string,
|
2026-04-12 13:11:46 +08:00
|
|
|
|
): Promise<WebSearchTestResult> {
|
|
|
|
|
|
const { data } = await apiClient.post<WebSearchTestResult>(
|
2026-04-21 17:35:12 +08:00
|
|
|
|
"/admin/settings/web-search-emulation/test",
|
|
|
|
|
|
{ query },
|
|
|
|
|
|
);
|
|
|
|
|
|
return data;
|
2026-04-12 13:11:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-21 17:35:12 +08:00
|
|
|
|
export async function resetWebSearchUsage(payload: {
|
|
|
|
|
|
provider_type: string;
|
|
|
|
|
|
}): Promise<void> {
|
|
|
|
|
|
await apiClient.post(
|
|
|
|
|
|
"/admin/settings/web-search-emulation/reset-usage",
|
|
|
|
|
|
payload,
|
|
|
|
|
|
);
|
2026-04-14 08:03:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-18 13:50:39 +08:00
|
|
|
|
export const settingsAPI = {
|
|
|
|
|
|
getSettings,
|
|
|
|
|
|
updateSettings,
|
|
|
|
|
|
testSmtpConnection,
|
|
|
|
|
|
sendTestEmail,
|
2026-05-20 11:07:51 +08:00
|
|
|
|
getEmailTemplates,
|
|
|
|
|
|
getEmailTemplate,
|
|
|
|
|
|
updateEmailTemplate,
|
|
|
|
|
|
restoreOfficialEmailTemplate,
|
|
|
|
|
|
previewEmailTemplate,
|
2025-12-20 15:11:43 +08:00
|
|
|
|
getAdminApiKey,
|
|
|
|
|
|
regenerateAdminApiKey,
|
2026-01-11 21:54:52 -08:00
|
|
|
|
deleteAdminApiKey,
|
2026-03-18 16:22:19 +08:00
|
|
|
|
getOverloadCooldownSettings,
|
|
|
|
|
|
updateOverloadCooldownSettings,
|
2026-04-30 03:01:39 +00:00
|
|
|
|
getRateLimit429CooldownSettings,
|
|
|
|
|
|
updateRateLimit429CooldownSettings,
|
2026-07-27 15:12:51 +08:00
|
|
|
|
getPanelRateLimitSettings,
|
|
|
|
|
|
updatePanelRateLimitSettings,
|
2026-01-11 21:54:52 -08:00
|
|
|
|
getStreamTimeoutSettings,
|
2026-02-28 15:01:20 +08:00
|
|
|
|
updateStreamTimeoutSettings,
|
2026-03-07 21:45:18 +08:00
|
|
|
|
getRectifierSettings,
|
|
|
|
|
|
updateRectifierSettings,
|
2026-03-10 11:14:17 +08:00
|
|
|
|
getBetaPolicySettings,
|
2026-04-12 00:02:26 +08:00
|
|
|
|
updateBetaPolicySettings,
|
|
|
|
|
|
getWebSearchEmulationConfig,
|
2026-04-12 13:11:46 +08:00
|
|
|
|
updateWebSearchEmulationConfig,
|
2026-04-14 08:03:27 +08:00
|
|
|
|
testWebSearchEmulation,
|
2026-04-21 17:35:12 +08:00
|
|
|
|
resetWebSearchUsage,
|
|
|
|
|
|
};
|
2025-12-18 13:50:39 +08:00
|
|
|
|
|
2026-04-21 17:35:12 +08:00
|
|
|
|
export default settingsAPI;
|