diff --git a/backend/app/api/cleanup.py b/backend/app/api/cleanup.py index 386c3e5..7149525 100644 --- a/backend/app/api/cleanup.py +++ b/backend/app/api/cleanup.py @@ -81,6 +81,13 @@ async def min_dim_preview(): s, min_width=min_w, min_height=min_h, ) ) + # Hand the canonical Tier-C delete token back with the preview so + # the frontend doesn't have to recompute SHA-256 client-side. + # window.crypto.subtle is Secure-Context-gated and undefined on + # plain-HTTP origins (homelab posture); without this the Delete + # button silently swallowed the TypeError and never opened the + # confirm modal. Operator-flagged 2026-05-27. + projection["confirm_token"] = _min_dim_token(min_w, min_h) return jsonify(projection) diff --git a/frontend/src/components/cleanup/MinDimensionCard.vue b/frontend/src/components/cleanup/MinDimensionCard.vue index a26ae2d..b1a1e9b 100644 --- a/frontend/src/components/cleanup/MinDimensionCard.vue +++ b/frontend/src/components/cleanup/MinDimensionCard.vue @@ -52,7 +52,7 @@ v-model="showModal" action="delete" kind="min-dim" - :run-id="tokenSha8" + :run-id="tokenSuffix" tier="C" :projected-counts="projectedCounts" :description="`Width < ${minW} OR height < ${minH}`" @@ -62,7 +62,7 @@ diff --git a/tests/test_api_cleanup.py b/tests/test_api_cleanup.py index ba9534f..35478a4 100644 --- a/tests/test_api_cleanup.py +++ b/tests/test_api_cleanup.py @@ -66,6 +66,10 @@ async def test_min_dimension_preview_returns_count(client, db, tmp_path): assert resp.status_code == 200 body = await resp.get_json() assert body["count"] == 1 + # Preview hands the canonical Tier-C confirm token back so the + # frontend doesn't have to recompute SHA-256 client-side + # (crypto.subtle is Secure-Context-gated; FC runs over plain HTTP). + assert body["confirm_token"] == _sha256_min_dim_token(200, 200) @pytest.mark.asyncio