feat: add storage cleanup mechanism with soft delete and auto scheduler

- Add soft delete (deleted_at column) to preserve task records for statistics
- Implement cleanup service to delete old files while keeping DB records
- Add automatic cleanup scheduler (configurable interval, default 24h)
- Add admin endpoints: storage stats, cleanup trigger, scheduler status
- Update task service with admin views (include deleted/files_deleted)
- Add frontend storage management UI in admin dashboard
- Add i18n translations for storage management

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
egg
2025-12-14 12:41:01 +08:00
parent 81a0a3ab0f
commit 73112db055
23 changed files with 1359 additions and 634 deletions

View File

@@ -39,6 +39,9 @@ import type {
TranslationListResponse,
TranslationResult,
ExportRule,
StorageStats,
CleanupResult,
AdminTaskListResponse,
} from '@/types/apiV2'
/**
@@ -771,6 +774,48 @@ class ApiClientV2 {
async deleteExportRule(ruleId: number): Promise<void> {
await this.client.delete(`/export/rules/${ruleId}`)
}
// ==================== Admin Storage Management ====================
/**
* Get storage statistics (admin only)
*/
async getStorageStats(): Promise<StorageStats> {
const response = await this.client.get<StorageStats>('/admin/storage/stats')
return response.data
}
/**
* Trigger file cleanup (admin only)
*/
async triggerCleanup(maxFilesPerUser?: number): Promise<CleanupResult> {
const params = maxFilesPerUser ? { max_files_per_user: maxFilesPerUser } : {}
const response = await this.client.post<CleanupResult>('/admin/cleanup/trigger', null, { params })
return response.data
}
/**
* List all tasks (admin only)
*/
async listAllTasksAdmin(params: {
user_id?: number
status_filter?: string
include_deleted?: boolean
include_files_deleted?: boolean
page?: number
page_size?: number
}): Promise<AdminTaskListResponse> {
const response = await this.client.get<AdminTaskListResponse>('/admin/tasks', { params })
return response.data
}
/**
* Get task details (admin only, can view any task including deleted)
*/
async getTaskAdmin(taskId: string): Promise<Task> {
const response = await this.client.get<Task>(`/admin/tasks/${taskId}`)
return response.data
}
}
// Export singleton instance