Skip to content

Signed-URL-only refresh for expired PDFs (shared fetch, abortable)#643

Merged
BigSimmo merged 1 commit into
mainfrom
claude/pdf-signed-url-refresh
Jul 14, 2026
Merged

Signed-URL-only refresh for expired PDFs (shared fetch, abortable)#643
BigSimmo merged 1 commit into
mainfrom
claude/pdf-signed-url-refresh

Conversation

@BigSimmo

@BigSimmo BigSimmo commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Summary

Follow-up to #619 (now merged), addressing the one CodeRabbit finding deliberately deferred there: when a PDF's signed URL expires mid-session, recovery reused setPreviewAttempt, which re-ran the whole detail-fetch effect — resetting loadingDocument/signedUrl, hiding the canvas behind the loading screen, and re-fetching document detail (pages/images/chunks/tableFacts) plus both signed URLs — all to re-mint one URL on an otherwise-unchanged document (jarring reload/flicker + wasted payload).

This does it properly (not the way CodeRabbit's reverted autofix did — that duplicated ~90 lines and left an un-aborted controller):

  • One shared fetch, used by both paths. Extracted requestSignedUrlPayload (a single signed-URL GET: JSON parse, 401 → markSessionExpired, throw on failure) and fetchSignedUrlPair (the preview + download pair, cache-first when allowed) at module scope, and applySignedUrlResults (applies the settled results to state — cache write, previewError, download fallback). The initial-load effect and the expiry refresh now take the exact same code path.
  • Targeted, in-place refresh. handleSignedUrlExpired calls refreshSignedUrls, which re-issues only the signed URLs — no detail refetch, no loadingDocument reset, no flicker; the fresh URL flows to PdfCanvasViewer and reloads the page.
  • Correct abort handling. refreshSignedUrls owns an AbortController that is aborted on the next refresh and on unmount, and is guarded by the auth epoch (isAuthEpochCurrent) so a stale response can't write state. The refresh-attempt cap (≤2) and reset-on-successful-load from Optimise and extend the document & image viewers #619 are retained.

Verification

  • Constituents of npm run verify:pr-local run individually: npm run lint (repo, --max-warnings 0) ✅, npm run typecheck (0 errors) ✅, npm run test (vitest: 2265 passed / 3 skipped) ✅, prettier --check ✅.
  • Chromium drive of the refactored initial load: document detail loads, the PDF canvas rasterises (signed URL fetched via fetchSignedUrlPair and applied via applySignedUrlResults), the Download link is present, and no preview error surfaces.
  • npm run verify:pr-local / npm run verify:ui as single commands — CI runs these. (The expiry-refresh path itself can't be forced in a quick drive — a 10-min TTL lapse — but it reuses the exact shared fetch/apply units verified above, with the same epoch/abort guards as the initial-load effect.)

Presentation/auth-plumbing only — no change to retrieval, ranking, ingestion, answer generation, or document-access policy.

Clinical Governance Preflight

Change touches source rendering only (how an already-authorized signed URL is re-issued and displayed); no ingestion, answer generation, ranking, document access, privacy, or clinical-output logic changed.

  • Source-backed claims still require linked source verification before clinical use — unchanged
  • No patient-identifiable document workflow was introduced or expanded — viewer/plumbing only
  • Supabase target remains Clinical KB Database (sjrfecxgysukkwxsowpy) — no Supabase changes
  • Service-role keys and private document access remain server-only — signed-URL API routes unchanged; the client still fetches through the existing endpoints with the same auth header + epoch guards
  • Demo/synthetic content remains clearly separated from real clinical sources — unchanged
  • Source metadata, review status, and outdated/unknown-source behavior remain conservative — unchanged
  • Deployment classification/TGA SaMD impact — no clinical decision-support behaviour changed

Notes

Depends on #619 (merged). Net diff is src/components/DocumentViewer.tsx only: the shared fetch/apply helpers, the refreshSignedUrls callback (+ unmount-abort effect), and rewiring handleSignedUrlExpired; the initial-load effect now consumes the same helpers so there is no duplicated signed-URL fetch logic.

🤖 Generated with Claude Code

https://claude.ai/code/session_01DqMTk5fDNgi5GwEK5peRmj


Generated by Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved document preview and download link handling.
    • Automatically refreshes expired links without reloading document details.
    • Provides more consistent error handling when secure links fail to load.
    • Prevents outdated link refreshes after authentication or session changes.
    • Uses cached links when available to improve document loading efficiency.

… abortable)

Follow-up to #619. An expired PDF signed URL previously recovered by bumping
previewAttempt, which re-ran the whole detail-fetch effect — resetting
loadingDocument/signedUrl, hiding the canvas behind the loading screen, and
re-fetching document detail (pages/images/chunks/tableFacts) plus both signed
URLs — just to re-mint one URL on an otherwise-unchanged document.

Extract the signed-URL fetch/apply into shared units used by both paths, so the
logic lives in exactly one place (not duplicated):
- requestSignedUrlPayload / fetchSignedUrlPair: one signed-URL GET and the
  preview+download pair (cache-first when allowed), at module scope.
- applySignedUrlResults: applies the settled results to state (cache write,
  previewError, download fallback) — used by the initial load and the refresh.

handleSignedUrlExpired now calls refreshSignedUrls, which re-issues only the
signed URLs in place (no detail refetch, no loadingDocument reset, no flicker),
guarded by the auth epoch and its own AbortController — aborted on the next
refresh and on unmount. The refresh-attempt cap and reset-on-successful-load are
retained.

Verified: lint, typecheck (0 errors), format, full vitest (2265 passing), and a
Chromium drive of the initial load (detail + PDF canvas + download link, no
preview error).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DqMTk5fDNgi5GwEK5peRmj
@supabase

supabase Bot commented Jul 14, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project sjrfecxgysukkwxsowpy because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 9d3df90f-5777-47b8-ac0c-349aa2cd3d4c

📥 Commits

Reviewing files that changed from the base of the PR and between 0696ded and 7f9062d.

📒 Files selected for processing (1)
  • src/components/DocumentViewer.tsx

📝 Walkthrough

Walkthrough

Changes

Signed URL lifecycle

Layer / File(s) Summary
Shared signed-URL fetching
src/components/DocumentViewer.tsx
Adds typed response parsing and paired preview/download signed-URL requests with cache support, authentication handling, and settled results.
Signed-URL state and refresh
src/components/DocumentViewer.tsx
Centralizes state and cache updates and refreshes expired URLs with cancellation and authentication-epoch checks.
Load and expiry integration
src/components/DocumentViewer.tsx
Uses the shared pipeline during initial loading and triggers signed-URL refresh when the PDF URL expires.

Estimated code review effort: 4 (Complex) | ~45 minutes

Suggested reviewers: claude, copilot

Sequence Diagram(s)

sequenceDiagram
  participant DocumentViewer
  participant fetchSignedUrlPair
  participant SignedUrlAPI
  participant URLCache

  DocumentViewer->>fetchSignedUrlPair: Request preview and download URLs
  fetchSignedUrlPair->>URLCache: Read cached URLs when enabled
  fetchSignedUrlPair->>SignedUrlAPI: Fetch missing signed URLs
  SignedUrlAPI-->>fetchSignedUrlPair: Return settled URL results
  fetchSignedUrlPair-->>DocumentViewer: Return preview and download results
  DocumentViewer->>URLCache: Cache fulfilled URLs
  DocumentViewer->>DocumentViewer: Apply URL state and errors
  DocumentViewer->>fetchSignedUrlPair: Refresh URLs after expiry
Loading
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/pdf-signed-url-refresh

Comment @coderabbitai help to get the list of available commands.

@BigSimmo
BigSimmo marked this pull request as ready for review July 14, 2026 07:46
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@BigSimmo
BigSimmo merged commit db9ec17 into main Jul 14, 2026
16 checks passed
@BigSimmo
BigSimmo deleted the claude/pdf-signed-url-refresh branch July 14, 2026 09:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants