From 7cad4073d47affaca8fe19db087b217dfc80e6f5 Mon Sep 17 00:00:00 2001 From: Till JS Date: Sun, 22 Mar 2026 19:16:21 +0100 Subject: [PATCH] feat(error-tracking): add browser error tracking to all 19 SvelteKit web apps Add @sentry/browser integration via shared-error-tracking/browser export and hooks.client.ts in every web app for client-side error reporting to GlitchTip. Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/calendar/apps/web/package.json | 1 + apps/calendar/apps/web/src/hooks.client.ts | 12 ++ apps/chat/apps/web/package.json | 1 + apps/chat/apps/web/src/hooks.client.ts | 12 ++ apps/clock/apps/web/package.json | 1 + apps/clock/apps/web/src/hooks.client.ts | 12 ++ apps/contacts/apps/web/package.json | 1 + apps/contacts/apps/web/src/hooks.client.ts | 12 ++ apps/context/apps/web/package.json | 1 + apps/context/apps/web/src/hooks.client.ts | 12 ++ apps/manacore/apps/web/package.json | 1 + apps/manacore/apps/web/src/hooks.client.ts | 12 ++ apps/manadeck/apps/web/package.json | 1 + apps/manadeck/apps/web/src/hooks.client.ts | 12 ++ apps/matrix/apps/web/package.json | 1 + apps/matrix/apps/web/src/hooks.client.ts | 12 ++ apps/mukke/apps/web/src/hooks.client.ts | 12 ++ apps/nutriphi/apps/web/package.json | 1 + apps/nutriphi/apps/web/src/hooks.client.ts | 12 ++ apps/photos/apps/web/package.json | 1 + apps/photos/apps/web/src/hooks.client.ts | 12 ++ apps/picture/apps/web/package.json | 1 + apps/picture/apps/web/src/hooks.client.ts | 12 ++ apps/planta/apps/web/package.json | 1 + apps/planta/apps/web/src/hooks.client.ts | 12 ++ apps/presi/apps/web/package.json | 1 + apps/presi/apps/web/src/hooks.client.ts | 12 ++ apps/questions/apps/web/package.json | 1 + apps/questions/apps/web/src/hooks.client.ts | 12 ++ apps/skilltree/apps/web/package.json | 1 + apps/skilltree/apps/web/src/hooks.client.ts | 12 ++ apps/storage/apps/web/package.json | 1 + apps/storage/apps/web/src/hooks.client.ts | 12 ++ apps/todo/apps/web/package.json | 1 + apps/todo/apps/web/src/hooks.client.ts | 12 ++ apps/zitare/apps/web/package.json | 1 + apps/zitare/apps/web/src/hooks.client.ts | 12 ++ docs/ERROR_TRACKING.md | 69 +++++++++- packages/shared-error-tracking/package.json | 5 + packages/shared-error-tracking/src/browser.ts | 128 ++++++++++++++++++ 40 files changed, 443 insertions(+), 5 deletions(-) create mode 100644 apps/calendar/apps/web/src/hooks.client.ts create mode 100644 apps/chat/apps/web/src/hooks.client.ts create mode 100644 apps/clock/apps/web/src/hooks.client.ts create mode 100644 apps/contacts/apps/web/src/hooks.client.ts create mode 100644 apps/context/apps/web/src/hooks.client.ts create mode 100644 apps/manacore/apps/web/src/hooks.client.ts create mode 100644 apps/manadeck/apps/web/src/hooks.client.ts create mode 100644 apps/matrix/apps/web/src/hooks.client.ts create mode 100644 apps/mukke/apps/web/src/hooks.client.ts create mode 100644 apps/nutriphi/apps/web/src/hooks.client.ts create mode 100644 apps/photos/apps/web/src/hooks.client.ts create mode 100644 apps/picture/apps/web/src/hooks.client.ts create mode 100644 apps/planta/apps/web/src/hooks.client.ts create mode 100644 apps/presi/apps/web/src/hooks.client.ts create mode 100644 apps/questions/apps/web/src/hooks.client.ts create mode 100644 apps/skilltree/apps/web/src/hooks.client.ts create mode 100644 apps/storage/apps/web/src/hooks.client.ts create mode 100644 apps/todo/apps/web/src/hooks.client.ts create mode 100644 apps/zitare/apps/web/src/hooks.client.ts create mode 100644 packages/shared-error-tracking/src/browser.ts diff --git a/apps/calendar/apps/web/package.json b/apps/calendar/apps/web/package.json index 4c90fb86b..0427343fd 100644 --- a/apps/calendar/apps/web/package.json +++ b/apps/calendar/apps/web/package.json @@ -48,6 +48,7 @@ "@manacore/shared-auth": "workspace:*", "@manacore/shared-auth-ui": "workspace:*", "@manacore/shared-branding": "workspace:*", + "@manacore/shared-error-tracking": "workspace:*", "@manacore/shared-feedback-service": "workspace:*", "@manacore/shared-feedback-ui": "workspace:*", "@manacore/shared-i18n": "workspace:*", diff --git a/apps/calendar/apps/web/src/hooks.client.ts b/apps/calendar/apps/web/src/hooks.client.ts new file mode 100644 index 000000000..07c1a0078 --- /dev/null +++ b/apps/calendar/apps/web/src/hooks.client.ts @@ -0,0 +1,12 @@ +import { initErrorTracking, handleSvelteError } from '@manacore/shared-error-tracking/browser'; +import type { HandleClientError } from '@sveltejs/kit'; + +initErrorTracking({ + serviceName: 'calendar-web', + dsn: (window as any).__PUBLIC_GLITCHTIP_DSN__, + environment: import.meta.env.MODE, +}); + +export const handleError: HandleClientError = ({ error }) => { + handleSvelteError(error); +}; diff --git a/apps/chat/apps/web/package.json b/apps/chat/apps/web/package.json index c9cc9d27b..c04145ad2 100644 --- a/apps/chat/apps/web/package.json +++ b/apps/chat/apps/web/package.json @@ -38,6 +38,7 @@ "@manacore/shared-auth": "workspace:*", "@manacore/shared-auth-ui": "workspace:*", "@manacore/shared-branding": "workspace:*", + "@manacore/shared-error-tracking": "workspace:*", "@manacore/shared-feedback-service": "workspace:*", "@manacore/shared-feedback-ui": "workspace:*", "@manacore/shared-i18n": "workspace:*", diff --git a/apps/chat/apps/web/src/hooks.client.ts b/apps/chat/apps/web/src/hooks.client.ts new file mode 100644 index 000000000..15549bb31 --- /dev/null +++ b/apps/chat/apps/web/src/hooks.client.ts @@ -0,0 +1,12 @@ +import { initErrorTracking, handleSvelteError } from '@manacore/shared-error-tracking/browser'; +import type { HandleClientError } from '@sveltejs/kit'; + +initErrorTracking({ + serviceName: 'chat-web', + dsn: (window as any).__PUBLIC_GLITCHTIP_DSN__, + environment: import.meta.env.MODE, +}); + +export const handleError: HandleClientError = ({ error }) => { + handleSvelteError(error); +}; diff --git a/apps/clock/apps/web/package.json b/apps/clock/apps/web/package.json index 766f99189..05a858e18 100644 --- a/apps/clock/apps/web/package.json +++ b/apps/clock/apps/web/package.json @@ -41,6 +41,7 @@ "@manacore/shared-auth": "workspace:*", "@manacore/shared-auth-ui": "workspace:*", "@manacore/shared-branding": "workspace:*", + "@manacore/shared-error-tracking": "workspace:*", "@manacore/shared-feedback-service": "workspace:*", "@manacore/shared-feedback-ui": "workspace:*", "@manacore/shared-i18n": "workspace:*", diff --git a/apps/clock/apps/web/src/hooks.client.ts b/apps/clock/apps/web/src/hooks.client.ts new file mode 100644 index 000000000..8f23fa0cb --- /dev/null +++ b/apps/clock/apps/web/src/hooks.client.ts @@ -0,0 +1,12 @@ +import { initErrorTracking, handleSvelteError } from '@manacore/shared-error-tracking/browser'; +import type { HandleClientError } from '@sveltejs/kit'; + +initErrorTracking({ + serviceName: 'clock-web', + dsn: (window as any).__PUBLIC_GLITCHTIP_DSN__, + environment: import.meta.env.MODE, +}); + +export const handleError: HandleClientError = ({ error }) => { + handleSvelteError(error); +}; diff --git a/apps/contacts/apps/web/package.json b/apps/contacts/apps/web/package.json index 6ec7334ed..ffec37fc9 100644 --- a/apps/contacts/apps/web/package.json +++ b/apps/contacts/apps/web/package.json @@ -40,6 +40,7 @@ "@manacore/shared-auth": "workspace:*", "@manacore/shared-auth-ui": "workspace:*", "@manacore/shared-branding": "workspace:*", + "@manacore/shared-error-tracking": "workspace:*", "@manacore/shared-feedback-service": "workspace:*", "@manacore/shared-feedback-ui": "workspace:*", "@manacore/shared-help-content": "workspace:*", diff --git a/apps/contacts/apps/web/src/hooks.client.ts b/apps/contacts/apps/web/src/hooks.client.ts new file mode 100644 index 000000000..1ad077157 --- /dev/null +++ b/apps/contacts/apps/web/src/hooks.client.ts @@ -0,0 +1,12 @@ +import { initErrorTracking, handleSvelteError } from '@manacore/shared-error-tracking/browser'; +import type { HandleClientError } from '@sveltejs/kit'; + +initErrorTracking({ + serviceName: 'contacts-web', + dsn: (window as any).__PUBLIC_GLITCHTIP_DSN__, + environment: import.meta.env.MODE, +}); + +export const handleError: HandleClientError = ({ error }) => { + handleSvelteError(error); +}; diff --git a/apps/context/apps/web/package.json b/apps/context/apps/web/package.json index d225d8caa..004c5d74f 100644 --- a/apps/context/apps/web/package.json +++ b/apps/context/apps/web/package.json @@ -37,6 +37,7 @@ "@manacore/shared-auth": "workspace:*", "@manacore/shared-auth-ui": "workspace:*", "@manacore/shared-branding": "workspace:*", + "@manacore/shared-error-tracking": "workspace:*", "@manacore/shared-feedback-service": "workspace:*", "@manacore/shared-feedback-ui": "workspace:*", "@manacore/shared-i18n": "workspace:*", diff --git a/apps/context/apps/web/src/hooks.client.ts b/apps/context/apps/web/src/hooks.client.ts new file mode 100644 index 000000000..6b0048d4a --- /dev/null +++ b/apps/context/apps/web/src/hooks.client.ts @@ -0,0 +1,12 @@ +import { initErrorTracking, handleSvelteError } from '@manacore/shared-error-tracking/browser'; +import type { HandleClientError } from '@sveltejs/kit'; + +initErrorTracking({ + serviceName: 'context-web', + dsn: (window as any).__PUBLIC_GLITCHTIP_DSN__, + environment: import.meta.env.MODE, +}); + +export const handleError: HandleClientError = ({ error }) => { + handleSvelteError(error); +}; diff --git a/apps/manacore/apps/web/package.json b/apps/manacore/apps/web/package.json index 793a9f3e3..6b25c01cd 100644 --- a/apps/manacore/apps/web/package.json +++ b/apps/manacore/apps/web/package.json @@ -48,6 +48,7 @@ "@manacore/shared-auth-ui": "workspace:*", "@manacore/shared-branding": "workspace:*", "@manacore/shared-config": "workspace:*", + "@manacore/shared-error-tracking": "workspace:*", "@manacore/shared-feedback-service": "workspace:*", "@manacore/shared-feedback-ui": "workspace:*", "@manacore/shared-i18n": "workspace:*", diff --git a/apps/manacore/apps/web/src/hooks.client.ts b/apps/manacore/apps/web/src/hooks.client.ts new file mode 100644 index 000000000..5f56d4358 --- /dev/null +++ b/apps/manacore/apps/web/src/hooks.client.ts @@ -0,0 +1,12 @@ +import { initErrorTracking, handleSvelteError } from '@manacore/shared-error-tracking/browser'; +import type { HandleClientError } from '@sveltejs/kit'; + +initErrorTracking({ + serviceName: 'manacore-web', + dsn: (window as any).__PUBLIC_GLITCHTIP_DSN__, + environment: import.meta.env.MODE, +}); + +export const handleError: HandleClientError = ({ error }) => { + handleSvelteError(error); +}; diff --git a/apps/manadeck/apps/web/package.json b/apps/manadeck/apps/web/package.json index 0d7da7434..1e1f28b89 100644 --- a/apps/manadeck/apps/web/package.json +++ b/apps/manadeck/apps/web/package.json @@ -34,6 +34,7 @@ "@manacore/shared-auth": "workspace:*", "@manacore/shared-auth-ui": "workspace:*", "@manacore/shared-branding": "workspace:*", + "@manacore/shared-error-tracking": "workspace:*", "@manacore/shared-feedback-service": "workspace:*", "@manacore/shared-feedback-ui": "workspace:*", "@manacore/shared-config": "workspace:*", diff --git a/apps/manadeck/apps/web/src/hooks.client.ts b/apps/manadeck/apps/web/src/hooks.client.ts new file mode 100644 index 000000000..bdd94188e --- /dev/null +++ b/apps/manadeck/apps/web/src/hooks.client.ts @@ -0,0 +1,12 @@ +import { initErrorTracking, handleSvelteError } from '@manacore/shared-error-tracking/browser'; +import type { HandleClientError } from '@sveltejs/kit'; + +initErrorTracking({ + serviceName: 'manadeck-web', + dsn: (window as any).__PUBLIC_GLITCHTIP_DSN__, + environment: import.meta.env.MODE, +}); + +export const handleError: HandleClientError = ({ error }) => { + handleSvelteError(error); +}; diff --git a/apps/matrix/apps/web/package.json b/apps/matrix/apps/web/package.json index c657869a8..f7c47980c 100644 --- a/apps/matrix/apps/web/package.json +++ b/apps/matrix/apps/web/package.json @@ -37,6 +37,7 @@ "dependencies": { "@manacore/shared-auth": "workspace:*", "@manacore/shared-branding": "workspace:*", + "@manacore/shared-error-tracking": "workspace:*", "@manacore/shared-i18n": "workspace:*", "@manacore/shared-icons": "workspace:*", "@manacore/shared-tailwind": "workspace:*", diff --git a/apps/matrix/apps/web/src/hooks.client.ts b/apps/matrix/apps/web/src/hooks.client.ts new file mode 100644 index 000000000..c95f9f0cd --- /dev/null +++ b/apps/matrix/apps/web/src/hooks.client.ts @@ -0,0 +1,12 @@ +import { initErrorTracking, handleSvelteError } from '@manacore/shared-error-tracking/browser'; +import type { HandleClientError } from '@sveltejs/kit'; + +initErrorTracking({ + serviceName: 'matrix-web', + dsn: (window as any).__PUBLIC_GLITCHTIP_DSN__, + environment: import.meta.env.MODE, +}); + +export const handleError: HandleClientError = ({ error }) => { + handleSvelteError(error); +}; diff --git a/apps/mukke/apps/web/src/hooks.client.ts b/apps/mukke/apps/web/src/hooks.client.ts new file mode 100644 index 000000000..6911989b3 --- /dev/null +++ b/apps/mukke/apps/web/src/hooks.client.ts @@ -0,0 +1,12 @@ +import { initErrorTracking, handleSvelteError } from '@manacore/shared-error-tracking/browser'; +import type { HandleClientError } from '@sveltejs/kit'; + +initErrorTracking({ + serviceName: 'mukke-web', + dsn: (window as any).__PUBLIC_GLITCHTIP_DSN__, + environment: import.meta.env.MODE, +}); + +export const handleError: HandleClientError = ({ error }) => { + handleSvelteError(error); +}; diff --git a/apps/nutriphi/apps/web/package.json b/apps/nutriphi/apps/web/package.json index 1aead977e..e6d9c054f 100644 --- a/apps/nutriphi/apps/web/package.json +++ b/apps/nutriphi/apps/web/package.json @@ -43,6 +43,7 @@ "@manacore/shared-auth": "workspace:*", "@manacore/shared-auth-ui": "workspace:*", "@manacore/shared-branding": "workspace:*", + "@manacore/shared-error-tracking": "workspace:*", "@manacore/shared-feedback-service": "workspace:*", "@manacore/shared-feedback-ui": "workspace:*", "@manacore/shared-i18n": "workspace:*", diff --git a/apps/nutriphi/apps/web/src/hooks.client.ts b/apps/nutriphi/apps/web/src/hooks.client.ts new file mode 100644 index 000000000..d8732005b --- /dev/null +++ b/apps/nutriphi/apps/web/src/hooks.client.ts @@ -0,0 +1,12 @@ +import { initErrorTracking, handleSvelteError } from '@manacore/shared-error-tracking/browser'; +import type { HandleClientError } from '@sveltejs/kit'; + +initErrorTracking({ + serviceName: 'nutriphi-web', + dsn: (window as any).__PUBLIC_GLITCHTIP_DSN__, + environment: import.meta.env.MODE, +}); + +export const handleError: HandleClientError = ({ error }) => { + handleSvelteError(error); +}; diff --git a/apps/photos/apps/web/package.json b/apps/photos/apps/web/package.json index ca84594cd..210e16e82 100644 --- a/apps/photos/apps/web/package.json +++ b/apps/photos/apps/web/package.json @@ -34,6 +34,7 @@ "@manacore/shared-auth": "workspace:*", "@manacore/shared-auth-ui": "workspace:*", "@manacore/shared-branding": "workspace:*", + "@manacore/shared-error-tracking": "workspace:*", "@manacore/shared-feedback-service": "workspace:*", "@manacore/shared-feedback-ui": "workspace:*", "@manacore/shared-help-content": "workspace:*", diff --git a/apps/photos/apps/web/src/hooks.client.ts b/apps/photos/apps/web/src/hooks.client.ts new file mode 100644 index 000000000..1e8c24bd1 --- /dev/null +++ b/apps/photos/apps/web/src/hooks.client.ts @@ -0,0 +1,12 @@ +import { initErrorTracking, handleSvelteError } from '@manacore/shared-error-tracking/browser'; +import type { HandleClientError } from '@sveltejs/kit'; + +initErrorTracking({ + serviceName: 'photos-web', + dsn: (window as any).__PUBLIC_GLITCHTIP_DSN__, + environment: import.meta.env.MODE, +}); + +export const handleError: HandleClientError = ({ error }) => { + handleSvelteError(error); +}; diff --git a/apps/picture/apps/web/package.json b/apps/picture/apps/web/package.json index 85d524f9c..738162e55 100644 --- a/apps/picture/apps/web/package.json +++ b/apps/picture/apps/web/package.json @@ -21,6 +21,7 @@ "@manacore/shared-auth": "workspace:*", "@manacore/shared-auth-ui": "workspace:*", "@manacore/shared-branding": "workspace:*", + "@manacore/shared-error-tracking": "workspace:*", "@manacore/shared-feedback-service": "workspace:*", "@manacore/shared-feedback-ui": "workspace:*", "@manacore/shared-i18n": "workspace:*", diff --git a/apps/picture/apps/web/src/hooks.client.ts b/apps/picture/apps/web/src/hooks.client.ts new file mode 100644 index 000000000..85dade6a6 --- /dev/null +++ b/apps/picture/apps/web/src/hooks.client.ts @@ -0,0 +1,12 @@ +import { initErrorTracking, handleSvelteError } from '@manacore/shared-error-tracking/browser'; +import type { HandleClientError } from '@sveltejs/kit'; + +initErrorTracking({ + serviceName: 'picture-web', + dsn: (window as any).__PUBLIC_GLITCHTIP_DSN__, + environment: import.meta.env.MODE, +}); + +export const handleError: HandleClientError = ({ error }) => { + handleSvelteError(error); +}; diff --git a/apps/planta/apps/web/package.json b/apps/planta/apps/web/package.json index c4115e2d2..a190bb55d 100644 --- a/apps/planta/apps/web/package.json +++ b/apps/planta/apps/web/package.json @@ -34,6 +34,7 @@ "@manacore/shared-auth": "workspace:*", "@manacore/shared-auth-ui": "workspace:*", "@manacore/shared-branding": "workspace:*", + "@manacore/shared-error-tracking": "workspace:*", "@manacore/shared-i18n": "workspace:*", "@manacore/shared-icons": "workspace:*", "@manacore/shared-tailwind": "workspace:*", diff --git a/apps/planta/apps/web/src/hooks.client.ts b/apps/planta/apps/web/src/hooks.client.ts new file mode 100644 index 000000000..7e71e0839 --- /dev/null +++ b/apps/planta/apps/web/src/hooks.client.ts @@ -0,0 +1,12 @@ +import { initErrorTracking, handleSvelteError } from '@manacore/shared-error-tracking/browser'; +import type { HandleClientError } from '@sveltejs/kit'; + +initErrorTracking({ + serviceName: 'planta-web', + dsn: (window as any).__PUBLIC_GLITCHTIP_DSN__, + environment: import.meta.env.MODE, +}); + +export const handleError: HandleClientError = ({ error }) => { + handleSvelteError(error); +}; diff --git a/apps/presi/apps/web/package.json b/apps/presi/apps/web/package.json index cfe8a06c3..e48aa5dfc 100644 --- a/apps/presi/apps/web/package.json +++ b/apps/presi/apps/web/package.json @@ -36,6 +36,7 @@ "@manacore/shared-auth": "workspace:*", "@manacore/shared-auth-ui": "workspace:*", "@manacore/shared-branding": "workspace:*", + "@manacore/shared-error-tracking": "workspace:*", "@manacore/shared-feedback-service": "workspace:*", "@manacore/shared-feedback-ui": "workspace:*", "@manacore/shared-i18n": "workspace:*", diff --git a/apps/presi/apps/web/src/hooks.client.ts b/apps/presi/apps/web/src/hooks.client.ts new file mode 100644 index 000000000..33a5f6263 --- /dev/null +++ b/apps/presi/apps/web/src/hooks.client.ts @@ -0,0 +1,12 @@ +import { initErrorTracking, handleSvelteError } from '@manacore/shared-error-tracking/browser'; +import type { HandleClientError } from '@sveltejs/kit'; + +initErrorTracking({ + serviceName: 'presi-web', + dsn: (window as any).__PUBLIC_GLITCHTIP_DSN__, + environment: import.meta.env.MODE, +}); + +export const handleError: HandleClientError = ({ error }) => { + handleSvelteError(error); +}; diff --git a/apps/questions/apps/web/package.json b/apps/questions/apps/web/package.json index 3bae8f494..562094799 100644 --- a/apps/questions/apps/web/package.json +++ b/apps/questions/apps/web/package.json @@ -38,6 +38,7 @@ "@manacore/shared-utils": "workspace:*", "@manacore/shared-auth-ui": "workspace:*", "@manacore/shared-branding": "workspace:*", + "@manacore/shared-error-tracking": "workspace:*", "@manacore/shared-i18n": "workspace:*", "@manacore/shared-icons": "workspace:*", "@manacore/shared-tailwind": "workspace:*", diff --git a/apps/questions/apps/web/src/hooks.client.ts b/apps/questions/apps/web/src/hooks.client.ts new file mode 100644 index 000000000..a5682ee5b --- /dev/null +++ b/apps/questions/apps/web/src/hooks.client.ts @@ -0,0 +1,12 @@ +import { initErrorTracking, handleSvelteError } from '@manacore/shared-error-tracking/browser'; +import type { HandleClientError } from '@sveltejs/kit'; + +initErrorTracking({ + serviceName: 'questions-web', + dsn: (window as any).__PUBLIC_GLITCHTIP_DSN__, + environment: import.meta.env.MODE, +}); + +export const handleError: HandleClientError = ({ error }) => { + handleSvelteError(error); +}; diff --git a/apps/skilltree/apps/web/package.json b/apps/skilltree/apps/web/package.json index 9f23f2a7b..3e6a7df6a 100644 --- a/apps/skilltree/apps/web/package.json +++ b/apps/skilltree/apps/web/package.json @@ -38,6 +38,7 @@ "@manacore/shared-auth": "workspace:*", "@manacore/shared-auth-ui": "workspace:*", "@manacore/shared-branding": "workspace:*", + "@manacore/shared-error-tracking": "workspace:*", "@manacore/shared-i18n": "workspace:*", "@manacore/shared-icons": "workspace:*", "@manacore/shared-tailwind": "workspace:*", diff --git a/apps/skilltree/apps/web/src/hooks.client.ts b/apps/skilltree/apps/web/src/hooks.client.ts new file mode 100644 index 000000000..8bc9f2235 --- /dev/null +++ b/apps/skilltree/apps/web/src/hooks.client.ts @@ -0,0 +1,12 @@ +import { initErrorTracking, handleSvelteError } from '@manacore/shared-error-tracking/browser'; +import type { HandleClientError } from '@sveltejs/kit'; + +initErrorTracking({ + serviceName: 'skilltree-web', + dsn: (window as any).__PUBLIC_GLITCHTIP_DSN__, + environment: import.meta.env.MODE, +}); + +export const handleError: HandleClientError = ({ error }) => { + handleSvelteError(error); +}; diff --git a/apps/storage/apps/web/package.json b/apps/storage/apps/web/package.json index 7410b5981..579a03673 100644 --- a/apps/storage/apps/web/package.json +++ b/apps/storage/apps/web/package.json @@ -42,6 +42,7 @@ "@manacore/shared-auth": "workspace:*", "@manacore/shared-auth-ui": "workspace:*", "@manacore/shared-branding": "workspace:*", + "@manacore/shared-error-tracking": "workspace:*", "@manacore/shared-feedback-service": "workspace:*", "@manacore/shared-feedback-ui": "workspace:*", "@manacore/shared-i18n": "workspace:*", diff --git a/apps/storage/apps/web/src/hooks.client.ts b/apps/storage/apps/web/src/hooks.client.ts new file mode 100644 index 000000000..b529a520b --- /dev/null +++ b/apps/storage/apps/web/src/hooks.client.ts @@ -0,0 +1,12 @@ +import { initErrorTracking, handleSvelteError } from '@manacore/shared-error-tracking/browser'; +import type { HandleClientError } from '@sveltejs/kit'; + +initErrorTracking({ + serviceName: 'storage-web', + dsn: (window as any).__PUBLIC_GLITCHTIP_DSN__, + environment: import.meta.env.MODE, +}); + +export const handleError: HandleClientError = ({ error }) => { + handleSvelteError(error); +}; diff --git a/apps/todo/apps/web/package.json b/apps/todo/apps/web/package.json index 26b0f224a..ba879be8c 100644 --- a/apps/todo/apps/web/package.json +++ b/apps/todo/apps/web/package.json @@ -44,6 +44,7 @@ "@manacore/shared-auth": "workspace:*", "@manacore/shared-auth-ui": "workspace:*", "@manacore/shared-branding": "workspace:*", + "@manacore/shared-error-tracking": "workspace:*", "@manacore/shared-feedback-service": "workspace:*", "@manacore/shared-feedback-ui": "workspace:*", "@manacore/shared-i18n": "workspace:*", diff --git a/apps/todo/apps/web/src/hooks.client.ts b/apps/todo/apps/web/src/hooks.client.ts new file mode 100644 index 000000000..9ad0cc201 --- /dev/null +++ b/apps/todo/apps/web/src/hooks.client.ts @@ -0,0 +1,12 @@ +import { initErrorTracking, handleSvelteError } from '@manacore/shared-error-tracking/browser'; +import type { HandleClientError } from '@sveltejs/kit'; + +initErrorTracking({ + serviceName: 'todo-web', + dsn: (window as any).__PUBLIC_GLITCHTIP_DSN__, + environment: import.meta.env.MODE, +}); + +export const handleError: HandleClientError = ({ error }) => { + handleSvelteError(error); +}; diff --git a/apps/zitare/apps/web/package.json b/apps/zitare/apps/web/package.json index 5292072cd..d869e5ec2 100644 --- a/apps/zitare/apps/web/package.json +++ b/apps/zitare/apps/web/package.json @@ -36,6 +36,7 @@ "@manacore/shared-auth": "workspace:*", "@manacore/shared-auth-ui": "workspace:*", "@manacore/shared-branding": "workspace:*", + "@manacore/shared-error-tracking": "workspace:*", "@manacore/shared-feedback-service": "workspace:*", "@manacore/shared-feedback-ui": "workspace:*", "@manacore/shared-i18n": "workspace:*", diff --git a/apps/zitare/apps/web/src/hooks.client.ts b/apps/zitare/apps/web/src/hooks.client.ts new file mode 100644 index 000000000..d5127a6f2 --- /dev/null +++ b/apps/zitare/apps/web/src/hooks.client.ts @@ -0,0 +1,12 @@ +import { initErrorTracking, handleSvelteError } from '@manacore/shared-error-tracking/browser'; +import type { HandleClientError } from '@sveltejs/kit'; + +initErrorTracking({ + serviceName: 'zitare-web', + dsn: (window as any).__PUBLIC_GLITCHTIP_DSN__, + environment: import.meta.env.MODE, +}); + +export const handleError: HandleClientError = ({ error }) => { + handleSvelteError(error); +}; diff --git a/docs/ERROR_TRACKING.md b/docs/ERROR_TRACKING.md index 2e9c04315..a4cd5a24a 100644 --- a/docs/ERROR_TRACKING.md +++ b/docs/ERROR_TRACKING.md @@ -32,6 +32,8 @@ Self-hosted, open-source error tracking for all ManaCore apps using [GlitchTip]( ## Project DSNs +### Backend DSNs + | App | Project ID | DSN | |-----|-----------|-----| | Calendar | 1 | `https://7dcf6e8648a04b85b2cb275921c059d5@glitchtip.mana.how/1` | @@ -43,6 +45,14 @@ Self-hosted, open-source error tracking for all ManaCore apps using [GlitchTip]( | Clock | 7 | `https://4d5ea890-019d-4a98-8e98-34bc3e374e0a@glitchtip.mana.how/7` | | Zitare | 8 | `https://53b87191-3d86-4628-a8c7-cb97b3f69e06@glitchtip.mana.how/8` | +### Frontend DSNs + +Frontend projects need to be created separately in GlitchTip so that browser errors are tracked in their own project (separate from backend errors). + +To create frontend projects, use the Django shell command in the [Administration](#server-access) section with `platform="javascript"` and name them `{app}-web` (e.g., `calendar-web`). + +Then set `PUBLIC_GLITCHTIP_DSN` in the web app's Docker environment. + ## Integration ### Backend (NestJS) @@ -91,18 +101,56 @@ NestJS-specific (`@manacore/shared-error-tracking/nestjs`): | `SentryExceptionFilter` | Global exception filter (captures 5xx only) | | `setUserFromRequest(req)` | Set user context from JWT request | -### Web Apps (SvelteKit) - Future +### Web Apps (SvelteKit) + +All 19 SvelteKit web apps have frontend error tracking via `hooks.client.ts`: ```typescript // src/hooks.client.ts -import { initErrorTracking } from '@manacore/shared-error-tracking'; +import { initErrorTracking, handleSvelteError } from '@manacore/shared-error-tracking/browser'; +import type { HandleClientError } from '@sveltejs/kit'; initErrorTracking({ - serviceName: 'calendar-web', - dsn: import.meta.env.PUBLIC_GLITCHTIP_DSN, + serviceName: 'calendar-web', + dsn: (window as any).__PUBLIC_GLITCHTIP_DSN__, + environment: import.meta.env.MODE, }); + +export const handleError: HandleClientError = ({ error }) => { + handleSvelteError(error); +}; ``` +The DSN is injected at runtime via `hooks.server.ts` (same pattern as auth URL and backend URL): + +```typescript +// In hooks.server.ts +const PUBLIC_GLITCHTIP_DSN = process.env.PUBLIC_GLITCHTIP_DSN || ''; +// Injected into HTML as window.__PUBLIC_GLITCHTIP_DSN__ +``` + +**What gets captured:** +- Unhandled JavaScript exceptions +- Unhandled promise rejections +- SvelteKit rendering errors (via `handleError` hook) +- Network errors, failed API calls + +**Environment variable:** +```env +PUBLIC_GLITCHTIP_DSN=https://@glitchtip.mana.how/ +``` + +Browser-specific exports (`@manacore/shared-error-tracking/browser`): + +| Function | Purpose | +|----------|---------| +| `initErrorTracking(options)` | Initialize browser Sentry SDK | +| `handleSvelteError(error)` | Capture SvelteKit client errors | +| `captureException(error, context)` | Capture an error manually | +| `captureMessage(message, level)` | Capture a message | +| `setUser({ id, email })` | Set user context | +| `setTag(key, value)` | Set extra context tags | + ## Docker Setup In `docker-compose.macmini.yml`: @@ -160,13 +208,24 @@ print(f"DSN: https://{key.public_key}@glitchtip.mana.how/{proj.id}") ### Adding a New App -1. Create project in GlitchTip (via UI or Django shell) +**Backend:** + +1. Create project in GlitchTip (via UI or Django shell, `platform="node"`) 2. Copy DSN 3. Add `@manacore/shared-error-tracking` to backend package.json 4. Create `src/instrument.ts` 5. Import `./instrument` as first line in `src/main.ts` 6. Set `GLITCHTIP_DSN` env variable +**Frontend (SvelteKit):** + +1. Create project in GlitchTip (`platform="javascript"`, name: `{app}-web`) +2. Copy DSN +3. Create `src/hooks.client.ts` (see [Web Apps section](#web-apps-sveltekit)) +4. Add `window.__PUBLIC_GLITCHTIP_DSN__` injection in `hooks.server.ts` +5. Add `https://glitchtip.mana.how` to CSP `connect-src` (if CSP is configured) +6. Set `PUBLIC_GLITCHTIP_DSN` env variable in Docker + ## Monitoring - GlitchTip Dashboard: https://glitchtip.mana.how diff --git a/packages/shared-error-tracking/package.json b/packages/shared-error-tracking/package.json index 55e321adf..7c0175fb5 100644 --- a/packages/shared-error-tracking/package.json +++ b/packages/shared-error-tracking/package.json @@ -12,6 +12,10 @@ "./nestjs": { "types": "./dist/nestjs.d.ts", "default": "./dist/nestjs.js" + }, + "./browser": { + "types": "./dist/browser.d.ts", + "default": "./dist/browser.js" } }, "files": [ @@ -22,6 +26,7 @@ "type-check": "tsc --noEmit" }, "dependencies": { + "@sentry/browser": "^9.0.0", "@sentry/node": "^9.0.0" }, "peerDependencies": { diff --git a/packages/shared-error-tracking/src/browser.ts b/packages/shared-error-tracking/src/browser.ts new file mode 100644 index 000000000..4f7354f24 --- /dev/null +++ b/packages/shared-error-tracking/src/browser.ts @@ -0,0 +1,128 @@ +/** + * Browser Error Tracking for ManaCore SvelteKit Apps + * + * Uses @sentry/browser with GlitchTip as the self-hosted backend. + * This is the browser counterpart to the Node.js `index.ts`. + * + * @example + * ```typescript + * // In hooks.client.ts + * import { initErrorTracking, handleSvelteError } from '@manacore/shared-error-tracking/browser'; + * import type { HandleClientError } from '@sveltejs/kit'; + * + * initErrorTracking({ + * serviceName: 'calendar-web', + * dsn: (window as any).__PUBLIC_GLITCHTIP_DSN__, + * environment: import.meta.env.MODE, + * }); + * + * export const handleError: HandleClientError = ({ error }) => { + * handleSvelteError(error); + * }; + * ``` + */ + +import * as Sentry from '@sentry/browser'; + +export interface BrowserErrorTrackingOptions { + /** Service/app name (e.g. 'calendar-web', 'contacts-web') */ + serviceName: string; + /** GlitchTip/Sentry DSN. If not set, error tracking is disabled. */ + dsn?: string; + /** Environment (development, staging, production) */ + environment?: string; + /** Release/version string */ + release?: string; + /** Sample rate for error events (0.0 to 1.0, default: 1.0) */ + sampleRate?: number; + /** Sample rate for performance traces (0.0 to 1.0, default: 0.1) */ + tracesSampleRate?: number; + /** Enable debug mode (default: false) */ + debug?: boolean; +} + +let initialized = false; + +/** + * Initialize browser error tracking. + * If no DSN is provided, error tracking is silently disabled. + */ +export function initErrorTracking(options: BrowserErrorTrackingOptions): void { + if (initialized) return; + if (typeof window === 'undefined') return; + + const dsn = options.dsn; + + if (!dsn) { + if (options.debug) { + console.log(`[ErrorTracking] No DSN configured for ${options.serviceName} - disabled`); + } + return; + } + + Sentry.init({ + dsn, + environment: options.environment || 'production', + release: options.release, + sampleRate: options.sampleRate ?? 1.0, + tracesSampleRate: options.tracesSampleRate ?? 0.1, + debug: options.debug ?? false, + initialScope: { + tags: { service: options.serviceName }, + }, + }); + + initialized = true; + + if (options.debug) { + console.log( + `[ErrorTracking] Initialized for ${options.serviceName} (${options.environment || 'production'})` + ); + } +} + +/** + * Handle SvelteKit client errors. + * Use this in hooks.client.ts handleError export. + */ +export function handleSvelteError(error: unknown): void { + if (!initialized) return; + Sentry.captureException(error); +} + +/** + * Capture an exception manually + */ +export function captureException(error: unknown, context?: Record): void { + if (!initialized) return; + Sentry.captureException(error, { extra: context }); +} + +/** + * Capture a message + */ +export function captureMessage( + message: string, + level: 'fatal' | 'error' | 'warning' | 'info' | 'debug' = 'info' +): void { + if (!initialized) return; + Sentry.captureMessage(message, level); +} + +/** + * Set user context for error reports + */ +export function setUser(user: { id: string; email?: string } | null): void { + if (!initialized) return; + Sentry.setUser(user); +} + +/** + * Set extra context tags + */ +export function setTag(key: string, value: string): void { + if (!initialized) return; + Sentry.setTag(key, value); +} + +export { Sentry };