diff --git a/.env.example b/.env.example index 44b6e2bb..30c16a75 100644 --- a/.env.example +++ b/.env.example @@ -5,6 +5,13 @@ GITHUB_CLIENT_ID= GITHUB_CLIENT_SECRET= GITHUB_CALLBACK_URL= +SENTRY_DSN= +SENTRY_ORG= +SENTRY_PROJECT= +SENTRY_TRACES_SAMPLE_RATE= +SENTRY_PROFILES_SAMPLE_RATE= +SENTRY_ENV= + SMTP_HOST= SMTP_PORT= SMTP_EMAIL_ADDRESS= diff --git a/.gitignore b/.gitignore index 66013e8a..327d76e7 100644 --- a/.gitignore +++ b/.gitignore @@ -45,7 +45,13 @@ Thumbs.db .next .vscode .env +.env.local pnpm-lock.yaml # Database -data/ \ No newline at end of file +data/ +# Sentry Config File +.sentryclirc + +# Sentry Config File +.env.sentry-build-plugin diff --git a/apps/api/src/main.ts b/apps/api/src/main.ts index 2bae2f15..b0d84662 100644 --- a/apps/api/src/main.ts +++ b/apps/api/src/main.ts @@ -3,7 +3,7 @@ * This is only a minimal backend to get started. */ -import { LoggerService, ValidationPipe } from '@nestjs/common' +import { Logger, LoggerService, ValidationPipe } from '@nestjs/common' import { NestFactory } from '@nestjs/core' import { AppModule } from './app/app.module' @@ -11,6 +11,10 @@ import chalk from 'chalk' import moment from 'moment' import { QueryTransformPipe } from './common/query.transform.pipe' import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger' +import * as Sentry from '@sentry/node' +import { ProfilingIntegration } from '@sentry/profiling-node' + +export const sentryEnv = process.env.SENTRY_ENV || 'production' class CustomLogger implements LoggerService { log(message: string) { @@ -42,11 +46,39 @@ class CustomLogger implements LoggerService { } } -async function bootstrap() { +async function initializeSentry() { + if ( + !process.env.SENTRY_DSN || + !process.env.SENTRY_ORG || + !process.env.SENTRY_PROJECT || + !process.env.SENTRY_AUTH_TOKEN + ) { + Logger.warn( + 'Missing one or more Sentry environment variables. Skipping initialization...' + ) + } else { + Sentry.init({ + dsn: process.env.SENTRY_DSN, + enabled: sentryEnv !== 'test' && sentryEnv !== 'e2e', + environment: sentryEnv, + tracesSampleRate: + parseFloat(process.env.SENTRY_TRACES_SAMPLE_RATE) || 1.0, + profilesSampleRate: + parseFloat(process.env.SENTRY_PROFILES_SAMPLE_RATE) || 1.0, + integrations: [new ProfilingIntegration()], + debug: sentryEnv.startsWith('dev') + }) + } +} + +async function initializeNestApp() { const logger = new CustomLogger() const app = await NestFactory.create(AppModule, { logger }) + app.use(Sentry.Handlers.requestHandler()) + app.use(Sentry.Handlers.tracingHandler()) + const globalPrefix = 'api' app.setGlobalPrefix(globalPrefix) app.useGlobalPipes( @@ -64,10 +96,29 @@ async function bootstrap() { .build() const document = SwaggerModule.createDocument(app, swaggerConfig) SwaggerModule.setup('docs', app, document) + app.use(Sentry.Handlers.errorHandler()) await app.listen(port) logger.log( `🚀 Application is running on: http://localhost:${port}/${globalPrefix}` ) } +async function bootstrap() { + try { + await initializeSentry() + await Sentry.startSpan( + { + op: 'applicationBootstrap', + name: 'Application Bootstrap Process' + }, + async () => { + await initializeNestApp() + } + ) + } catch (error) { + Sentry.captureException(error) + Logger.error(error) + } +} + bootstrap() diff --git a/apps/api/webpack.config.js b/apps/api/webpack.config.js index d7731705..4616dc7f 100644 --- a/apps/api/webpack.config.js +++ b/apps/api/webpack.config.js @@ -1,13 +1,24 @@ const { composePlugins, withNx } = require('@nx/webpack') +const { sentryWebpackPlugin } = require('@sentry/webpack-plugin') -// Nx plugins for webpack. module.exports = composePlugins( withNx({ - target: 'node' + target: 'node', + devtool: 'source-map', + plugins: [ + ...(process.env.SENTRY_ENV === 'production' || + process.env.SENTRY_ENV === 'stage' + ? [ + sentryWebpackPlugin({ + org: process.env.SENTRY_ORG, + project: process.env.SENTRY_PROJECT, + authToken: process.env.SENTRY_AUTH_TOKEN + }) + ] + : []) + ] }), (config) => { - // Update the webpack config as needed here. - // e.g. `config.plugins.push(new MyPlugin())` return config } ) diff --git a/docs/contributing-to-keyshade/environment-variables.md b/docs/contributing-to-keyshade/environment-variables.md index 251930f0..1f908b70 100644 --- a/docs/contributing-to-keyshade/environment-variables.md +++ b/docs/contributing-to-keyshade/environment-variables.md @@ -16,6 +16,12 @@ Here's the description of the environment variables used in the project. You can * **SMTP\_EMAIL\_ADDRESS:** The email address you want to be sending out the emails from. * **SMTP\_PASSWORD:** The app password for your email account. * **GITHUB\_CLIENT\_ID, GITHUB\_CLIENT\_SECRET, GITHUB\_CALLBACK\_URL:** These settings can be configured by adding an OAuth app in your GitHub account's developer section. Please note that it's not mandatory, until and unless you want to support GitHub OAuth. +* **SENTRY\_DSN**: The Data Source Name (DSN) for Sentry, a platform for monitoring, troubleshooting, and resolving issues in real-time. This is used to configure error tracking in the project. +* **SENTRY\_ORG**: The organization ID associated with your Sentry account. +* **SENTRY\_PROJECT**: The project ID within your Sentry organization where events will be reported. +* **SENTRY\_TRACES\_SAMPLE\_RATE**: The sample rate for collecting transaction traces in Sentry. It determines the percentage of transactions to capture traces for. +* **SENTRY\_PROFILES\_SAMPLE\_RATE**: The sample rate for collecting performance profiles in Sentry. It determines the percentage of requests to capture performance profiles for. +* **SENTRY\_ENV**: The The environment in which the app is running. It can be either 'development', 'production', or 'test'. Please note that it's not mandatory, it will default to "production" enviroment for the sentry configuration. * **FROM\_EMAIL**: The display of the email sender title. * **JWT\_SECRET**: The secret used to sign the JWT tokens. It is insignificant in the development environment. * **WEB\_FRONTEND\_URL, WORKSPACE\_FRONTEND\_URL**: The URLs of the web and workspace frontend respectively. These are used in the emails sometimes and in other spaces of the application too. diff --git a/package.json b/package.json index 353223c9..e5338f4d 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ "prettier:fix": "nx run-many -t prettier:lint --parallel", "prettier:fix:api": "nx run api:prettier:fix", "build": "nx run-many -t build -p api web workspace --parallel --maxParallel 3", - "build:api": "nx run api:build --configuration=production", + "build:api": "nx run api:build --configuration=production && pnpm sentry:sourcemaps", "build:web": "nx run web:build --configuration=production", "build:workspace": "nx run workspace:build", "test": "nx run-many -t test --parallel", @@ -114,7 +114,9 @@ "db:validate": "nx run api:prisma:validate", "db:format": "nx run api:prisma:format", "db:reset": "nx run api:prisma:reset", - "prepare": "husky install" + "prepare": "husky install", + "sentry:sourcemaps": "sentry-cli sourcemaps inject ./dist && sentry-cli sourcemaps upload ./dist || pnpm run sentry:sourcemaps:exit", + "sentry:sourcemaps:exit": "echo 'Failed to upload source maps to Sentry' && exit 1" }, "devDependencies": { "@nestjs/schematics": "^10.0.3", @@ -129,6 +131,7 @@ "@nx/react": "17.2.7", "@nx/webpack": "17.2.7", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.11", + "@sentry/webpack-plugin": "^2.14.1", "@svgr/webpack": "^8.1.0", "@swc-node/register": "~1.6.8", "@swc/core": "~1.3.102", @@ -183,6 +186,15 @@ "@nestjs/schedule": "^4.0.0", "@nestjs/swagger": "^7.1.17", "@prisma/client": "^5.7.1", + "@semantic-release/changelog": "^6.0.3", + "@semantic-release/commit-analyzer": "^11.1.0", + "@semantic-release/git": "^10.0.1", + "@semantic-release/release-notes-generator": "^12.1.0", + "@sentry/cli": "^2.28.0", + "@sentry/node": "^7.100.1", + "@sentry/profiling-node": "^7.100.1", + "@supabase/supabase-js": "^2.39.2", + "@types/jsonwebtoken": "^9.0.5", "axios": "^1.6.5", "chalk": "4.1.2", "class-transformer": "^0.5.1", diff --git a/tsconfig.base.json b/tsconfig.base.json index 3a1c5bb6..2083a484 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -14,9 +14,13 @@ "skipLibCheck": true, "skipDefaultLibCheck": true, "baseUrl": ".", + "paths": { "cli": ["apps/cli/src/index.ts"] - } + }, + + "inlineSources": true, + "sourceRoot": "/" }, "exclude": ["node_modules", "tmp"] }