Skip to content

feat(error-reporting): CrashReport panel + Sentry-compatible client#247

Merged
rubenhensen merged 2 commits into
mainfrom
feat/error-reporting
Jun 4, 2026
Merged

feat(error-reporting): CrashReport panel + Sentry-compatible client#247
rubenhensen merged 2 commits into
mainfrom
feat/error-reporting

Conversation

@rubenhensen

Copy link
Copy Markdown
Contributor

Summary

Wraps the fileshare encrypt flow in a <svelte:boundary>. On any uncaught render-time throw, a "Tell PostGuard" panel appears with two buttons — primary captures the exception (with rich client context: URL, viewport, user agent, locale, app version) and POSTs it to GlitchTip via @sentry/browser; secondary resets the state and re-renders the form.

  • New: src/lib/errorReporting.ts (lazy SDK init, manual-capture only — no global handlers, no breadcrumbs, 3 s flush budget).
  • New: src/lib/components/filesharing/CrashReport.svelte (two-button panel; primary label flips to "Sending…" → "Thank you!" in place with a pinned width so the row doesn't reflow).
  • src/lib/env.ts: GLITCHTIP_DSN runtime flag — empty value disables reporting at module load.
  • src/hooks.client.ts: init on app boot.
  • src/routes/(app)/fileshare/+page.svelte: boundary + CrashReport as the failed snippet.
  • Locale strings: crash section in en + nl.
  • static/config.js: local dev DSN matching the docker-compose stack. Deployed environments override via the Terraform ConfigMap (see the matching postguard-ops PR).
  • docker-compose.yml: local GlitchTip stack — postgres + redis + web + worker + idempotent init Job (seeds admin user, PostGuard org, postguard-website project, pinned ProjectKey UUID so the DSN survives docker compose down -v).
  • package.json + lockfile: adds @sentry/browser@^10, bumps svelte-preprocess to ^6.0.5 (older ^5.0.0-only TS peer dep was rejecting the wider Sentry-pulled range).

Test plan

  • npx svelte-check clean.
  • Local: docker compose up, init Job seeds GlitchTip with the dev admin + DSN; the website's CrashReport button delivers events to http://localhost:8001 (the dev DSN is wired in static/config.js).
  • Adblocker check: blockers default-block */api/*/envelope/; works in a private window.
  • Deployed: requires the postguard-ops GlitchTip stack and a same-origin reverse-proxy path before the DSN gets injected into the production ConfigMap.

Pairs with privacybydesign/postguard-ops#54 (GlitchTip K8s stack).

Drops into the fileshare error boundary: on any uncaught render-time
throw inside the encrypt flow, render a "Tell PostGuard" panel that
captures the exception (with rich client context: URL, viewport, UA,
locale, app version) and POSTs it to the configured GlitchTip / Sentry
endpoint via @sentry/browser. Reset path wipes encryptState and resets
the boundary so the user can keep going.

- `src/lib/errorReporting.ts` — lazy SDK init, manual-capture only
  (no global error / unhandledrejection hooks, no breadcrumbs), 3 s
  flush budget.
- `src/lib/components/filesharing/CrashReport.svelte` — two-button
  panel: primary "Tell PostGuard" (flips to "Sending…" → "Thank you!"
  in-place, width pinned), secondary "Try again".
- `src/lib/env.ts` — `GLITCHTIP_DSN` runtime config flag; empty value
  disables reporting at module load.
- `src/hooks.client.ts` — initialises the SDK on app boot.
- `src/routes/(app)/fileshare/+page.svelte` — wraps the encrypt flow
  in `<svelte:boundary>` with CrashReport as the `failed` snippet.
- Locale strings (`crash` section, en + nl).
- `static/config.js` — local dev DSN matching the GlitchTip stack in
  docker-compose. Deployed environments override via the Terraform
  ConfigMap.
- `docker-compose.yml` — local GlitchTip stack (postgres + redis +
  web + worker + idempotent init Job that seeds the admin user, the
  PostGuard org, the postguard-website project, and a pinned
  ProjectKey UUID so the DSN survives `docker compose down -v`).
- `package.json` / `package-lock.json` — adds `@sentry/browser ^10`
  and bumps `svelte-preprocess` to `^6.0.5` (the older 6.0.3 declared
  `typescript@^5.0.0` only, which broke `npm ci` after the Sentry
  install pulled in a wider tsc range).

Pairs with the postguard-ops GlitchTip deployment.
@rubenhensen

Copy link
Copy Markdown
Contributor Author

/dobby review

@dobby-coder

dobby-coder Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Dobby has received the request! Routing to the right specialist now...

@dobby-coder dobby-coder Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review

Adds GlitchTip-backed crash reporting behind a Svelte boundary on the fileshare page. Implementation is clean and svelte-check is green. Main concern: the GlitchTip init script's DSN-pinning branch is unreachable, so on a fresh volume the hardcoded DSN in static/config.js silently goes stale — the exact case the comment claims to prevent.

Rule compliance

WCAG 2.2 AA: the new CrashReport panel is missing a :focus / :focus-visible style (SC 2.4.7 Focus Visible) and the failed-status <p> lacks role="status" / aria-live (SC 4.1.3 Status Messages). Also flagging that the new GLITCHTIP_* env vars and dev-stack steps aren't documented in the README — informational, not a blocker.

Comment thread docker-compose.yml Outdated
project=project,
defaults={'public_key': DEV_KEY},
)
if key_created and key.public_key != DEV_KEY:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Code review] if key_created and key.public_key != DEV_KEY is unreachable: when key_created=True, get_or_create already populated public_key from defaults to DEV_KEY, so the inequality is false; when key_created=False, the first clause is false. GlitchTip auto-creates a default ProjectKey when a Project is created, so on a fresh volume get_or_create returns the auto-created key (key_created=False) and DEV_KEY is never applied — the hardcoded DSN in static/config.js silently goes stale. Replace with update_or_create, or invert to if not key_created and key.public_key != DEV_KEY.

Comment thread src/lib/errorReporting.ts Outdated
}
if (typeof navigator !== 'undefined') {
ctx.userAgent = navigator.userAgent
ctx.platform = navigator.platform

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Code review] navigator.platform is deprecated and returns an empty string in current Firefox; drop it or switch to navigator.userAgentData?.platform with a UA fallback.

appVersion: APP_VERSION,
uiLocale: $locale,
})
sendState = ok ? 'sent' : 'failed'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Code review] If reportError throws synchronously (Sentry SDK internal error, malformed DSN), the await rejects and sendState is stuck at 'sending' forever — the primary button stays disabled with no failed-state UI. Wrap in try/catch and set sendState = 'failed' in the catch.

attributes={ATTRIBUTES}
readonly={encryptState.encryptionState ===
EncryptionState.Encrypting}
<svelte:boundary>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Code review] <FileInput> sits outside the boundary, so a render-time error in FileInput escapes the boundary and crashes the page instead of showing the CrashReport panel. Move the <svelte:boundary> opening above the FileInput so the safety net covers the whole flow.

transform: none;
}

.crash-btn:not(:disabled):hover {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Rule: wcag-aa-compliance] No :focus / :focus-visible rule on .crash-btn — keyboard users get no visible focus indicator (WCAG 2.4.7 Focus Visible, Level AA). Add a :focus-visible style with a contrasting outline or border.

<p class="crash-message">{$_('filesharing.crash.message')}</p>

{#if sendState === 'failed'}
<p class="crash-status error">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Rule: wcag-aa-compliance] The failed-state <p> appears dynamically but has no role="status" / aria-live="polite", so screen readers won't announce the failure (WCAG 4.1.3 Status Messages, Level AA). Add role="status" to the <p> (or move the live region to a stable wrapper element so it's announced when the text changes).

- docker-compose.yml glitchtip-init: invert the DEV_KEY rewrite
  condition. GlitchTip auto-creates a default ProjectKey on Project
  save, so get_or_create returns that auto-created key
  (key_created=False) and the `defaults={'public_key': DEV_KEY}`
  clause is ignored. The previous `if key_created and …` branch was
  unreachable, leaving the DSN pinned UUID never applied on fresh
  volumes.
- errorReporting.ts: drop `navigator.platform` (deprecated; Firefox
  returns "" already).
- CrashReport.svelte:
  - Wrap reportError() in try/catch so a synchronous SDK throw can't
    leave the button stuck on "Sending…".
  - Add `:focus-visible` outline on .crash-btn (WCAG 2.4.7).
  - Move the failed-state <p> into a stable role=status / aria-live
    live region so screen readers announce it (WCAG 4.1.3).
- fileshare/+page.svelte: move <svelte:boundary> above <FileInput>
  so a render-time error in FileInput is caught by CrashReport
  instead of crashing the whole page.
@rubenhensen

Copy link
Copy Markdown
Contributor Author

/dobby please re-review — pushed 4d52834 addressing all six prior comments.

@dobby-coder

dobby-coder Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Dobby has received the request! Routing to the right specialist now...

@dobby-coder dobby-coder Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review

All six prior findings (DEV_KEY rewrite branch, try/catch around reportError, :focus-visible, live region, boundary above FileInput, deprecated navigator.platform) verified resolved in 4d52834. svelte-check clean, no new issues introduced.

Rule compliance

No violations. WCAG 2.2 AA (focus indicator, live region, tap target, contrast via tokens) ✓; conventional-commit title ✓; no design-slop tells (uses repo tokens); svelte-preprocess bump 6.0.3 → 6.0.5 matches the dep-bump-gotchas note's "unpin once 6.0.5 ships" guidance; uses defined --pg-font-weight-{bold,medium} (not the undefined -semibold); feat PR so the tests-required-on-fixes rule doesn't apply.

@rubenhensen rubenhensen merged commit a23d1cb into main Jun 4, 2026
12 of 13 checks passed
@rubenhensen rubenhensen deleted the feat/error-reporting branch June 4, 2026 08:48
rubenhensen pushed a commit to encryption4all/postguard-docs that referenced this pull request Jun 15, 2026
* docs: document pg-js 2.0 DecryptFileResult + onDownloadProgress + cause-preserving IdentityMismatchError

Sources:
- encryption4all/postguard-js#86 (DecryptFileResult shape change, onDownloadProgress)
- encryption4all/postguard-js#84 (IdentityMismatchError cause preservation)

* docs: clarify createEnvelope tier 2 vs tier 3 upload-failure semantics

Source: encryption4all/postguard-js#82 (re-throw on tier 3, console.warn on tier 2)

* docs: add Runtime config section for postguard-website APP_CONFIG keys

Sources:
- encryption4all/postguard-website#244 (STAGING)
- encryption4all/postguard-website#247 (GLITCHTIP_DSN)
- encryption4all/postguard-website#255 (SITE_URL)

* docs: surface private signing attributes on FriendlySender (from encryption4all/postguard-js#89)

* docs: document /download trust-confirmation gate (from encryption4all/postguard-website#258)

---------

Co-authored-by: dobby-yivi-agent[bot] <275734547+dobby-yivi-agent[bot]@users.noreply.github.com>
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.

1 participant