feat(sync): full code sync from release

This commit is contained in:
yangjianbo
2026-02-28 15:01:20 +08:00
parent bfc7b339f7
commit bb664d9bbf
338 changed files with 54513 additions and 2011 deletions

View File

@@ -369,6 +369,22 @@ export async function getTodayStats(id: number): Promise<WindowStats> {
return data
}
export interface BatchTodayStatsResponse {
stats: Record<string, WindowStats>
}
/**
* 批量获取多个账号的今日统计
* @param accountIds - 账号 ID 列表
* @returns 以账号 ID字符串为键的统计映射
*/
export async function getBatchTodayStats(accountIds: number[]): Promise<BatchTodayStatsResponse> {
const { data } = await apiClient.post<BatchTodayStatsResponse>('/admin/accounts/today-stats/batch', {
account_ids: accountIds
})
return data
}
/**
* Set account schedulable status
* @param id - Account ID
@@ -556,6 +572,7 @@ export const accountsAPI = {
clearError,
getUsage,
getTodayStats,
getBatchTodayStats,
clearRateLimit,
getTempUnschedulableStatus,
resetTempUnschedulable,

View File

@@ -9,7 +9,8 @@ import type {
TrendDataPoint,
ModelStat,
ApiKeyUsageTrendPoint,
UserUsageTrendPoint
UserUsageTrendPoint,
UsageRequestType
} from '@/types'
/**
@@ -49,6 +50,7 @@ export interface TrendParams {
model?: string
account_id?: number
group_id?: number
request_type?: UsageRequestType
stream?: boolean
billing_type?: number | null
}
@@ -78,6 +80,7 @@ export interface ModelStatsParams {
model?: string
account_id?: number
group_id?: number
request_type?: UsageRequestType
stream?: boolean
billing_type?: number | null
}

View File

@@ -0,0 +1,332 @@
import { apiClient } from '../client'
export type BackupType = 'postgres' | 'redis' | 'full'
export type BackupJobStatus = 'queued' | 'running' | 'succeeded' | 'failed' | 'partial_succeeded'
export interface BackupAgentInfo {
status: string
version: string
uptime_seconds: number
}
export interface BackupAgentHealth {
enabled: boolean
reason: string
socket_path: string
agent?: BackupAgentInfo
}
export interface DataManagementPostgresConfig {
host: string
port: number
user: string
password?: string
password_configured?: boolean
database: string
ssl_mode: string
container_name: string
}
export interface DataManagementRedisConfig {
addr: string
username: string
password?: string
password_configured?: boolean
db: number
container_name: string
}
export interface DataManagementS3Config {
enabled: boolean
endpoint: string
region: string
bucket: string
access_key_id: string
secret_access_key?: string
secret_access_key_configured?: boolean
prefix: string
force_path_style: boolean
use_ssl: boolean
}
export interface DataManagementConfig {
source_mode: 'direct' | 'docker_exec'
backup_root: string
sqlite_path?: string
retention_days: number
keep_last: number
active_postgres_profile_id?: string
active_redis_profile_id?: string
active_s3_profile_id?: string
postgres: DataManagementPostgresConfig
redis: DataManagementRedisConfig
s3: DataManagementS3Config
}
export type SourceType = 'postgres' | 'redis'
export interface DataManagementSourceConfig {
host: string
port: number
user: string
password?: string
database: string
ssl_mode: string
addr: string
username: string
db: number
container_name: string
}
export interface DataManagementSourceProfile {
source_type: SourceType
profile_id: string
name: string
is_active: boolean
password_configured?: boolean
config: DataManagementSourceConfig
created_at?: string
updated_at?: string
}
export interface TestS3Request {
endpoint: string
region: string
bucket: string
access_key_id: string
secret_access_key: string
prefix?: string
force_path_style?: boolean
use_ssl?: boolean
}
export interface TestS3Response {
ok: boolean
message: string
}
export interface CreateBackupJobRequest {
backup_type: BackupType
upload_to_s3?: boolean
s3_profile_id?: string
postgres_profile_id?: string
redis_profile_id?: string
idempotency_key?: string
}
export interface CreateBackupJobResponse {
job_id: string
status: BackupJobStatus
}
export interface BackupArtifactInfo {
local_path: string
size_bytes: number
sha256: string
}
export interface BackupS3Info {
bucket: string
key: string
etag: string
}
export interface BackupJob {
job_id: string
backup_type: BackupType
status: BackupJobStatus
triggered_by: string
s3_profile_id?: string
postgres_profile_id?: string
redis_profile_id?: string
started_at?: string
finished_at?: string
error_message?: string
artifact?: BackupArtifactInfo
s3?: BackupS3Info
}
export interface ListSourceProfilesResponse {
items: DataManagementSourceProfile[]
}
export interface CreateSourceProfileRequest {
profile_id: string
name: string
config: DataManagementSourceConfig
set_active?: boolean
}
export interface UpdateSourceProfileRequest {
name: string
config: DataManagementSourceConfig
}
export interface DataManagementS3Profile {
profile_id: string
name: string
is_active: boolean
s3: DataManagementS3Config
secret_access_key_configured?: boolean
created_at?: string
updated_at?: string
}
export interface ListS3ProfilesResponse {
items: DataManagementS3Profile[]
}
export interface CreateS3ProfileRequest {
profile_id: string
name: string
enabled: boolean
endpoint: string
region: string
bucket: string
access_key_id: string
secret_access_key?: string
prefix?: string
force_path_style?: boolean
use_ssl?: boolean
set_active?: boolean
}
export interface UpdateS3ProfileRequest {
name: string
enabled: boolean
endpoint: string
region: string
bucket: string
access_key_id: string
secret_access_key?: string
prefix?: string
force_path_style?: boolean
use_ssl?: boolean
}
export interface ListBackupJobsRequest {
page_size?: number
page_token?: string
status?: BackupJobStatus
backup_type?: BackupType
}
export interface ListBackupJobsResponse {
items: BackupJob[]
next_page_token?: string
}
export async function getAgentHealth(): Promise<BackupAgentHealth> {
const { data } = await apiClient.get<BackupAgentHealth>('/admin/data-management/agent/health')
return data
}
export async function getConfig(): Promise<DataManagementConfig> {
const { data } = await apiClient.get<DataManagementConfig>('/admin/data-management/config')
return data
}
export async function updateConfig(request: DataManagementConfig): Promise<DataManagementConfig> {
const { data } = await apiClient.put<DataManagementConfig>('/admin/data-management/config', request)
return data
}
export async function testS3(request: TestS3Request): Promise<TestS3Response> {
const { data } = await apiClient.post<TestS3Response>('/admin/data-management/s3/test', request)
return data
}
export async function listSourceProfiles(sourceType: SourceType): Promise<ListSourceProfilesResponse> {
const { data } = await apiClient.get<ListSourceProfilesResponse>(`/admin/data-management/sources/${sourceType}/profiles`)
return data
}
export async function createSourceProfile(sourceType: SourceType, request: CreateSourceProfileRequest): Promise<DataManagementSourceProfile> {
const { data } = await apiClient.post<DataManagementSourceProfile>(`/admin/data-management/sources/${sourceType}/profiles`, request)
return data
}
export async function updateSourceProfile(sourceType: SourceType, profileID: string, request: UpdateSourceProfileRequest): Promise<DataManagementSourceProfile> {
const { data } = await apiClient.put<DataManagementSourceProfile>(`/admin/data-management/sources/${sourceType}/profiles/${profileID}`, request)
return data
}
export async function deleteSourceProfile(sourceType: SourceType, profileID: string): Promise<void> {
await apiClient.delete(`/admin/data-management/sources/${sourceType}/profiles/${profileID}`)
}
export async function setActiveSourceProfile(sourceType: SourceType, profileID: string): Promise<DataManagementSourceProfile> {
const { data } = await apiClient.post<DataManagementSourceProfile>(`/admin/data-management/sources/${sourceType}/profiles/${profileID}/activate`)
return data
}
export async function listS3Profiles(): Promise<ListS3ProfilesResponse> {
const { data } = await apiClient.get<ListS3ProfilesResponse>('/admin/data-management/s3/profiles')
return data
}
export async function createS3Profile(request: CreateS3ProfileRequest): Promise<DataManagementS3Profile> {
const { data } = await apiClient.post<DataManagementS3Profile>('/admin/data-management/s3/profiles', request)
return data
}
export async function updateS3Profile(profileID: string, request: UpdateS3ProfileRequest): Promise<DataManagementS3Profile> {
const { data } = await apiClient.put<DataManagementS3Profile>(`/admin/data-management/s3/profiles/${profileID}`, request)
return data
}
export async function deleteS3Profile(profileID: string): Promise<void> {
await apiClient.delete(`/admin/data-management/s3/profiles/${profileID}`)
}
export async function setActiveS3Profile(profileID: string): Promise<DataManagementS3Profile> {
const { data } = await apiClient.post<DataManagementS3Profile>(`/admin/data-management/s3/profiles/${profileID}/activate`)
return data
}
export async function createBackupJob(request: CreateBackupJobRequest): Promise<CreateBackupJobResponse> {
const headers = request.idempotency_key
? { 'X-Idempotency-Key': request.idempotency_key }
: undefined
const { data } = await apiClient.post<CreateBackupJobResponse>(
'/admin/data-management/backups',
request,
{ headers }
)
return data
}
export async function listBackupJobs(request?: ListBackupJobsRequest): Promise<ListBackupJobsResponse> {
const { data } = await apiClient.get<ListBackupJobsResponse>('/admin/data-management/backups', {
params: request
})
return data
}
export async function getBackupJob(jobID: string): Promise<BackupJob> {
const { data } = await apiClient.get<BackupJob>(`/admin/data-management/backups/${jobID}`)
return data
}
export const dataManagementAPI = {
getAgentHealth,
getConfig,
updateConfig,
listSourceProfiles,
createSourceProfile,
updateSourceProfile,
deleteSourceProfile,
setActiveSourceProfile,
testS3,
listS3Profiles,
createS3Profile,
updateS3Profile,
deleteS3Profile,
setActiveS3Profile,
createBackupJob,
listBackupJobs,
getBackupJob
}
export default dataManagementAPI

View File

@@ -20,6 +20,7 @@ import antigravityAPI from './antigravity'
import userAttributesAPI from './userAttributes'
import opsAPI from './ops'
import errorPassthroughAPI from './errorPassthrough'
import dataManagementAPI from './dataManagement'
/**
* Unified admin API object for convenient access
@@ -41,7 +42,8 @@ export const adminAPI = {
antigravity: antigravityAPI,
userAttributes: userAttributesAPI,
ops: opsAPI,
errorPassthrough: errorPassthroughAPI
errorPassthrough: errorPassthroughAPI,
dataManagement: dataManagementAPI
}
export {
@@ -61,7 +63,8 @@ export {
antigravityAPI,
userAttributesAPI,
opsAPI,
errorPassthroughAPI
errorPassthroughAPI,
dataManagementAPI
}
export default adminAPI
@@ -69,3 +72,4 @@ export default adminAPI
// Re-export types used by components
export type { BalanceHistoryItem } from './users'
export type { ErrorPassthroughRule, CreateRuleRequest, UpdateRuleRequest } from './errorPassthrough'
export type { BackupAgentHealth, DataManagementConfig } from './dataManagement'

View File

@@ -31,6 +31,7 @@ export interface SystemSettings {
hide_ccs_import_button: boolean
purchase_subscription_enabled: boolean
purchase_subscription_url: string
sora_client_enabled: boolean
// SMTP settings
smtp_host: string
smtp_port: number
@@ -87,6 +88,7 @@ export interface UpdateSettingsRequest {
hide_ccs_import_button?: boolean
purchase_subscription_enabled?: boolean
purchase_subscription_url?: string
sora_client_enabled?: boolean
smtp_host?: string
smtp_port?: number
smtp_username?: string
@@ -251,6 +253,142 @@ export async function updateStreamTimeoutSettings(
return data
}
// ==================== Sora S3 Settings ====================
export interface SoraS3Settings {
enabled: boolean
endpoint: string
region: string
bucket: string
access_key_id: string
secret_access_key_configured: boolean
prefix: string
force_path_style: boolean
cdn_url: string
default_storage_quota_bytes: number
}
export interface SoraS3Profile {
profile_id: string
name: string
is_active: boolean
enabled: boolean
endpoint: string
region: string
bucket: string
access_key_id: string
secret_access_key_configured: boolean
prefix: string
force_path_style: boolean
cdn_url: string
default_storage_quota_bytes: number
updated_at: string
}
export interface ListSoraS3ProfilesResponse {
active_profile_id: string
items: SoraS3Profile[]
}
export interface UpdateSoraS3SettingsRequest {
profile_id?: string
enabled: boolean
endpoint: string
region: string
bucket: string
access_key_id: string
secret_access_key?: string
prefix: string
force_path_style: boolean
cdn_url: string
default_storage_quota_bytes: number
}
export interface CreateSoraS3ProfileRequest {
profile_id: string
name: string
set_active?: boolean
enabled: boolean
endpoint: string
region: string
bucket: string
access_key_id: string
secret_access_key?: string
prefix: string
force_path_style: boolean
cdn_url: string
default_storage_quota_bytes: number
}
export interface UpdateSoraS3ProfileRequest {
name: string
enabled: boolean
endpoint: string
region: string
bucket: string
access_key_id: string
secret_access_key?: string
prefix: string
force_path_style: boolean
cdn_url: string
default_storage_quota_bytes: number
}
export interface TestSoraS3ConnectionRequest {
profile_id?: string
enabled: boolean
endpoint: string
region: string
bucket: string
access_key_id: string
secret_access_key?: string
prefix: string
force_path_style: boolean
cdn_url: string
default_storage_quota_bytes?: number
}
export async function getSoraS3Settings(): Promise<SoraS3Settings> {
const { data } = await apiClient.get<SoraS3Settings>('/admin/settings/sora-s3')
return data
}
export async function updateSoraS3Settings(settings: UpdateSoraS3SettingsRequest): Promise<SoraS3Settings> {
const { data } = await apiClient.put<SoraS3Settings>('/admin/settings/sora-s3', settings)
return data
}
export async function testSoraS3Connection(
settings: TestSoraS3ConnectionRequest
): Promise<{ message: string }> {
const { data } = await apiClient.post<{ message: string }>('/admin/settings/sora-s3/test', settings)
return data
}
export async function listSoraS3Profiles(): Promise<ListSoraS3ProfilesResponse> {
const { data } = await apiClient.get<ListSoraS3ProfilesResponse>('/admin/settings/sora-s3/profiles')
return data
}
export async function createSoraS3Profile(request: CreateSoraS3ProfileRequest): Promise<SoraS3Profile> {
const { data } = await apiClient.post<SoraS3Profile>('/admin/settings/sora-s3/profiles', request)
return data
}
export async function updateSoraS3Profile(profileID: string, request: UpdateSoraS3ProfileRequest): Promise<SoraS3Profile> {
const { data } = await apiClient.put<SoraS3Profile>(`/admin/settings/sora-s3/profiles/${profileID}`, request)
return data
}
export async function deleteSoraS3Profile(profileID: string): Promise<void> {
await apiClient.delete(`/admin/settings/sora-s3/profiles/${profileID}`)
}
export async function setActiveSoraS3Profile(profileID: string): Promise<SoraS3Profile> {
const { data } = await apiClient.post<SoraS3Profile>(`/admin/settings/sora-s3/profiles/${profileID}/activate`)
return data
}
export const settingsAPI = {
getSettings,
updateSettings,
@@ -260,7 +398,15 @@ export const settingsAPI = {
regenerateAdminApiKey,
deleteAdminApiKey,
getStreamTimeoutSettings,
updateStreamTimeoutSettings
updateStreamTimeoutSettings,
getSoraS3Settings,
updateSoraS3Settings,
testSoraS3Connection,
listSoraS3Profiles,
createSoraS3Profile,
updateSoraS3Profile,
deleteSoraS3Profile,
setActiveSoraS3Profile
}
export default settingsAPI

View File

@@ -4,7 +4,7 @@
*/
import { apiClient } from '../client'
import type { AdminUsageLog, UsageQueryParams, PaginatedResponse } from '@/types'
import type { AdminUsageLog, UsageQueryParams, PaginatedResponse, UsageRequestType } from '@/types'
// ==================== Types ====================
@@ -39,6 +39,7 @@ export interface UsageCleanupFilters {
account_id?: number
group_id?: number
model?: string | null
request_type?: UsageRequestType | null
stream?: boolean | null
billing_type?: number | null
}
@@ -66,6 +67,7 @@ export interface CreateUsageCleanupTaskRequest {
account_id?: number
group_id?: number
model?: string | null
request_type?: UsageRequestType | null
stream?: boolean | null
billing_type?: number | null
timezone?: string
@@ -104,6 +106,7 @@ export async function getStats(params: {
account_id?: number
group_id?: number
model?: string
request_type?: UsageRequestType
stream?: boolean
period?: string
start_date?: string