fix: restore security hardening compatibility and validation

Restore authorized rule reveal, explicit full HTTP capture and retention, video task business fields, and valid payment URLs. Add opt-in credential preservation for trusted recovery, fix frontend type contracts and async races, and eliminate PostgreSQL test fixture resource leaks. Document audit coverage and successful fmt and CI-scoped Clippy checks.
This commit is contained in:
elky
2026-09-07 21:14:27 +08:00
parent a5c3699ae9
commit a90d564931
191 changed files with 6785 additions and 1643 deletions
+86 -16
View File
@@ -1,4 +1,5 @@
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'
@@ -238,13 +239,13 @@ export const meApi = {
username?: string
feature_settings?: FeatureSettingsMap | null
}): Promise<{ message: string }> {
const response = await apiClient.put('/api/users/me', data)
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('/api/users/me/password', data)
const response = await apiClient.patch<{ message: string }>('/api/users/me/password', data)
return response.data
},
@@ -261,12 +262,12 @@ export const meApi = {
},
async revokeSession(sessionId: string): Promise<{ message: string }> {
const response = await apiClient.delete(`/api/users/me/sessions/${sessionId}`)
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('/api/users/me/sessions/others')
const response = await apiClient.delete<{ message: string; revoked_count: number }>('/api/users/me/sessions/others')
return response.data
},
@@ -303,7 +304,7 @@ export const meApi = {
},
async deleteApiKey(keyId: string): Promise<{ message: string }> {
const response = await apiClient.delete(`/api/users/me/api-keys/${keyId}`)
const response = await apiClient.delete<{ message: string }>(`/api/users/me/api-keys/${keyId}`)
return response.data
},
@@ -396,16 +397,61 @@ export const meApi = {
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('/api/users/me/usage/active', { params })
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
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<Array<Record<string, unknown>>> {
const response = await apiClient.get('/api/users/me/providers')
const response = await apiClient.get<Array<Record<string, unknown>>>('/api/users/me/providers')
return response.data
},
@@ -429,24 +475,38 @@ export const meApi = {
}>
total: number
}> {
const response = await apiClient.get('/api/users/me/available-models', { params })
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<string, unknown> | null
usage_count: number
}>
total: number
}>('/api/users/me/available-models', { params })
return response.data
},
// 获取端点状态(不包含敏感信息)
async getEndpointStatus(): Promise<Array<Record<string, unknown>>> {
const response = await apiClient.get('/api/users/me/endpoint-status')
const response = await apiClient.get<Array<Record<string, unknown>>>('/api/users/me/endpoint-status')
return response.data
},
// 偏好设置
async getPreferences(): Promise<UserPreferences> {
const response = await apiClient.get('/api/users/me/preferences')
const response = await apiClient.get<UserPreferences>('/api/users/me/preferences')
return response.data
},
async updatePreferences(data: Partial<UserPreferences>): Promise<{ message: string }> {
const response = await apiClient.put('/api/users/me/preferences', data)
const response = await apiClient.put<{ message: string }>('/api/users/me/preferences', data)
return response.data
},
@@ -456,7 +516,7 @@ export const meApi = {
async updateApiKeyProviders(keyId: string, data: {
allowed_providers?: ProviderConfig[]
}): Promise<{ message: string }> {
const response = await apiClient.put(`/api/users/me/api-keys/${keyId}/providers`, data)
const response = await apiClient.put<{ message: string }>(`/api/users/me/api-keys/${keyId}/providers`, data)
return response.data
},
@@ -464,7 +524,7 @@ export const meApi = {
async updateApiKeyCapabilities(keyId: string, data: {
force_capabilities?: Record<string, boolean> | null
}): Promise<{ message: string; force_capabilities?: Record<string, boolean> | null }> {
const response = await apiClient.put(`/api/users/me/api-keys/${keyId}/capabilities`, data)
const response = await apiClient.put<{ message: string; force_capabilities?: Record<string, boolean> | null }>(`/api/users/me/api-keys/${keyId}/capabilities`, data)
return response.data
},
@@ -472,7 +532,9 @@ export const meApi = {
async getModelCapabilitySettings(): Promise<{
model_capability_settings: Record<string, Record<string, boolean>>
}> {
const response = await apiClient.get('/api/users/me/model-capabilities')
const response = await apiClient.get<{
model_capability_settings: Record<string, Record<string, boolean>>
}>('/api/users/me/model-capabilities')
return response.data
},
@@ -482,7 +544,10 @@ export const meApi = {
message: string
model_capability_settings: Record<string, Record<string, boolean>> | null
}> {
const response = await apiClient.put('/api/users/me/model-capabilities', data)
const response = await apiClient.put<{
message: string
model_capability_settings: Record<string, Record<string, boolean>> | null
}>('/api/users/me/model-capabilities', data)
return response.data
},
@@ -500,7 +565,12 @@ export const meApi = {
return cachedRequest(
cacheKey,
async () => {
const response = await apiClient.get('/api/users/me/usage/interval-timeline', { params })
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