feat(sources): pre-flight credential verify on backfill/recovery arm — #703 step 2
CI / frontend-build (push) Successful in 22s
CI / backend-lint-and-test (push) Successful in 25s
CI / lint (push) Successful in 3s
CI / integration (push) Successful in 2m59s

Before arming a deep walk on a native-ingester platform (Patreon — where
verify is one cheap API page), POST /sources/{id}/backfill {start|recover}
runs the shared verify_source_credential first and REFUSES (409 + reason)
only on a definitive rejection (verify→False, e.g. expired cookies). It
proceeds on valid (True) or inconclusive (None — a network blip must not
block). Gated to native platforms: gallery-dl verify is a slow --simulate
subprocess, too heavy for an arm action. The credential read happens in a
session that's CLOSED before the verify network call (no held conn).

Frontend: onBackfill/onRecover now read e.body.detail (ApiError carries the
reason in .body, not .detail) so the rejection text surfaces in the toast.

Tests: arm blocked on rejection (409, source not armed), proceeds on
inconclusive, stop never pre-flights, gallery-dl platform skips pre-flight.
An autouse fixture stubs verify to 'valid' for the existing backfill
endpoint tests so they stay network-free.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-05 23:21:05 -04:00
parent d6c15f4ea0
commit 5b615b7ded
3 changed files with 145 additions and 2 deletions
@@ -550,7 +550,7 @@ async function onBackfill(source) {
await store.loadAll()
} catch (e) {
toast({
text: `Backfill ${running ? 'stop' : 'start'} failed: ${e?.detail || e?.message || e}`,
text: `Backfill ${running ? 'stop' : 'start'} failed: ${e?.body?.detail || e?.message || e}`,
type: 'error',
})
}
@@ -566,7 +566,7 @@ async function onRecover(source) {
await store.loadAll()
} catch (e) {
toast({
text: `Recovery start failed: ${e?.detail || e?.message || e}`,
text: `Recovery start failed: ${e?.body?.detail || e?.message || e}`,
type: 'error',
})
}