Stop Sentry on logout; fix E2E + Dockerfile for merged consolidation

- sentry.client.ts: add stopSentryClient() that awaits Sentry.close() so
  the SDK tears down when a user logs out. Hub methods become no-ops
  until initSentryClient() is called again on next login.
- root.tsx: call stopSentryClient() from the user-effect when user === null.
- Add sentry-config to journal + planner Dockerfiles (was missing after
  #237 introduced the new workspace package, breaking the Dockerfile
  package check).
- Footer: replace inner <nav> with <div> — nested nav landmarks broke
  the journal "nav bar shows on all pages" e2e test (two navigation
  roles on the page).
- e2e auth: tick the required ToS checkbox in the shared registerUser
  helper so passkey registration tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ullrich Schäfer 2026-04-18 00:40:36 +02:00
parent adbf507101
commit 01f002cc46
No known key found for this signature in database
GPG key ID: A32FF691A0F752D9
6 changed files with 21 additions and 3 deletions

View file

@ -11,6 +11,7 @@ COPY packages/ui/package.json packages/ui/
COPY packages/map/package.json packages/map/
COPY packages/gpx/package.json packages/gpx/
COPY packages/i18n/package.json packages/i18n/
COPY packages/sentry-config/package.json packages/sentry-config/
COPY packages/api/package.json packages/api/
COPY packages/map-core/package.json packages/map-core/
COPY packages/db/package.json packages/db/

View file

@ -5,7 +5,7 @@ export function Footer() {
return (
<footer className="mt-16 border-t border-gray-200 bg-white">
<div className="mx-auto max-w-7xl px-4 py-6 text-sm text-gray-500">
<nav className="flex flex-wrap items-center gap-x-6 gap-y-2">
<div className="flex flex-wrap items-center gap-x-6 gap-y-2">
<a href="/legal/imprint" className="hover:text-gray-700">
{t("footer.imprint")}
</a>
@ -26,7 +26,7 @@ export function Footer() {
<span className="ml-auto text-xs text-gray-400">
{t("footer.alpha")}
</span>
</nav>
</div>
</div>
</footer>
);

View file

@ -23,3 +23,16 @@ export function initSentryClient() {
...browserSentryConfig("journal client", import.meta.env),
});
}
/**
* Tear down the Sentry client on logout. After this call, `Sentry.captureException`
* and other hub methods become no-ops until `initSentryClient` is called again.
*
* Fire-and-forget: the close flush happens async, but we don't want to block the
* logout UI on it.
*/
export function stopSentryClient() {
if (!initialized) return;
initialized = false;
void Sentry.close();
}

View file

@ -9,7 +9,7 @@ import { getSessionUser } from "~/lib/auth.server";
import { LocaleProvider } from "~/components/LocaleContext";
import { AlphaBanner } from "~/components/AlphaBanner";
import { Footer } from "~/components/Footer";
import { initSentryClient } from "~/lib/sentry.client";
import { initSentryClient, stopSentryClient } from "~/lib/sentry.client";
import stylesheet from "@trails-cool/ui/styles.css?url";
export const links: LinksFunction = () => [{ rel: "stylesheet", href: stylesheet }];
@ -123,6 +123,7 @@ export default function App({ loaderData }: Route.ComponentProps) {
Sentry.setUser({ id: user.id });
} else {
Sentry.setUser(null);
stopSentryClient();
}
}, [user]);

View file

@ -11,6 +11,7 @@ COPY packages/ui/package.json packages/ui/
COPY packages/map/package.json packages/map/
COPY packages/gpx/package.json packages/gpx/
COPY packages/i18n/package.json packages/i18n/
COPY packages/sentry-config/package.json packages/sentry-config/
COPY packages/api/package.json packages/api/
COPY packages/map-core/package.json packages/map-core/
COPY packages/db/package.json packages/db/

View file

@ -30,6 +30,8 @@ async function registerUser(page: Page, email: string, username: string) {
// Verify both fields retained values before submitting
await expect(page.getByLabel("Email")).toHaveValue(email);
await expect(page.getByLabel("Username")).toHaveValue(username);
// Accept the Terms of Service (required)
await page.getByRole("checkbox").check();
await page.getByRole("button", { name: /Register with Passkey/ }).click();
}