From d8e6759bbf7f811d809208b334abe6463e4b668b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 24 Nov 2025 21:33:22 +0000 Subject: [PATCH 1/3] Initial plan From a66a053ca9c06f326ebf73f8a87d83404f51a38a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 24 Nov 2025 21:41:05 +0000 Subject: [PATCH 2/3] Improve Sentry user context implementation with error handling and documentation Co-authored-by: egelhaus <156946629+egelhaus@users.noreply.github.com> --- .env.example | 3 + .../src/sentry/sentry.user.context.ts | 52 ++++++++++------- .../src/sentry/sentry.user.interceptor.ts | 30 ++++++++++ .../src/sentry/sentry.user.context.ts | 58 +++++++++++-------- 4 files changed, 96 insertions(+), 47 deletions(-) diff --git a/.env.example b/.env.example index 61d2a02067..da1488d080 100644 --- a/.env.example +++ b/.env.example @@ -83,6 +83,9 @@ NEXT_PUBLIC_POLOTNO="" # NOT_SECURED=false API_LIMIT=30 # The limit of the public API hour limit +# Sentry Error Tracking Settings (optional) +# NEXT_PUBLIC_SENTRY_DSN="" # Your Sentry DSN for error tracking. User context will be automatically associated with errors. + # Payment settings FEE_AMOUNT=0.05 STRIPE_PUBLISHABLE_KEY="" diff --git a/libraries/nestjs-libraries/src/sentry/sentry.user.context.ts b/libraries/nestjs-libraries/src/sentry/sentry.user.context.ts index 476d163f92..932bc15835 100644 --- a/libraries/nestjs-libraries/src/sentry/sentry.user.context.ts +++ b/libraries/nestjs-libraries/src/sentry/sentry.user.context.ts @@ -14,26 +14,30 @@ export const setSentryUserContext = (user: User | null) => { return; } - if (!user) { - // Clear user context when no user is present - Sentry.setUser(null); - return; - } + try { + if (!user) { + // Clear user context when no user is present + Sentry.setUser(null); + return; + } - Sentry.setUser({ - id: user.id, - email: user.email, - username: user.email, // Use email as username since that's the primary identifier - // Add additional useful context - ip_address: undefined, // Let Sentry auto-detect IP - }); + Sentry.setUser({ + id: user.id, + email: user.email, + username: user.email, // Use email as username since that's the primary identifier + // Add additional useful context + ip_address: undefined, // Let Sentry auto-detect IP + }); - // Also set additional tags for better filtering in Sentry - Sentry.setTag('user.activated', user.activated); - Sentry.setTag('user.provider', user.providerName || 'local'); - - if (user.isSuperAdmin) { - Sentry.setTag('user.super_admin', true); + // Also set additional tags for better filtering in Sentry + Sentry.setTag('user.activated', user.activated); + Sentry.setTag('user.provider', user.providerName || 'local'); + + if (user.isSuperAdmin) { + Sentry.setTag('user.super_admin', true); + } + } catch { + // Silently fail if Sentry throws an error - we don't want to break the app } }; @@ -48,8 +52,12 @@ export const clearSentryUserContext = () => { return; } - Sentry.setUser(null); - Sentry.setTag('user.activated', null); - Sentry.setTag('user.provider', null); - Sentry.setTag('user.super_admin', null); + try { + Sentry.setUser(null); + Sentry.setTag('user.activated', ''); + Sentry.setTag('user.provider', ''); + Sentry.setTag('user.super_admin', ''); + } catch { + // Silently fail if Sentry throws an error - we don't want to break the app + } }; diff --git a/libraries/nestjs-libraries/src/sentry/sentry.user.interceptor.ts b/libraries/nestjs-libraries/src/sentry/sentry.user.interceptor.ts index 80f458e1ee..1dae1c9949 100644 --- a/libraries/nestjs-libraries/src/sentry/sentry.user.interceptor.ts +++ b/libraries/nestjs-libraries/src/sentry/sentry.user.interceptor.ts @@ -7,6 +7,36 @@ import { setSentryUserContext } from './sentry.user.context'; /** * Interceptor that automatically sets Sentry user context for all requests. * This interceptor runs after authentication middleware has set req.user. + * + * Usage Options: + * + * 1. Global interceptor (recommended for APIs with consistent auth): + * In your app.module.ts: + * ```typescript + * import { APP_INTERCEPTOR } from '@nestjs/core'; + * import { SentryUserInterceptor } from '@gitroom/nestjs-libraries/sentry/sentry.user.interceptor'; + * + * @Module({ + * providers: [ + * { provide: APP_INTERCEPTOR, useClass: SentryUserInterceptor }, + * ], + * }) + * export class AppModule {} + * ``` + * + * 2. Controller-level (for specific controllers): + * ```typescript + * @UseInterceptors(SentryUserInterceptor) + * @Controller('users') + * export class UsersController {} + * ``` + * + * 3. Method-level (for specific routes): + * ```typescript + * @UseInterceptors(SentryUserInterceptor) + * @Get('profile') + * getProfile() {} + * ``` */ @Injectable() export class SentryUserInterceptor implements NestInterceptor { diff --git a/libraries/react-shared-libraries/src/sentry/sentry.user.context.ts b/libraries/react-shared-libraries/src/sentry/sentry.user.context.ts index 874f24677c..e27de97aad 100644 --- a/libraries/react-shared-libraries/src/sentry/sentry.user.context.ts +++ b/libraries/react-shared-libraries/src/sentry/sentry.user.context.ts @@ -23,29 +23,33 @@ export const setSentryUserContext = (user: UserInfo | null) => { return; } - if (!user) { - // Clear user context when no user is present - Sentry.setUser(null); - return; - } + try { + if (!user) { + // Clear user context when no user is present + Sentry.setUser(null); + return; + } - Sentry.setUser({ - id: user.id, - email: user.email, - username: user.email, // Use email as username since that's the primary identifier - }); + Sentry.setUser({ + id: user.id, + email: user.email, + username: user.email, // Use email as username since that's the primary identifier + }); - // Also set additional tags for better filtering in Sentry - if (user.orgId) { - Sentry.setTag('user.org_id', user.orgId); - } - - if (user.role) { - Sentry.setTag('user.role', user.role); - } - - if (user.tier) { - Sentry.setTag('user.tier', user.tier); + // Also set additional tags for better filtering in Sentry + if (user.orgId) { + Sentry.setTag('user.org_id', user.orgId); + } + + if (user.role) { + Sentry.setTag('user.role', user.role); + } + + if (user.tier) { + Sentry.setTag('user.tier', user.tier); + } + } catch { + // Silently fail if Sentry throws an error - we don't want to break the app } }; @@ -60,8 +64,12 @@ export const clearSentryUserContext = () => { return; } - Sentry.setUser(null); - Sentry.setTag('user.org_id', ''); - Sentry.setTag('user.role', ''); - Sentry.setTag('user.tier', ''); + try { + Sentry.setUser(null); + Sentry.setTag('user.org_id', ''); + Sentry.setTag('user.role', ''); + Sentry.setTag('user.tier', ''); + } catch { + // Silently fail if Sentry throws an error - we don't want to break the app + } }; From b5bbf878a25d4b960b402f199e1d402018b07590 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 24 Nov 2025 21:46:37 +0000 Subject: [PATCH 3/3] Use null instead of empty strings to clear Sentry tags Co-authored-by: egelhaus <156946629+egelhaus@users.noreply.github.com> --- .../nestjs-libraries/src/sentry/sentry.user.context.ts | 6 +++--- .../src/sentry/sentry.user.context.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/nestjs-libraries/src/sentry/sentry.user.context.ts b/libraries/nestjs-libraries/src/sentry/sentry.user.context.ts index 932bc15835..df3146942a 100644 --- a/libraries/nestjs-libraries/src/sentry/sentry.user.context.ts +++ b/libraries/nestjs-libraries/src/sentry/sentry.user.context.ts @@ -54,9 +54,9 @@ export const clearSentryUserContext = () => { try { Sentry.setUser(null); - Sentry.setTag('user.activated', ''); - Sentry.setTag('user.provider', ''); - Sentry.setTag('user.super_admin', ''); + Sentry.setTag('user.activated', null); + Sentry.setTag('user.provider', null); + Sentry.setTag('user.super_admin', null); } catch { // Silently fail if Sentry throws an error - we don't want to break the app } diff --git a/libraries/react-shared-libraries/src/sentry/sentry.user.context.ts b/libraries/react-shared-libraries/src/sentry/sentry.user.context.ts index e27de97aad..dd0206467d 100644 --- a/libraries/react-shared-libraries/src/sentry/sentry.user.context.ts +++ b/libraries/react-shared-libraries/src/sentry/sentry.user.context.ts @@ -66,9 +66,9 @@ export const clearSentryUserContext = () => { try { Sentry.setUser(null); - Sentry.setTag('user.org_id', ''); - Sentry.setTag('user.role', ''); - Sentry.setTag('user.tier', ''); + Sentry.setTag('user.org_id', null); + Sentry.setTag('user.role', null); + Sentry.setTag('user.tier', null); } catch { // Silently fail if Sentry throws an error - we don't want to break the app }