feat: persist failure journal and bounded retries (F11)
This commit is contained in:
parent
ec3e0c16e0
commit
871182d6d5
15 changed files with 2769 additions and 23 deletions
|
|
@ -195,6 +195,34 @@
|
|||
</table>
|
||||
</section>
|
||||
|
||||
<section class="ip__card">
|
||||
<header class="ip__head">
|
||||
<h2>Failed previews</h2>
|
||||
<v-button @click="loadFailures">Refresh</v-button>
|
||||
</header>
|
||||
<p class="ip__hint">
|
||||
Persisted failure journal. Retries run automatically inside the maintenance window with no
|
||||
active backfill: up to 3 attempts, then 5 and 15 minute backoff.
|
||||
</p>
|
||||
<table class="ip__table">
|
||||
<thead>
|
||||
<tr><th>file</th><th>preset</th><th>format</th><th>attempts</th><th>last error</th><th>next retry</th><th>status</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="failure in failures" :key="failure.file_id + failure.preset_key + (failure.format ?? '')">
|
||||
<td class="ip__mono">{{ failure.file?.filename_download ?? failure.file_id.slice(0, 8) }}</td>
|
||||
<td>{{ failure.preset_key }}</td>
|
||||
<td>{{ failure.format ?? 'original' }}</td>
|
||||
<td>{{ failure.attempts }}</td>
|
||||
<td class="ip__error">{{ failure.error ?? '' }}</td>
|
||||
<td>{{ failure.next_retry_at_human ?? '—' }}</td>
|
||||
<td>{{ failure.status }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p v-if="failures.length === 0" class="ip__hint">No failures recorded.</p>
|
||||
</section>
|
||||
|
||||
<section class="ip__card">
|
||||
<header class="ip__head"><h2>API reference</h2></header>
|
||||
<p class="ip__hint">
|
||||
|
|
@ -250,11 +278,23 @@ interface PresetInfo {
|
|||
enabled: boolean;
|
||||
}
|
||||
|
||||
interface FailureRecord {
|
||||
file_id: string;
|
||||
preset_key: string;
|
||||
format: string | null;
|
||||
attempts: number;
|
||||
status: string;
|
||||
error: string | null;
|
||||
next_retry_at_human: string | null;
|
||||
file: { filename_download: string | null; type: string | null; folder: string | null } | null;
|
||||
}
|
||||
|
||||
const base = '/image-previews';
|
||||
const status = ref<Status | null>(null);
|
||||
const config = ref<Record<string, any> | null>(null);
|
||||
const disk = ref<{ previews: Bucket; originals: Bucket; total: Bucket } | null>(null);
|
||||
const ops = ref<any[]>([]);
|
||||
const failures = ref<FailureRecord[]>([]);
|
||||
const presets = ref<PresetInfo[]>([]);
|
||||
const shadowedKeys = ref<string[]>([]);
|
||||
const fileId = ref('');
|
||||
|
|
@ -351,6 +391,13 @@ const endpoints = [
|
|||
description: 'In-memory ring buffer of the last 200 generation attempts with timing and error messages.',
|
||||
example: 'curl -H "Authorization: Bearer $TOKEN" "$BASE/image-previews/ops?limit=50"',
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/failures',
|
||||
summary: 'persisted failures',
|
||||
description: 'Failure journal records with file metadata, attempts, timestamps, next retry and status. Filter with status=failed|recovered|retry, limit (max 200) and offset.',
|
||||
example: 'curl -H "Authorization: Bearer $TOKEN" "$BASE/image-previews/failures?status=failed&limit=50&offset=0"',
|
||||
},
|
||||
];
|
||||
|
||||
async function api(path: string, init?: RequestInit) {
|
||||
|
|
@ -389,6 +436,15 @@ async function loadPresets() {
|
|||
shadowedKeys.value = result.shadowedKeys ?? [];
|
||||
}
|
||||
|
||||
async function loadFailures() {
|
||||
try {
|
||||
const result = await api('/failures?limit=100');
|
||||
failures.value = result.records ?? [];
|
||||
} catch {
|
||||
/* ignore polling errors */
|
||||
}
|
||||
}
|
||||
|
||||
async function saveConfig() {
|
||||
if (!config.value) return;
|
||||
|
||||
|
|
@ -456,7 +512,7 @@ async function generate(dryRun: boolean) {
|
|||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await Promise.all([ loadStatus(), loadConfig(), loadDisk(), loadPresets() ]);
|
||||
await Promise.all([ loadStatus(), loadConfig(), loadDisk(), loadPresets(), loadFailures() ]);
|
||||
timer = setInterval(loadStatus, 3000);
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue