Commit 0c29468f authored by kyx236's avatar kyx236
Browse files

feat(admin): 支持定时测试自动恢复并统一账号恢复入口

- 为定时测试计划增加 auto_recover 配置,补齐前后端类型、接口、仓储与数据库迁移
- 在定时测试成功后自动恢复账号 error、rate-limit 等可恢复运行时状态
- 新增 /admin/accounts/:id/recover-state 接口,合并原有重置状态与清限流操作
- 更新账号管理菜单与定时测试面板,补充自动恢复开关、说明提示和状态展示
- 补充账号恢复、限流清理与仓储同步相关测试
parent 03bf3485
......@@ -1487,6 +1487,7 @@ export interface ScheduledTestPlan {
cron_expression: string
enabled: boolean
max_results: number
auto_recover: boolean
last_run_at: string | null
next_run_at: string | null
created_at: string
......@@ -1511,6 +1512,7 @@ export interface CreateScheduledTestPlanRequest {
cron_expression: string
enabled?: boolean
max_results?: number
auto_recover?: boolean
}
export interface UpdateScheduledTestPlanRequest {
......@@ -1518,4 +1520,5 @@ export interface UpdateScheduledTestPlanRequest {
cron_expression?: string
enabled?: boolean
max_results?: number
auto_recover?: boolean
}
......@@ -261,7 +261,7 @@
<AccountTestModal :show="showTest" :account="testingAcc" @close="closeTestModal" />
<AccountStatsModal :show="showStats" :account="statsAcc" @close="closeStatsModal" />
<ScheduledTestsPanel :show="showSchedulePanel" :account-id="scheduleAcc?.id ?? null" :model-options="scheduleModelOptions" @close="closeSchedulePanel" />
<AccountActionMenu :show="menu.show" :account="menu.acc" :position="menu.pos" @close="menu.show = false" @test="handleTest" @stats="handleViewStats" @schedule="handleSchedule" @reauth="handleReAuth" @refresh-token="handleRefresh" @reset-status="handleResetStatus" @clear-rate-limit="handleClearRateLimit" @reset-quota="handleResetQuota" />
<AccountActionMenu :show="menu.show" :account="menu.acc" :position="menu.pos" @close="menu.show = false" @test="handleTest" @stats="handleViewStats" @schedule="handleSchedule" @reauth="handleReAuth" @refresh-token="handleRefresh" @recover-state="handleRecoverState" @reset-quota="handleResetQuota" />
<SyncFromCrsModal :show="showSync" @close="showSync = false" @synced="reload" />
<ImportDataModal :show="showImportData" @close="showImportData = false" @imported="handleDataImported" />
<BulkEditAccountModal :show="showBulkEdit" :account-ids="selIds" :selected-platforms="selPlatforms" :selected-types="selTypes" :proxies="proxies" :groups="groups" @close="showBulkEdit = false" @updated="handleBulkUpdated" />
......@@ -1105,24 +1105,15 @@ const handleRefresh = async (a: Account) => {
console.error('Failed to refresh credentials:', error)
}
}
const handleResetStatus = async (a: Account) => {
const handleRecoverState = async (a: Account) => {
try {
const updated = await adminAPI.accounts.clearError(a.id)
const updated = await adminAPI.accounts.recoverState(a.id)
patchAccountInList(updated)
enterAutoRefreshSilentWindow()
appStore.showSuccess(t('common.success'))
} catch (error) {
console.error('Failed to reset status:', error)
}
}
const handleClearRateLimit = async (a: Account) => {
try {
const updated = await adminAPI.accounts.clearRateLimit(a.id)
patchAccountInList(updated)
enterAutoRefreshSilentWindow()
appStore.showSuccess(t('common.success'))
} catch (error) {
console.error('Failed to clear rate limit:', error)
appStore.showSuccess(t('admin.accounts.recoverStateSuccess'))
} catch (error: any) {
console.error('Failed to recover account state:', error)
appStore.showError(error?.message || t('admin.accounts.recoverStateFailed'))
}
}
const handleResetQuota = async (a: Account) => {
......@@ -1152,17 +1143,11 @@ const handleToggleSchedulable = async (a: Account) => {
}
}
const handleShowTempUnsched = (a: Account) => { tempUnschedAcc.value = a; showTempUnsched.value = true }
const handleTempUnschedReset = async () => {
if(!tempUnschedAcc.value) return
try {
const updated = await adminAPI.accounts.clearError(tempUnschedAcc.value.id)
showTempUnsched.value = false
tempUnschedAcc.value = null
patchAccountInList(updated)
enterAutoRefreshSilentWindow()
} catch (error) {
console.error('Failed to reset temp unscheduled:', error)
}
const handleTempUnschedReset = async (updated: Account) => {
showTempUnsched.value = false
tempUnschedAcc.value = null
patchAccountInList(updated)
enterAutoRefreshSilentWindow()
}
const formatExpiresAt = (value: number | null) => {
if (!value) return '-'
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment