Commit Graph
5 Commits
Author SHA1 Message Date
Marc-André Bombeck ee2e9a3563 feat(sharing): add an error boundary to the public share route
A render failure on `/c/<token>` previously fell through to the full-app
error screen, which carries app chrome the public share view never wants.
Add a route-scoped, chrome-free error boundary that shows a localised,
stack-free message and a single retry. Add the matching copy across all
six locales.
2026-06-28 11:53:52 +02:00
Marc-André Bombeck be37a87fa7 fix(sharing): resolve gate strings on the server so the share view renders
The passphrase gate is a client component, but the page passed it a `t`
translator function. A function cannot cross the server→client boundary,
so a fresh (passphrase-protected) link threw a generic server-components
render error instead of painting the unlock form. Pre-resolve the gate
copy on the server into a plain, serializable `strings` object and pass
that instead. Locale resolution stays server-authoritative.

The page test mocked the gate away and only checked the token prop, so
the regression slipped through. Tighten it to assert the gate receives a
serializable contract: no prop value is a function, and the copy arrives
as a pre-resolved plain string object.
2026-06-28 11:53:46 +02:00
Marc-André Bombeck 1f4ba5939d style: apply prettier across the tree
One-shot prettier pass so the new format:check gate starts from a clean
baseline. Formatting only — no behaviour change. Generator-owned and
vendored artifacts stay untouched via .prettierignore.
2026-06-19 22:20:31 +02:00
Marc-André Bombeck 5783acacbb Gate the public share view behind the passphrase
A protected share link no longer renders the record until the passphrase
is verified. The public page checks the gate state first (without bumping
access counters), and for a link with a passphrase hash it serves a gate
island unless a valid, token-scoped unlock cookie is present. A legacy
link with a null passphrase hash renders exactly as before.

The unlock route verifies the submitted passphrase with a constant-time
compare against the stored hash, is rate-limited by share-unlock token
and IP before any compare, and on success sets a short-lived (30 min)
httpOnly, SameSite=Strict, Secure cookie scoped to that one token's view
path. Every failure class answers one blunt 401; no-store and the flat
404 for unknown links are preserved. The gate island auto-submits the
#k= fragment (scrubbing it from history first) or prompts for input.
2026-06-19 22:03:46 +02:00
Marc-André Bombeck 6aa3c75d27 feat(share-links): public clinician view at /c/[token]
A standalone read-only RSC that resolves the path token through the share-token
resolver and renders an owner-scoped clinical summary: provenance header,
clinical vitals and labs, medications with adherence, and a fenced muted card
for the descriptive wellness scores carrying the not-a-clinical-assessment,
not-a-diagnosis disclaimer. KVNR is default off. No app chrome, no coach, no AI,
no session, no markdown — every value renders as escaped text. force-dynamic and
noindex; the page returns a flat 404 for any null resolve.

The route joins the proxy public-path allowlist and the AuthShell standalone
list so it reaches the page bare without a sign-in redirect. New clinicianView
strings land in all six locales.
2026-06-04 01:21:42 +02:00