Merge remote-tracking branch 'origin/pr/498'

This commit is contained in:
fawney19
2026-05-21 22:56:43 +08:00
89 changed files with 18396 additions and 382 deletions

View File

@@ -79,6 +79,137 @@ export interface OAuthBatchImportTaskStatusResponse {
updated_at: number
}
export type BatchImportCredentialsNormalization =
| { ok: true; isBatch: boolean; credentials: string }
| { ok: false; message: string }
function getImportCredentialLines(text: string): Array<{ lineNumber: number; text: string }> {
return text
.split('\n')
.map((line, index) => ({ lineNumber: index + 1, text: line.trim() }))
.filter(line => line.text && !line.text.startsWith('#'))
}
function jsonParseErrorMessage(error: unknown): string {
return error instanceof Error ? error.message : String(error)
}
function normalizeBatchImportItem(
value: unknown,
location: string,
): { ok: true; value: string | Record<string, unknown> } | { ok: false; message: string } {
if (typeof value === 'string') {
const trimmed = value.trim()
if (trimmed) return { ok: true, value: trimmed }
return { ok: false, message: `${location} 不能为空字符串` }
}
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
return { ok: true, value: value as Record<string, unknown> }
}
return { ok: false, message: `${location} 必须是 JSON 对象或字符串` }
}
function normalizeBatchImportArray(items: unknown[]): BatchImportCredentialsNormalization {
if (items.length === 0) {
return { ok: false, message: 'JSON 数组不能为空' }
}
const normalized: Array<string | Record<string, unknown>> = []
for (const [index, item] of items.entries()) {
const result = normalizeBatchImportItem(item, `JSON 数组第 ${index + 1}`)
if (!result.ok) return result
normalized.push(result.value)
}
return {
ok: true,
isBatch: true,
credentials: JSON.stringify(normalized),
}
}
function parseImportCredentialLines(
lines: Array<{ lineNumber: number; text: string }>,
): BatchImportCredentialsNormalization {
const normalized: Array<string | Record<string, unknown>> = []
for (const line of lines) {
const firstChar = line.text[0]
if (firstChar === '{' || firstChar === '[') {
let parsed: unknown
try {
parsed = JSON.parse(line.text)
} catch (error) {
return {
ok: false,
message: `JSON Lines 格式无效,请检查第 ${line.lineNumber} 行: ${jsonParseErrorMessage(error)}`,
}
}
if (Array.isArray(parsed)) {
for (const [index, item] of parsed.entries()) {
const result = normalizeBatchImportItem(item, `${line.lineNumber} 行数组第 ${index + 1}`)
if (!result.ok) return result
normalized.push(result.value)
}
continue
}
const result = normalizeBatchImportItem(parsed, `${line.lineNumber}`)
if (!result.ok) return result
normalized.push(result.value)
continue
}
normalized.push(line.text)
}
return normalizeBatchImportArray(normalized)
}
export function normalizeBatchImportCredentials(text: string): BatchImportCredentialsNormalization {
const trimmed = text.trim()
if (!trimmed) {
return { ok: false, message: '请输入凭据数据' }
}
const firstChar = trimmed[0]
if (firstChar === '[') {
try {
const parsed: unknown = JSON.parse(trimmed)
if (!Array.isArray(parsed)) {
return { ok: false, message: 'JSON 批量凭据必须是数组' }
}
return normalizeBatchImportArray(parsed)
} catch (error) {
return { ok: false, message: `JSON 数组格式无效: ${jsonParseErrorMessage(error)}` }
}
}
if (firstChar === '{') {
try {
const parsed: unknown = JSON.parse(trimmed)
if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
return { ok: true, isBatch: false, credentials: trimmed }
}
return { ok: false, message: '单条 JSON 凭据必须是对象' }
} catch (error) {
const lines = getImportCredentialLines(trimmed)
if (lines.length > 1) {
return parseImportCredentialLines(lines)
}
return { ok: false, message: `JSON 格式无效: ${jsonParseErrorMessage(error)}` }
}
}
const lines = getImportCredentialLines(trimmed)
if (lines.length > 1) {
return parseImportCredentialLines(lines)
}
return { ok: true, isBatch: false, credentials: trimmed }
}
export async function refreshProviderOAuth(keyId: string): Promise<ProviderOAuthCompleteResponse> {
const resp = await client.post(`/api/admin/provider-oauth/keys/${keyId}/refresh`)
return resp.data
@@ -102,8 +233,14 @@ export async function completeProviderLevelOAuth(
export async function importProviderRefreshToken(
providerId: string,
data: {
api_key?: string
apiKey?: string
token?: string
auth_token?: string
authToken?: string
refresh_token?: string
access_token?: string
password?: string
expires_at?: number
name?: string
proxy_node_id?: string
@@ -150,7 +287,8 @@ export async function getBatchImportOAuthTaskStatus(
export interface DeviceAuthorizeRequest {
start_url?: string
region?: string
auth_type?: 'builder_id' | 'identity_center' | 'google' | 'github'
auth_type?: 'builder_id' | 'identity_center' | 'google' | 'github' | 'browser'
login_option?: 'google' | 'github' | 'default'
redirect_uri?: string
proxy_node_id?: string
}
@@ -170,6 +308,7 @@ export interface DeviceAuthorizeResponse {
export interface DevicePollRequest {
session_id: string
callback_url?: string
token?: string
}
export interface DevicePollResponse {

View File

@@ -5,6 +5,7 @@ import type {
FailoverRulesConfig,
PoolAdvancedConfig,
ProviderConfig,
ProviderType,
ProviderWithEndpointsSummary,
ProxyConfig,
} from './types'
@@ -92,7 +93,7 @@ export async function updateProvider(
providerId: string,
data: Partial<{
name: string
provider_type: 'custom' | 'vertex_ai' | 'claude_code' | 'codex' | 'chatgpt_web' | 'gemini_cli' | 'antigravity' | 'kiro' | 'grok'
provider_type: ProviderType
description: string | null
website: string
provider_priority: number
@@ -127,7 +128,7 @@ export async function updateProvider(
export async function createProvider(
data: {
name: string
provider_type?: 'custom' | 'vertex_ai' | 'claude_code' | 'codex' | 'chatgpt_web' | 'gemini_cli' | 'antigravity' | 'kiro' | 'grok'
provider_type?: ProviderType
description?: string
website?: string
billing_type?: 'monthly_quota' | 'pay_as_you_go' | 'free_tier'

View File

@@ -370,6 +370,32 @@ export interface KiroUpstreamMetadata {
banned_at?: number // 封禁时间Unix 时间戳,秒)
}
// Windsurf 上游配额信息
export interface WindsurfUpstreamMetadata {
updated_at?: number
plan_name?: string
daily_remaining_percent?: number | null
weekly_remaining_percent?: number | null
daily_reset_at?: number | null
weekly_reset_at?: number | null
prompt_used?: number | null
prompt_limit?: number | null
prompt_remaining?: number | null
flex_used?: number | null
flex_limit?: number | null
flex_remaining?: number | null
allowed_models_count?: number | null
models?: Array<{
model_uid?: string | null
label?: string | null
provider?: string | null
supports_images?: boolean | null
credit_multiplier?: number | null
}> | null
rate_limit?: Record<string, unknown> | null
last_error?: string | null
}
export interface ChatGPTWebUpstreamMetadata {
updated_at?: number // Unix 时间戳(秒)
plan_type?: string | null
@@ -406,6 +432,7 @@ export interface UpstreamMetadata {
codex?: CodexUpstreamMetadata
antigravity?: AntigravityUpstreamMetadata
kiro?: KiroUpstreamMetadata
windsurf?: WindsurfUpstreamMetadata
chatgpt_web?: ChatGPTWebUpstreamMetadata
grok?: GrokUpstreamMetadata
}
@@ -535,7 +562,7 @@ export interface PublicEndpointStatusMonitorResponse {
formats: PublicEndpointStatusMonitor[]
}
export type ProviderType = 'custom' | 'claude_code' | 'codex' | 'chatgpt_web' | 'gemini_cli' | 'antigravity' | 'kiro' | 'grok' | 'vertex_ai'
export type ProviderType = 'custom' | 'claude_code' | 'codex' | 'chatgpt_web' | 'gemini_cli' | 'antigravity' | 'kiro' | 'grok' | 'windsurf' | 'vertex_ai'
export interface ClaudeCodeAdvancedConfig {
// 会话数量控制null/undefined 表示不限制

View File

@@ -66,6 +66,8 @@ export interface QuotaStatusSnapshot {
plan_type?: string | null
pool_tier?: string | null
credits?: QuotaCreditsSnapshot | null
allowed_models_count?: number | null
rate_limit?: Record<string, unknown> | null
windows?: QuotaWindowSnapshot[] | null
}