import apiClient from './client' import type { ImageProgress } from './requestTrace' import type { ActivityHeatmap } from '@/types/activity' import type { TieredPricingConfig } from './endpoints/types' import { cachedRequest, buildCacheKey } from '@/utils/cache' import type { BillingSummary } from './auth' import type { UserSession } from '@/types/session' import type { FeatureSettingsMap } from '@/utils/featureSettings' const ACTIVITY_HEATMAP_CACHE_TTL_MS = 30 * 60 * 1000 export type { UserSession } export interface Profile { id: string // UUID email?: string | null username: string role: string is_active: boolean billing: BillingSummary created_at: string updated_at?: string last_login_at?: string auth_source: 'local' | 'ldap' | 'oauth' has_password: boolean preferences?: UserPreferences feature_settings?: FeatureSettingsMap | null } export interface UserPreferences { avatar_url?: string bio?: string default_provider_id?: string // UUID default_provider?: Record | string | null // 仅管理员可见 theme: string language: string timezone?: string notifications?: { email?: boolean usage_alerts?: boolean announcements?: boolean } } // 提供商配置接口 export interface ProviderConfig { provider_id: string priority: number // 优先级(越高越优先) weight: number // 负载均衡权重 enabled: boolean // 是否启用 } // 使用记录接口 export interface UsageRecordDetail { id: string provider?: string // 仅管理员可见 model: string request_type?: string | null requested_reasoning_effort?: string | null reasoning_effort?: string | null service_tier?: string | null actual_service_tier?: string | null response_model?: string | null input_tokens: number effective_input_tokens?: number output_tokens: number total_tokens: number cost: number // 官方费率 actual_cost?: number // 倍率消耗(仅管理员可见) rate_multiplier?: number // 成本倍率(仅管理员可见) response_time_ms?: number | null first_byte_time_ms?: number | null end_to_end_time_ms?: number | null end_to_end_first_byte_time_ms?: number | null updated_at?: string | null response_time_updated_at?: string | null is_stream: boolean is_websocket?: boolean websocket_transport?: string | null usage_available?: boolean usage_pricing_available?: boolean input_audio_tokens?: number | null output_audio_tokens?: number | null 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 cache_creation_ephemeral_1h_input_tokens?: number cache_read_input_tokens?: number status_code: number error_message?: string input_price_per_1m: number output_price_per_1m: number cache_creation_price_per_1m?: number cache_read_price_per_1m?: number price_per_request?: number // 按次计费价格 has_fallback?: boolean api_key?: { id: string name: string display: string } } // 模型统计接口 export interface ModelSummary { model: string requests: number input_tokens: number effective_input_tokens?: number output_tokens: number total_tokens: number cache_read_tokens?: number cache_creation_tokens?: number total_input_context?: number cache_hit_rate?: number total_cost_usd: number actual_total_cost_usd?: number // 倍率消耗(仅管理员可见) } // 提供商统计接口 export interface ProviderSummary { provider: string requests: number effective_input_tokens?: number total_tokens: number output_tokens?: number cache_read_tokens?: number cache_creation_tokens?: number total_input_context?: number cache_hit_rate?: number total_cost_usd: number success_rate: number | null avg_response_time_ms: number | null } // API 格式统计接口 export interface ApiFormatSummary { api_format: string request_count: number effective_input_tokens?: number total_tokens: number output_tokens?: number cache_read_tokens: number cache_creation_tokens?: number total_input_context?: number cache_hit_rate: number total_cost_usd: number avg_response_time_ms: number } // 使用统计响应接口 export interface UsageResponse { total_requests: number total_input_tokens: number total_output_tokens: number total_tokens: number total_cost: number // 官方费率 total_actual_cost?: number // 倍率消耗(仅管理员可见) avg_response_time: number billing: BillingSummary summary_by_model: ModelSummary[] summary_by_provider?: ProviderSummary[] summary_by_api_format?: ApiFormatSummary[] pagination?: { total: number limit: number offset: number has_more: boolean } records: UsageRecordDetail[] activity_heatmap?: ActivityHeatmap | null } export interface ApiKey { id: string // UUID name: string key?: string key_display: string is_active: boolean is_locked: boolean // 管理员锁定标志 last_used_at?: string | null created_at?: string | null total_requests?: number total_cost_usd?: number rate_limit?: number | null concurrent_limit?: number | null ip_rules?: string[] | null allowed_providers?: ProviderConfig[] force_capabilities?: Record | null // 强制能力配置 feature_settings?: FeatureSettingsMap | null } export type InstallTargetCli = 'claude_code' | 'codex_cli' | 'gemini_cli' export type InstallTargetSystem = 'macos' | 'linux' | 'windows' | 'auto' export type InstallSessionTargetSystem = Exclude export interface ApiKeyInstallSession { install_code: string expires_at_unix_secs: number expires_in_seconds: number target_cli: InstallTargetCli target_cli_label: string target_system: InstallTargetSystem target_system_label: string unix_command: string powershell_command: string } export interface UserClientConfig { base_url: string site_name?: string } // 不再需要 ProviderBinding 接口 export interface ChangePasswordRequest { old_password?: string // 可选:首次设置密码时不需要 new_password: string } export const meApi = { // 获取个人信息 async getProfile(): Promise { const response = await apiClient.get('/api/users/me') return response.data }, // 更新个人信息 async updateProfile(data: { email?: string email_verification_token?: string username?: string feature_settings?: FeatureSettingsMap | null }): Promise<{ message: string }> { const response = await apiClient.put<{ message: string }>('/api/users/me', data) return response.data }, // 修改密码 async changePassword(data: ChangePasswordRequest): Promise<{ message: string }> { const response = await apiClient.patch<{ message: string }>('/api/users/me/password', data) return response.data }, async listSessions(): Promise { const response = await apiClient.get('/api/users/me/sessions') return response.data }, async updateSessionLabel(sessionId: string, deviceLabel: string): Promise { const response = await apiClient.patch(`/api/users/me/sessions/${sessionId}`, { device_label: deviceLabel, }) return response.data }, async revokeSession(sessionId: string): Promise<{ message: string }> { const response = await apiClient.delete<{ message: string }>(`/api/users/me/sessions/${sessionId}`) return response.data }, async revokeOtherSessions(): Promise<{ message: string; revoked_count: number }> { const response = await apiClient.delete<{ message: string; revoked_count: number }>('/api/users/me/sessions/others') return response.data }, // API密钥管理 async getApiKeys(): Promise { const response = await apiClient.get('/api/users/me/api-keys') return response.data }, async createApiKey(data: { name: string; rate_limit?: number | null; concurrent_limit?: number | null; ip_rules?: string[] | null; feature_settings?: FeatureSettingsMap | null }): Promise { const response = await apiClient.post('/api/users/me/api-keys', data) return response.data }, async getApiKeyDetail(keyId: string, includeKey: boolean = false): Promise { const response = await apiClient.get( `/api/users/me/api-keys/${keyId}`, { params: { include_key: includeKey } } ) return response.data }, async getFullApiKey(keyId: string): Promise<{ key: string }> { const response = await apiClient.get<{ key: string }>( `/api/users/me/api-keys/${keyId}`, { params: { include_key: true } } ) return response.data }, async getClientConfig(): Promise { const response = await apiClient.get('/api/users/me/client-config') return response.data }, async deleteApiKey(keyId: string): Promise<{ message: string }> { const response = await apiClient.delete<{ message: string }>(`/api/users/me/api-keys/${keyId}`) return response.data }, async toggleApiKey(keyId: string, isActive: boolean): Promise { const response = await apiClient.patch(`/api/users/me/api-keys/${keyId}`, { is_active: isActive, }) return response.data }, async updateApiKey( keyId: string, data: { name?: string; rate_limit?: number | null; concurrent_limit?: number | null; ip_rules?: string[] | null; feature_settings?: FeatureSettingsMap | null | undefined } ): Promise { const response = await apiClient.put( `/api/users/me/api-keys/${keyId}`, data ) return response.data }, async createApiKeyInstallSession( keyId: string, data: { target_cli: InstallTargetCli; target_system: InstallSessionTargetSystem } ): Promise { const response = await apiClient.post( `/api/users/me/api-keys/${keyId}/install-sessions`, data ) return response.data }, // 使用统计 async getUsage(params?: { start_date?: string end_date?: string preset?: string timezone?: string tz_offset_minutes?: number search?: string // 通用搜索:密钥名、模型名 api_format?: string status?: string limit?: number offset?: number }): Promise { const response = await apiClient.get('/api/users/me/usage', { params }) return response.data }, // 获取活跃请求状态(用于轮询更新) async getActiveRequests(ids?: string): Promise<{ requests: Array<{ id: string status: 'pending' | 'streaming' | 'completed' | 'failed' | 'cancelled' input_tokens: number effective_input_tokens?: number | null output_tokens: number cache_creation_input_tokens?: number | null cache_creation_ephemeral_5m_input_tokens?: number | null cache_creation_ephemeral_1h_input_tokens?: number | null cache_read_input_tokens?: number | null cost: number actual_cost?: number | null rate_multiplier?: number | null response_time_ms: number | null first_byte_time_ms: number | null end_to_end_time_ms?: number | null end_to_end_first_byte_time_ms?: number | null updated_at?: string | null response_time_updated_at?: string | null status_code?: number | null error_message?: string | null api_format?: string | null endpoint_api_format?: string | null is_stream?: boolean | null is_websocket?: boolean | null websocket_transport?: string | null usage_available?: boolean | null usage_pricing_available?: boolean | null input_audio_tokens?: number | null output_audio_tokens?: number | null upstream_is_stream?: boolean | null client_requested_stream?: boolean | null client_is_stream?: boolean | null has_format_conversion?: boolean | null has_fallback?: boolean | null target_model?: string | null response_model?: string | null request_type?: string | null requested_reasoning_effort?: string | null reasoning_effort?: string | null service_tier?: string | null actual_service_tier?: string | null image_progress?: ImageProgress | null }> }> { const params = ids ? { ids } : {} const response = await apiClient.get<{ requests: Array<{ id: string status: 'pending' | 'streaming' | 'completed' | 'failed' | 'cancelled' input_tokens: number effective_input_tokens?: number | null output_tokens: number cache_creation_input_tokens?: number | null cache_creation_ephemeral_5m_input_tokens?: number | null cache_creation_ephemeral_1h_input_tokens?: number | null cache_read_input_tokens?: number | null cost: number actual_cost?: number | null rate_multiplier?: number | null response_time_ms: number | null first_byte_time_ms: number | null end_to_end_time_ms?: number | null end_to_end_first_byte_time_ms?: number | null updated_at?: string | null response_time_updated_at?: string | null status_code?: number | null error_message?: string | null api_format?: string | null endpoint_api_format?: string | null is_stream?: boolean | null is_websocket?: boolean | null websocket_transport?: string | null usage_available?: boolean | null usage_pricing_available?: boolean | null input_audio_tokens?: number | null output_audio_tokens?: number | null upstream_is_stream?: boolean | null client_requested_stream?: boolean | null client_is_stream?: boolean | null has_format_conversion?: boolean | null has_fallback?: boolean | null target_model?: string | null response_model?: string | null request_type?: string | null requested_reasoning_effort?: string | null reasoning_effort?: string | null service_tier?: string | null actual_service_tier?: string | null image_progress?: ImageProgress | null }> }>('/api/users/me/usage/active', { params }) return response.data }, // 获取可用的提供商 async getAvailableProviders(): Promise>> { const response = await apiClient.get>>('/api/users/me/providers') return response.data }, // 获取用户可用的模型列表 async getAvailableModels(params?: { skip?: number limit?: number search?: string }): Promise<{ models: Array<{ id: string name: string display_name: string | null is_active: boolean default_price_per_request: number | null default_tiered_pricing: TieredPricingConfig | null supported_capabilities: string[] | null supports_embedding?: boolean | null config: Record | null usage_count: number }> total: number }> { const response = await apiClient.get<{ models: Array<{ id: string name: string display_name: string | null is_active: boolean default_price_per_request: number | null default_tiered_pricing: TieredPricingConfig | null supported_capabilities: string[] | null supports_embedding?: boolean | null config: Record | null usage_count: number }> total: number }>('/api/users/me/available-models', { params }) return response.data }, // 获取端点状态(不包含敏感信息) async getEndpointStatus(): Promise>> { const response = await apiClient.get>>('/api/users/me/endpoint-status') return response.data }, // 偏好设置 async getPreferences(): Promise { const response = await apiClient.get('/api/users/me/preferences') return response.data }, async updatePreferences(data: Partial): Promise<{ message: string }> { const response = await apiClient.put<{ message: string }>('/api/users/me/preferences', data) return response.data }, // 提供商绑定管理相关方法已移除,改为直接从可用提供商中选择 // API密钥提供商关联 async updateApiKeyProviders(keyId: string, data: { allowed_providers?: ProviderConfig[] }): Promise<{ message: string }> { const response = await apiClient.put<{ message: string }>(`/api/users/me/api-keys/${keyId}/providers`, data) return response.data }, // API密钥能力配置 async updateApiKeyCapabilities(keyId: string, data: { force_capabilities?: Record | null }): Promise<{ message: string; force_capabilities?: Record | null }> { const response = await apiClient.put<{ message: string; force_capabilities?: Record | null }>(`/api/users/me/api-keys/${keyId}/capabilities`, data) return response.data }, // 模型能力配置 async getModelCapabilitySettings(): Promise<{ model_capability_settings: Record> }> { const response = await apiClient.get<{ model_capability_settings: Record> }>('/api/users/me/model-capabilities') return response.data }, async updateModelCapabilitySettings(data: { model_capability_settings: Record> | null }): Promise<{ message: string model_capability_settings: Record> | null }> { const response = await apiClient.put<{ message: string model_capability_settings: Record> | null }>('/api/users/me/model-capabilities', data) return response.data }, // 获取请求间隔时间线(用于散点图) async getIntervalTimeline(params?: { hours?: number limit?: number }): Promise<{ analysis_period_hours: number total_points: number points: Array<{ x: string; y: number; model?: string }> models?: string[] }> { const cacheKey = buildCacheKey('me:interval-timeline', params as Record | undefined) return cachedRequest( cacheKey, async () => { const response = await apiClient.get<{ analysis_period_hours: number total_points: number points: Array<{ x: string; y: number; model?: string }> models?: string[] }>('/api/users/me/usage/interval-timeline', { params }) return response.data }, 30000 ) }, /** * 获取活跃度热力图数据(用户) * 历史热力图变化很慢,前端做长缓存,避免短时间重复请求。 */ async getActivityHeatmap(): Promise { return cachedRequest( 'me-activity-heatmap', async () => { const response = await apiClient.get('/api/users/me/usage/heatmap') return response.data }, ACTIVITY_HEATMAP_CACHE_TTL_MS ) } }