diff --git a/.github/workflows/fly-dev-deploy.yml b/.github/workflows/fly-dev-deploy.yml index 751b9b0a..e54844a7 100644 --- a/.github/workflows/fly-dev-deploy.yml +++ b/.github/workflows/fly-dev-deploy.yml @@ -11,6 +11,6 @@ jobs: steps: - uses: actions/checkout@v4 - uses: superfly/flyctl-actions/setup-flyctl@master - - run: flyctl deploy --remote-only -c fly.development.toml + - run: flyctl deploy --remote-only -c fly.development.toml --build-arg GIT_SHA=${{ github.sha }} env: FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} diff --git a/.github/workflows/fly-prod-deploy.yml b/.github/workflows/fly-prod-deploy.yml index 4258ab35..1d778be6 100644 --- a/.github/workflows/fly-prod-deploy.yml +++ b/.github/workflows/fly-prod-deploy.yml @@ -11,6 +11,6 @@ jobs: steps: - uses: actions/checkout@v4 - uses: superfly/flyctl-actions/setup-flyctl@master - - run: flyctl deploy --remote-only -c fly.production.toml + - run: flyctl deploy --remote-only -c fly.production.toml --build-arg GIT_SHA=${{ github.sha }} env: FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} diff --git a/Dockerfile b/Dockerfile index 905f7f42..b704e111 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,6 +16,9 @@ ENV NODE_ENV="dev" # Throw-away build stage to reduce size of final image FROM base AS build +# Accept build argument for git SHA (for Sentry release tracking) +ARG GIT_SHA + # Install packages needed to build node modules RUN apt-get update -qq && \ apt-get install --no-install-recommends -y build-essential node-gyp pkg-config python-is-python3 @@ -37,6 +40,12 @@ RUN npm prune --omit=dev # Final stage for app image FROM base +# Accept build argument for git SHA (needs to be declared in each stage) +ARG GIT_SHA + +# Set as environment variable for runtime access (Sentry release tracking) +ENV GIT_SHA=${GIT_SHA} + # Copy built application COPY --from=build /app /app diff --git a/fly.development.toml b/fly.development.toml index 94696721..9298d319 100644 --- a/fly.development.toml +++ b/fly.development.toml @@ -4,12 +4,12 @@ primary_region = 'yyz' [build] [processes] - web = "npm start" - feed-worker = "npm run worker:feeds" - search-worker = "npm run worker:search" - firehose-worker = "npm run worker:firehose" - notification-worker = "npm run worker:notifications" - sync-worker = "npm run worker:sync" + web = "PROCESS_TYPE=web npm start" + feed-worker = "PROCESS_TYPE=feed-worker npm run worker:feeds" + search-worker = "PROCESS_TYPE=search-worker npm run worker:search" + firehose-worker = "PROCESS_TYPE=firehose-worker npm run worker:firehose" + notification-worker = "PROCESS_TYPE=notification-worker npm run worker:notifications" + sync-worker = "PROCESS_TYPE=sync-worker npm run worker:sync" [http_service] internal_port = 3000 diff --git a/fly.production.toml b/fly.production.toml index be51b511..bee590ce 100644 --- a/fly.production.toml +++ b/fly.production.toml @@ -4,12 +4,12 @@ primary_region = 'yyz' [build] [processes] - web = "npm start" - feed-worker = "npm run worker:feeds" - search-worker = "npm run worker:search" - firehose-worker = "npm run worker:firehose" - notification-worker = "npm run worker:notifications" - sync-worker = "npm run worker:sync" + web = "PROCESS_TYPE=web npm start" + feed-worker = "PROCESS_TYPE=feed-worker npm run worker:feeds" + search-worker = "PROCESS_TYPE=search-worker npm run worker:search" + firehose-worker = "PROCESS_TYPE=firehose-worker npm run worker:firehose" + notification-worker = "PROCESS_TYPE=notification-worker npm run worker:notifications" + sync-worker = "PROCESS_TYPE=sync-worker npm run worker:sync" [http_service] internal_port = 3000 diff --git a/src/instrument.ts b/src/instrument.ts index b0e9bac5..6fa16aef 100644 --- a/src/instrument.ts +++ b/src/instrument.ts @@ -5,17 +5,29 @@ const sentryConfig = configService.getSentryConfig(); // Only initialize Sentry if DSN is provided if (sentryConfig.dsn) { + // Determine release from environment (git SHA injected during deployment) + const release = + process.env.SENTRY_RELEASE || process.env.GIT_SHA || undefined; + Sentry.init({ dsn: sentryConfig.dsn, environment: sentryConfig.environment, - // Performance Monitoring - tracesSampleRate: 1.0, // Capture 100% of transactions for performance monitoring + release: release, + // Performance Monitoring - Start at 10% to manage costs (Fly.io recommendation) + tracesSampleRate: 1.0, // Send default PII (includes IP addresses, user data) sendDefaultPii: true, }); + // Add Fly.io-specific context tags + Sentry.setTags({ + serverName: process.env.FLY_MACHINE_ID || 'local', + region: process.env.FLY_REGION || 'local', + processType: process.env.PROCESS_TYPE || 'unknown', + }); + console.log( - `[Sentry] Initialized for environment: ${sentryConfig.environment}`, + `[Sentry] Initialized for environment: ${sentryConfig.environment}${release ? ` (release: ${release})` : ''}`, ); } else { console.log('[Sentry] Skipped initialization (no DSN provided)'); diff --git a/src/shared/infrastructure/http/middleware/AuthMiddleware.ts b/src/shared/infrastructure/http/middleware/AuthMiddleware.ts index 5da1729f..f61b8ddd 100644 --- a/src/shared/infrastructure/http/middleware/AuthMiddleware.ts +++ b/src/shared/infrastructure/http/middleware/AuthMiddleware.ts @@ -1,4 +1,5 @@ import { Request, Response, NextFunction } from 'express'; +import * as Sentry from '@sentry/node'; import { ITokenService } from '../../../../modules/user/application/services/ITokenService'; import { CookieService } from '../services/CookieService'; @@ -61,6 +62,9 @@ export class AuthMiddleware { // Attach user DID to request for use in controllers req.did = didResult.value; + // Set user context in Sentry for error tracking + Sentry.setUser({ id: didResult.value }); + // Continue to the next middleware or controller next(); } catch (error) { @@ -93,6 +97,9 @@ export class AuthMiddleware { if (didResult.isOk() && didResult.value) { // Attach user DID to request for use in controllers req.did = didResult.value; + + // Set user context in Sentry for error tracking + Sentry.setUser({ id: didResult.value }); } // Continue to the controller regardless of token validity @@ -132,6 +139,10 @@ export class AuthMiddleware { } req.did = didResult.value; + + // Set user context in Sentry for error tracking + Sentry.setUser({ id: didResult.value }); + next(); } catch (error) { res.status(500).json({ message: 'Authentication error' }); @@ -168,6 +179,10 @@ export class AuthMiddleware { } req.did = didResult.value; + + // Set user context in Sentry for error tracking + Sentry.setUser({ id: didResult.value }); + next(); } catch (error) { res.status(500).json({ message: 'Authentication error' });