Merge upstream main into feat/500-api-key-ip-whitelist

This commit is contained in:
RWDai
2026-05-20 10:26:56 +08:00
501 changed files with 47013 additions and 3667 deletions
+3
View File
@@ -1,4 +1,5 @@
import apiClient from './client'
import type { ModelTestCapabilities } from './endpoints/types'
import axios from 'axios'
import { cachedRequest, buildCacheKey } from '@/utils/cache'
import type { BillingSummary } from './auth'
@@ -420,6 +421,8 @@ export interface ProviderModelsQueryResponse {
owned_by?: string
display_name?: string
api_format?: string
api_formats?: string[]
model_test_capabilities?: ModelTestCapabilities | null
}>
error?: string
from_cache?: boolean
+9 -1
View File
@@ -8,6 +8,7 @@ export interface Announcement {
priority: number
is_pinned: boolean
is_active: boolean
requires_ack: boolean
author: {
id: string // UUID
username: string
@@ -31,6 +32,7 @@ export interface CreateAnnouncementRequest {
type?: 'info' | 'warning' | 'maintenance' | 'important'
priority?: number
is_pinned?: boolean
requires_ack?: boolean
start_time?: string
end_time?: string
}
@@ -42,6 +44,7 @@ export interface UpdateAnnouncementRequest {
priority?: number
is_active?: boolean
is_pinned?: boolean
requires_ack?: boolean
start_time?: string
end_time?: string
}
@@ -88,6 +91,11 @@ export const announcementApi = {
return response.data
},
async getRequiredUnreadAnnouncements(): Promise<AnnouncementListResponse> {
const response = await apiClient.get('/api/announcements/users/me/required-unread')
return response.data
},
// 管理员方法
// 创建公告
async createAnnouncement(data: CreateAnnouncementRequest): Promise<{ id: string; title: string; message: string }> {
@@ -106,4 +114,4 @@ export const announcementApi = {
const response = await apiClient.delete(`/api/announcements/${id}`)
return response.data
}
}
}
+11
View File
@@ -69,6 +69,9 @@ export interface RegisterRequest {
username: string
password: string
turnstile_token?: string
invite_code?: string
privacy_policy_accepted?: boolean
privacy_policy_version?: string
}
export interface RegisterResponse {
@@ -86,6 +89,14 @@ export interface RegistrationSettingsResponse {
turnstile_enabled?: boolean
turnstile_site_key?: string | null
turnstile_required_actions?: string[]
privacy_policy?: RegistrationPrivacyPolicySettings
}
export interface RegistrationPrivacyPolicySettings {
enabled: boolean
format: 'markdown' | 'html'
content: string
version: string
}
export interface AuthSettingsResponse {
+1
View File
@@ -196,6 +196,7 @@ export interface RequestDetail {
total_cost?: number
cache_creation_cost?: number
cache_read_cost?: number
image_output_cost?: number
request_cost?: number // 按次计费费用
// Historical pricing fields (per 1M tokens)
input_price_per_1m?: number
+1
View File
@@ -112,6 +112,7 @@ export interface PoolPresetMeta {
export interface PoolKeyDetail {
key_id: string
key_name: string
provider_type?: string | null
is_active: boolean
auth_type: string
auth_type_by_format?: Record<string, 'api_key' | 'bearer'> | null
@@ -111,6 +111,12 @@ export async function importProviderRefreshToken(
account_id?: string
account_user_id?: string
plan_type?: string
pool_tier?: string
sso_rw_token?: string
cf_cookies?: string
cf_clearance?: string
user_agent?: string
browser_profile?: string
user_id?: string
account_name?: string
}
+3 -2
View File
@@ -48,6 +48,7 @@ function normalizeProviderSummary(
...provider,
chat_pii_redaction: normalizeChatPiiRedactionProvider(provider.chat_pii_redaction),
pool_advanced: normalizePoolAdvanced(provider.pool_advanced),
kiro_simulated_cache_enabled: provider.kiro_simulated_cache_enabled ?? false,
}
}
@@ -91,7 +92,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'
provider_type: 'custom' | 'vertex_ai' | 'claude_code' | 'codex' | 'chatgpt_web' | 'gemini_cli' | 'antigravity' | 'kiro' | 'grok'
description: string | null
website: string
provider_priority: number
@@ -126,7 +127,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'
provider_type?: 'custom' | 'vertex_ai' | 'claude_code' | 'codex' | 'chatgpt_web' | 'gemini_cli' | 'antigravity' | 'kiro' | 'grok'
description?: string
website?: string
billing_type?: 'monthly_quota' | 'pay_as_you_go' | 'free_tier'
+11
View File
@@ -18,9 +18,20 @@ export interface PricingTier {
cache_ttl_pricing?: CacheTTLPricing[]
}
export type ImageOutputQuality = 'low' | 'medium' | 'high'
export interface ImageOutputPriceRange {
up_to_pixels: number | null
prices: Partial<Record<ImageOutputQuality, number>>
label?: string | null
}
/** 阶梯计费配置 */
export interface TieredPricingConfig {
tiers: PricingTier[]
image_output_prices?: Record<string, Record<string, number>> | null
image_output_price_default?: number | null
image_output_price_ranges?: ImageOutputPriceRange[] | null
}
export interface Model {
+26 -2
View File
@@ -10,7 +10,7 @@ export interface ProxyConfig {
url?: string
username?: string
password?: string
node_id?: string // 代理节点 IDaether-proxy 注册的节点,与 url 互斥)
node_id?: string // 代理节点 IDaether-tunnel 注册的节点,与 url 互斥)
enabled?: boolean // 是否启用代理(false 时保留配置但不使用)
}
@@ -389,11 +389,25 @@ export interface ChatGPTWebUpstreamMetadata {
user_id?: string | null
}
export interface GrokUpstreamMetadata {
updated_at?: number // Unix 时间戳(秒)
plan_type?: string | null
pool_tier?: string | null
is_banned?: boolean | null
ban_reason?: string | null
last_rate_limit_probe_at?: number | null
clearance_state?: string | null
email?: string | null
account_id?: string | null
account_user_id?: string | null
}
export interface UpstreamMetadata {
codex?: CodexUpstreamMetadata
antigravity?: AntigravityUpstreamMetadata
kiro?: KiroUpstreamMetadata
chatgpt_web?: ChatGPTWebUpstreamMetadata
grok?: GrokUpstreamMetadata
}
// 按格式的健康度数据
@@ -409,11 +423,20 @@ export interface FormatHealthData {
// 按格式的熔断器数据
export interface FormatCircuitBreakerData {
open: boolean
reason?: string | null
open_at?: string | null
next_probe_at?: string | null
next_probe_at_unix_secs?: number | null
probe_interval_minutes?: number | null
max_probe_interval_minutes?: number | null
failure_count?: number | null
consecutive_failures?: number | null
last_failure_at?: string | null
last_probe_failure_at?: string | null
half_open_until?: string | null
half_open_successes: number
half_open_failures: number
request_results_window?: Array<{ ts: number; ok: boolean }>
}
export interface EndpointAPIKeyUpdate {
@@ -512,7 +535,7 @@ export interface PublicEndpointStatusMonitorResponse {
formats: PublicEndpointStatusMonitor[]
}
export type ProviderType = 'custom' | 'claude_code' | 'codex' | 'chatgpt_web' | 'gemini_cli' | 'antigravity' | 'kiro' | 'vertex_ai'
export type ProviderType = 'custom' | 'claude_code' | 'codex' | 'chatgpt_web' | 'gemini_cli' | 'antigravity' | 'kiro' | 'grok' | 'vertex_ai'
export interface ClaudeCodeAdvancedConfig {
// 会话数量控制:null/undefined 表示不限制
@@ -661,6 +684,7 @@ export interface ProviderWithEndpointsSummary {
failover_rules?: FailoverRulesConfig | null
ops_configured: boolean // 是否配置了扩展操作(余额监控等)
ops_architecture_id?: string // 扩展操作使用的架构 ID(如 cubence, anyrouter
kiro_simulated_cache_enabled?: boolean
created_at: string
updated_at: string
}
@@ -64,6 +64,7 @@ export interface QuotaStatusSnapshot {
reset_at?: number | null
reset_seconds?: number | null
plan_type?: string | null
pool_tier?: string | null
credits?: QuotaCreditsSnapshot | null
windows?: QuotaWindowSnapshot[] | null
}
+7 -1
View File
@@ -61,11 +61,17 @@ export interface UsageRecordDetail {
cost: number // 官方费率
actual_cost?: number // 倍率消耗(仅管理员可见)
rate_multiplier?: number // 成本倍率(仅管理员可见)
response_time_ms?: number
response_time_ms?: number | null
first_byte_time_ms?: number | null
is_stream: boolean
upstream_is_stream?: boolean
client_requested_stream?: boolean
client_is_stream?: boolean
client_family?: string | null
client_ip?: string | null
user_agent?: string | null
request_path?: string | null
request_path_and_query?: string | null
created_at: string
cache_creation_input_tokens?: number
cache_creation_ephemeral_5m_input_tokens?: number
+3
View File
@@ -3,6 +3,7 @@ import apiClient from './client'
export interface OAuthProviderInfo {
provider_type: string
display_name: string
icon_url?: string | null
}
export interface OAuthProvidersResponse {
@@ -46,6 +47,7 @@ export interface OAuthProviderAdminConfig {
frontend_callback_url: string
attribute_mapping?: Record<string, unknown> | null
extra_config?: Record<string, unknown> | null
icon_url?: string | null
is_enabled: boolean
}
@@ -61,6 +63,7 @@ export interface OAuthProviderUpsertRequest {
frontend_callback_url: string
attribute_mapping?: Record<string, unknown> | null
extra_config?: Record<string, unknown> | null
icon_url?: string | null
is_enabled: boolean
force?: boolean
}
+2 -2
View File
@@ -26,10 +26,10 @@ export interface ProxyNode {
proxy_url?: string
proxy_username?: string
proxy_password?: string
// 硬件信息(aether-proxy 节点)
// 硬件信息(aether-tunnel 节点)
hardware_info: Record<string, unknown> | null
estimated_max_concurrency: number | null
// 远程配置(aether-proxy 节点)
// 远程配置(aether-tunnel 节点)
remote_config: ProxyNodeRemoteConfig | null
config_version: number
registered_by: string | null
+114
View File
@@ -0,0 +1,114 @@
import apiClient from './client'
export interface ReferralSummary {
total_invites: number
effective_invites: number
paid_reward_usd: number
pending_reward_usd: number
reversed_reward_usd: number
}
export interface ReferralDashboardResponse {
invite_code: string
invitation_link: string
summary: ReferralSummary
}
export interface ReferralRelationshipRecord {
id: string
inviter_user_id: string
inviter_username?: string | null
invitee_user_id: string
invitee_username?: string | null
invite_code_snapshot: string
first_paid_order_id?: string | null
first_paid_at_unix_secs?: number | null
source?: Record<string, unknown> | null
created_at_unix_secs: number
}
export interface ReferralRewardRecord {
id: string
referral_id: string
inviter_user_id: string
invitee_user_id: string
reward_type: string
source_order_id?: string | null
trigger_point: string
amount_usd: number
status: string
wallet_transaction_id?: string | null
idempotency_key: string
reversed_amount_usd: number
pending_reversal_amount_usd: number
admin_operator_id?: string | null
admin_note?: string | null
created_at_unix_secs: number
updated_at_unix_secs: number
}
export interface ReferralListResponse<T> {
items: T[]
total: number
limit: number
offset: number
stats: ReferralSummary
}
export interface ReferralRelationshipQuery {
inviter?: string
invitee?: string
invite_code?: string
first_paid?: boolean | null
limit?: number
offset?: number
}
export interface ReferralRewardQuery {
order_id?: string
reward_type?: string
status?: string
limit?: number
offset?: number
}
function cleanParams<T extends Record<string, unknown>>(params: T): Partial<T> {
return Object.fromEntries(
Object.entries(params).filter(([, value]) => value !== undefined && value !== null && value !== '')
) as Partial<T>
}
export const referralApi = {
async getMyReferral(): Promise<ReferralDashboardResponse> {
const response = await apiClient.get<ReferralDashboardResponse>('/api/users/me/referral')
return response.data
},
async getAdminReferrals(
params: ReferralRelationshipQuery = {}
): Promise<ReferralListResponse<ReferralRelationshipRecord>> {
const response = await apiClient.get('/api/admin/referrals', {
params: cleanParams(params as Record<string, unknown>)
})
return response.data
},
async getAdminReferralRewards(
params: ReferralRewardQuery = {}
): Promise<ReferralListResponse<ReferralRewardRecord>> {
const response = await apiClient.get('/api/admin/referral-rewards', {
params: cleanParams(params as Record<string, unknown>)
})
return response.data
},
async retryReferralReward(id: string, note?: string): Promise<{ reward: ReferralRewardRecord }> {
const response = await apiClient.post(`/api/admin/referral-rewards/${id}/retry`, { note })
return response.data
},
async voidReferralReward(id: string, note?: string): Promise<{ reward: ReferralRewardRecord }> {
const response = await apiClient.post(`/api/admin/referral-rewards/${id}/void`, { note })
return response.data
}
}
+19
View File
@@ -9,6 +9,17 @@ export interface IPBlacklistEntry {
ttl?: number
}
export interface BlacklistListEntry {
ip_address: string
reason: string
ttl_seconds?: number | null
}
export interface BlacklistResponse {
items: BlacklistListEntry[]
total: number
}
export interface IPWhitelistEntry {
ip_address: string
}
@@ -50,6 +61,14 @@ export const blacklistApi = {
async getStats(): Promise<BlacklistStats> {
const response = await apiClient.get('/api/admin/security/ip/blacklist/stats')
return response.data
},
/**
* 获取黑名单列表
*/
async getList(): Promise<BlacklistResponse> {
const response = await apiClient.get('/api/admin/security/ip/blacklist')
return response.data
}
}
+6
View File
@@ -24,6 +24,11 @@ export interface UsageRecord {
response_time?: number
created_at: string
has_fallback?: boolean // 🆕 是否发生了 fallback
client_family?: string | null
client_ip?: string | null
user_agent?: string | null
request_path?: string | null
request_path_and_query?: string | null
}
export interface UsageStats {
@@ -107,6 +112,7 @@ export interface UsageFilters {
granularity?: 'hour' | 'day' | 'week' | 'month'
timezone?: string
tz_offset_minutes?: number
client_family?: string
page?: number
page_size?: number
}