Files
sub2api/frontend/src/api/admin/backup.ts
T
harukaandClaude Opus 4.8 0343dba916 feat(admin): 备份页新增异步生图对象存储配置卡片
对应后端 /admin/backups/image-storage。放在备份页而非系统设置页,是因为它与
数据库备份共用同一套 S3 客户端与凭证——用户的心智就是"在备份的 S3 上加个开关"。

- 默认勾选"复用备份 S3",此时隐藏端点/密钥字段,只留 bucket 与 prefix,
  bucket 留空则沿用备份桶。
- 保存走 useStepUp,与备份 S3 配置的保存路径一致。
- 补中英文案。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHreE5pzCkSYz7J45fmd2Y
2026-07-19 00:56:33 -07:00

169 lines
4.4 KiB
TypeScript

import { apiClient } from '../client'
export interface BackupS3Config {
endpoint: string
region: string
bucket: string
access_key_id: string
secret_access_key?: string
prefix: string
force_path_style: boolean
}
export interface BackupScheduleConfig {
enabled: boolean
cron_expr: string
retain_days: number
retain_count: number
}
export interface BackupRecord {
id: string
status: 'pending' | 'running' | 'completed' | 'failed'
backup_type: string
file_name: string
s3_key: string
size_bytes: number
triggered_by: string
error_message?: string
started_at: string
finished_at?: string
expires_at?: string
progress?: string
restore_status?: string
restore_error?: string
restored_at?: string
}
export interface CreateBackupRequest {
expire_days?: number
}
export interface TestS3Response {
ok: boolean
message: string
}
// S3 Config
export async function getS3Config(): Promise<BackupS3Config> {
const { data } = await apiClient.get<BackupS3Config>('/admin/backups/s3-config')
return data
}
export async function updateS3Config(config: BackupS3Config): Promise<BackupS3Config> {
const { data } = await apiClient.put<BackupS3Config>('/admin/backups/s3-config', config)
return data
}
export async function testS3Connection(config: BackupS3Config): Promise<TestS3Response> {
const { data } = await apiClient.post<TestS3Response>('/admin/backups/s3-config/test', config)
return data
}
// Async image object storage
//
// Shares the S3 client with backups, so `reuse_backup_s3` borrows the endpoint and
// credentials configured above and only keeps its own bucket/prefix.
export interface ImageStorageConfig {
enabled: boolean
reuse_backup_s3: boolean
bucket: string
prefix: string
public_base_url: string
presign_expiry_hours: number
max_download_bytes: number
endpoint: string
region: string
access_key_id: string
secret_access_key?: string
force_path_style: boolean
}
export interface ImageStorageConfigResponse {
config: ImageStorageConfig
secret_configured: boolean
}
export async function getImageStorageConfig(): Promise<ImageStorageConfigResponse> {
const { data } = await apiClient.get<ImageStorageConfigResponse>('/admin/backups/image-storage')
return data
}
export async function updateImageStorageConfig(
config: ImageStorageConfig,
): Promise<ImageStorageConfig> {
const { data } = await apiClient.put<ImageStorageConfig>('/admin/backups/image-storage', config)
return data
}
export async function testImageStorageConnection(
config: ImageStorageConfig,
): Promise<TestS3Response> {
const { data } = await apiClient.post<TestS3Response>(
'/admin/backups/image-storage/test',
config,
)
return data
}
// Schedule
export async function getSchedule(): Promise<BackupScheduleConfig> {
const { data } = await apiClient.get<BackupScheduleConfig>('/admin/backups/schedule')
return data
}
export async function updateSchedule(config: BackupScheduleConfig): Promise<BackupScheduleConfig> {
const { data } = await apiClient.put<BackupScheduleConfig>('/admin/backups/schedule', config)
return data
}
// Backup operations
export async function createBackup(req?: CreateBackupRequest): Promise<BackupRecord> {
const { data } = await apiClient.post<BackupRecord>('/admin/backups', req || {})
return data
}
export async function listBackups(): Promise<{ items: BackupRecord[] }> {
const { data } = await apiClient.get<{ items: BackupRecord[] }>('/admin/backups')
return data
}
export async function getBackup(id: string): Promise<BackupRecord> {
const { data } = await apiClient.get<BackupRecord>(`/admin/backups/${id}`)
return data
}
export async function deleteBackup(id: string): Promise<void> {
await apiClient.delete(`/admin/backups/${id}`)
}
export async function getDownloadURL(id: string): Promise<{ url: string }> {
const { data } = await apiClient.get<{ url: string }>(`/admin/backups/${id}/download-url`)
return data
}
// Restore
export async function restoreBackup(id: string, password: string): Promise<BackupRecord> {
const { data } = await apiClient.post<BackupRecord>(`/admin/backups/${id}/restore`, { password })
return data
}
export const backupAPI = {
getS3Config,
updateS3Config,
testS3Connection,
getImageStorageConfig,
updateImageStorageConfig,
testImageStorageConnection,
getSchedule,
updateSchedule,
createBackup,
listBackups,
getBackup,
deleteBackup,
getDownloadURL,
restoreBackup,
}
export default backupAPI