fix(audit-g4): status-enum miss batch
Five extension-miss findings from the 2026-06-02 audit, where a status
value was added on one side but a downstream consumer didn't pick it up.
- download_service._phase3_persist: explicit branches for
ImportResult.status in ('failed','refreshed'). For 'failed' (archive
probe crash from _import_archive), unlink the source file so the
filesystem scanner doesn't re-import and re-crash on the same
archive forever. 'refreshed' is currently unreachable from the
download path (no deep=True) but matches the importer's documented
contract; treat as 'attached'.
- gallery-dl backfill auto-complete now gates on dl_result.success +
no error_type, not just return_code==0 + files_downloaded==0.
VALIDATION_FAILED exits the subprocess with returncode=0 and
files_downloaded=0 when every file was quarantined, matching the
prior predicate exactly and zeroing the operator's armed backfill
budget on the FIRST quarantine run instead of decrementing.
- attach_in_place archive dispatch now threads artist + source_row
through _import_archive (and _import_media for archive members)
and _supersede. The path-walk fallback (_resolve_artist) is still
used by filesystem-import; the download path now binds
ImageProvenance to the explicit subscription Source instead of
rediscovering by (artist_id, platform).
- Three FE handlers now recognize status:'deferred' from
/api/sources/<id>/check: SubscriptionsTab.onCheck (was toasting
"event #undefined"), SubscriptionsTab.checkAll (was counting
deferred as queued), DownloadEventRow.onRetry (was saying
"re-queued" when nothing was). Pattern matches DownloadsTab.onRetryAll
which already had it.
- celery_signals._queue_for now maps backup/admin/library_audit
prefixes to 'maintenance' (matching task_routes). TaskRun.queue
was returning 'default' for those rows, so per-queue dashboard
filters and per-queue threshold overrides (added in G3) silently
missed them.
This commit is contained in:
@@ -412,6 +412,17 @@ function onArtistCreated(artist) {
|
||||
async function onCheck(source) {
|
||||
try {
|
||||
const body = await store.checkNow(source.id)
|
||||
// Audit 2026-06-02: /api/sources/<id>/check returns 202 with
|
||||
// `{status:'deferred', cooldown_until}` when the platform is in
|
||||
// cooldown — the previous handler treated this as success and
|
||||
// toasted "event #undefined", masking that nothing was enqueued.
|
||||
if (body?.status === 'deferred') {
|
||||
toast({
|
||||
text: 'Check deferred — platform in cooldown',
|
||||
type: 'info',
|
||||
})
|
||||
return
|
||||
}
|
||||
toast({
|
||||
text: `Check enqueued (event #${body.download_event_id})`,
|
||||
type: 'success',
|
||||
@@ -465,17 +476,23 @@ async function onBackfill(source) {
|
||||
async function checkAll(group) {
|
||||
let ok = 0
|
||||
let conflict = 0
|
||||
let deferred = 0
|
||||
for (const s of group.sources) {
|
||||
if (!s.enabled) continue
|
||||
try {
|
||||
await store.checkNow(s.id)
|
||||
ok += 1
|
||||
const body = await store.checkNow(s.id)
|
||||
// Audit 2026-06-02: deferred (202 + cooldown_until) used to be
|
||||
// counted as queued, inflating the success tally and hiding
|
||||
// that the cooldown actually held the work back.
|
||||
if (body?.status === 'deferred') deferred += 1
|
||||
else ok += 1
|
||||
} catch (e) {
|
||||
if (e?.body?.download_event_id) conflict += 1
|
||||
}
|
||||
}
|
||||
const parts = []
|
||||
if (ok) parts.push(`${ok} queued`)
|
||||
if (deferred) parts.push(`${deferred} deferred (cooldown)`)
|
||||
if (conflict) parts.push(`${conflict} already running`)
|
||||
toast({
|
||||
text: parts.join(', ') || 'Nothing to check (no enabled sources)',
|
||||
|
||||
Reference in New Issue
Block a user