From f6c50489d980d9e6c5d653bafa66b0bbf0ab09c8 Mon Sep 17 00:00:00 2001 From: Ardalan Amini Date: Tue, 8 Mar 2022 18:33:57 +0330 Subject: [PATCH] deps: Add @sentry/node version 6.18.2 --- .env.example | 2 + README.md | 8 ++ package-lock.json | 225 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 2 + src/config.ts | 4 +- src/database.ts | 3 + src/lib/Sentry.ts | 18 ++++ src/lib/index.ts | 1 + src/server.ts | 18 +++- 9 files changed, 275 insertions(+), 6 deletions(-) create mode 100644 src/lib/Sentry.ts diff --git a/.env.example b/.env.example index ba6bb2a..e3ebae2 100644 --- a/.env.example +++ b/.env.example @@ -5,3 +5,5 @@ SERVER_PORT=3000 CORS_ORIGINS=http://localhost:3000 DATABASE_URI= + +SENTRY_DSN= diff --git a/README.md b/README.md index 2de7998..2411c6d 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ XCoins code review repository. - [Common](#common-config) - [Server](#server-config) - [Database](#database-config) + - [Sentry](#sentry-config) - [Start](#start) - [Docker](#docker) - [Directory Layout](#directory-layout) @@ -88,6 +89,12 @@ cp .env.example .env |----------------|:------:|:-------:|:-----------------------------:| | `DATABASE_URI` | string | - | MongoDB connection string uri | +#### Sentry Config + +| Name | Type | Default | Description | +|--------------|:------:|:-------:|:------------------------:| +| `SENTRY_DSN` | string | - | Sentry project DSN value | + ### Start Start the API. @@ -239,6 +246,7 @@ This section includes the issues, changes & improvements I've made, with the tho > `express` doesn't catch async errors properly, so I added this utility to wrap the controllers before passing them > to the `Router` instance. - Added pagination to the endpoints that were listing records. + - Added `Sentry` to track issues in the production environment. diff --git a/package-lock.json b/package-lock.json index 1d9ee3c..b11d2c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,8 @@ "version": "1.0.0", "license": "MIT", "dependencies": { + "@sentry/node": "6.18.2", + "@sentry/tracing": "6.18.2", "body-parser": "1.19.2", "celebrate": "15.0.1", "cors": "2.8.5", @@ -126,6 +128,100 @@ "node": ">= 8" } }, + "node_modules/@sentry/core": { + "version": "6.18.2", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.18.2.tgz", + "integrity": "sha512-r5ad/gq5S/JHc9sd5CUhZQT9ojQ+f+thk/AoGeGawX/8HURZYAgIqD565d6FK0VsZEDkdRMl58z1Qon20h3y1g==", + "dependencies": { + "@sentry/hub": "6.18.2", + "@sentry/minimal": "6.18.2", + "@sentry/types": "6.18.2", + "@sentry/utils": "6.18.2", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/hub": { + "version": "6.18.2", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.18.2.tgz", + "integrity": "sha512-d0AugekMkbnN12b4EXMjseJxtLPc9S20DGobCPUb4oAQT6S2oDQEj1jwP6PQ5vtgyy+GMYWxBMgqAQ4pjVYISQ==", + "dependencies": { + "@sentry/types": "6.18.2", + "@sentry/utils": "6.18.2", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/minimal": { + "version": "6.18.2", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.18.2.tgz", + "integrity": "sha512-n7KYuo34W2LxE+3dnZ47of7XHuORINCnXq66XH72eoj67tf0XeWbIhEJrYGmoLRyRfoCYYrBLWiDl/uTjLzrzQ==", + "dependencies": { + "@sentry/hub": "6.18.2", + "@sentry/types": "6.18.2", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/node": { + "version": "6.18.2", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-6.18.2.tgz", + "integrity": "sha512-1S+44c09n3KVpCYjwOfnA9jKvnpPegpQWM81Nu5J6ToGx+ZiddMq6B9GRXUnFfZ7Z6fJHZzFtySasQC7KqkQoA==", + "dependencies": { + "@sentry/core": "6.18.2", + "@sentry/hub": "6.18.2", + "@sentry/types": "6.18.2", + "@sentry/utils": "6.18.2", + "cookie": "^0.4.1", + "https-proxy-agent": "^5.0.0", + "lru_map": "^0.3.3", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/tracing": { + "version": "6.18.2", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.18.2.tgz", + "integrity": "sha512-hg6NLqrqJ5sUPTyWEQ2RqdnhQVnyLtx8II0IyWxQLDWD8UCe3Mu6G7mroDtakPWcP+lWz6OnKfMEfuhMcxR8fw==", + "dependencies": { + "@sentry/hub": "6.18.2", + "@sentry/minimal": "6.18.2", + "@sentry/types": "6.18.2", + "@sentry/utils": "6.18.2", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/types": { + "version": "6.18.2", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.18.2.tgz", + "integrity": "sha512-WzpJf/Q5aORTzrSwer/As1NlO90dBAQpaHV2ikDDKqOyMWEgjKb5/4gh59p9gH8JMMnLetP1AvQel0fOj5UnUw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/utils": { + "version": "6.18.2", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.18.2.tgz", + "integrity": "sha512-EC619jesknyu4xpwud5WC/5odYLz6JUy7OSFy5405PpdGeh/m8XUvuJAx4zDx0Iz/Mlk0S1Md+ZcQwqkv39dkw==", + "dependencies": { + "@sentry/types": "6.18.2", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@sideway/address": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.3.tgz", @@ -483,6 +579,17 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1500,6 +1607,18 @@ "node": ">= 0.6" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -1717,6 +1836,11 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lru_map": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", + "integrity": "sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0=" + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -2641,8 +2765,7 @@ "node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/tsutils": { "version": "3.21.0", @@ -2904,6 +3027,79 @@ "fastq": "^1.6.0" } }, + "@sentry/core": { + "version": "6.18.2", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.18.2.tgz", + "integrity": "sha512-r5ad/gq5S/JHc9sd5CUhZQT9ojQ+f+thk/AoGeGawX/8HURZYAgIqD565d6FK0VsZEDkdRMl58z1Qon20h3y1g==", + "requires": { + "@sentry/hub": "6.18.2", + "@sentry/minimal": "6.18.2", + "@sentry/types": "6.18.2", + "@sentry/utils": "6.18.2", + "tslib": "^1.9.3" + } + }, + "@sentry/hub": { + "version": "6.18.2", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.18.2.tgz", + "integrity": "sha512-d0AugekMkbnN12b4EXMjseJxtLPc9S20DGobCPUb4oAQT6S2oDQEj1jwP6PQ5vtgyy+GMYWxBMgqAQ4pjVYISQ==", + "requires": { + "@sentry/types": "6.18.2", + "@sentry/utils": "6.18.2", + "tslib": "^1.9.3" + } + }, + "@sentry/minimal": { + "version": "6.18.2", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.18.2.tgz", + "integrity": "sha512-n7KYuo34W2LxE+3dnZ47of7XHuORINCnXq66XH72eoj67tf0XeWbIhEJrYGmoLRyRfoCYYrBLWiDl/uTjLzrzQ==", + "requires": { + "@sentry/hub": "6.18.2", + "@sentry/types": "6.18.2", + "tslib": "^1.9.3" + } + }, + "@sentry/node": { + "version": "6.18.2", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-6.18.2.tgz", + "integrity": "sha512-1S+44c09n3KVpCYjwOfnA9jKvnpPegpQWM81Nu5J6ToGx+ZiddMq6B9GRXUnFfZ7Z6fJHZzFtySasQC7KqkQoA==", + "requires": { + "@sentry/core": "6.18.2", + "@sentry/hub": "6.18.2", + "@sentry/types": "6.18.2", + "@sentry/utils": "6.18.2", + "cookie": "^0.4.1", + "https-proxy-agent": "^5.0.0", + "lru_map": "^0.3.3", + "tslib": "^1.9.3" + } + }, + "@sentry/tracing": { + "version": "6.18.2", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.18.2.tgz", + "integrity": "sha512-hg6NLqrqJ5sUPTyWEQ2RqdnhQVnyLtx8II0IyWxQLDWD8UCe3Mu6G7mroDtakPWcP+lWz6OnKfMEfuhMcxR8fw==", + "requires": { + "@sentry/hub": "6.18.2", + "@sentry/minimal": "6.18.2", + "@sentry/types": "6.18.2", + "@sentry/utils": "6.18.2", + "tslib": "^1.9.3" + } + }, + "@sentry/types": { + "version": "6.18.2", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.18.2.tgz", + "integrity": "sha512-WzpJf/Q5aORTzrSwer/As1NlO90dBAQpaHV2ikDDKqOyMWEgjKb5/4gh59p9gH8JMMnLetP1AvQel0fOj5UnUw==" + }, + "@sentry/utils": { + "version": "6.18.2", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.18.2.tgz", + "integrity": "sha512-EC619jesknyu4xpwud5WC/5odYLz6JUy7OSFy5405PpdGeh/m8XUvuJAx4zDx0Iz/Mlk0S1Md+ZcQwqkv39dkw==", + "requires": { + "@sentry/types": "6.18.2", + "tslib": "^1.9.3" + } + }, "@sideway/address": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.3.tgz", @@ -3160,6 +3356,14 @@ "dev": true, "requires": {} }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + } + }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -3936,6 +4140,15 @@ "toidentifier": "1.0.1" } }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "requires": { + "agent-base": "6", + "debug": "4" + } + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -4100,6 +4313,11 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "lru_map": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", + "integrity": "sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0=" + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -4748,8 +4966,7 @@ "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "tsutils": { "version": "3.21.0", diff --git a/package.json b/package.json index 8111929..11bdef7 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,8 @@ "lint:fix": "npm run lint -- --fix" }, "dependencies": { + "@sentry/node": "6.18.2", + "@sentry/tracing": "6.18.2", "body-parser": "1.19.2", "celebrate": "15.0.1", "cors": "2.8.5", diff --git a/src/config.ts b/src/config.ts index 43c67a2..62b4c72 100644 --- a/src/config.ts +++ b/src/config.ts @@ -15,6 +15,8 @@ export const NODE_ENV = process.env.NODE_ENV = check("NODE_ENV", ENV.DEVELOPMENT export const SERVER_PORT = parseInt(check("SERVER_PORT", "3000"), 10); +export const CORS_ORIGINS = check("CORS_ORIGINS", "http://localhost:3000").split(","); + export const DATABASE_URI = check("DATABASE_URI"); -export const CORS_ORIGINS = check("CORS_ORIGINS", "http://localhost:3000").split(","); +export const SENTRY_DSN = process.env.SENTRY_DSN; diff --git a/src/database.ts b/src/database.ts index f55eff6..297ea07 100644 --- a/src/database.ts +++ b/src/database.ts @@ -1,4 +1,5 @@ import { DATABASE_URI } from "#src/config.js"; +import { Sentry } from "#src/lib/index.js"; import mongoose from "mongoose"; mongoose @@ -7,6 +8,8 @@ mongoose .catch((error) => { console.log("❌ Failed to establish database connection!"); + Sentry.captureException(error); + console.error(error); process.exit(1); diff --git a/src/lib/Sentry.ts b/src/lib/Sentry.ts new file mode 100644 index 0000000..2c575e7 --- /dev/null +++ b/src/lib/Sentry.ts @@ -0,0 +1,18 @@ +import { SENTRY_DSN } from "#src/config.js"; +import * as Sentry from "@sentry/node"; +import * as Tracing from "@sentry/tracing"; + +Sentry.init({ + dsn: SENTRY_DSN, + integrations: [ + // enable HTTP calls tracing + new Sentry.Integrations.Http({ tracing: true }), + // enable Express.js middleware tracing + new Tracing.Integrations.Express(), + ], + // We recommend adjusting this value in production, or using tracesSampler + // for finer control + tracesSampleRate: 1.0, +}); + +export { Sentry }; diff --git a/src/lib/index.ts b/src/lib/index.ts index fa2429e..3ff521e 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -1 +1,2 @@ export * from "./Joi.js"; +export * from "./Sentry.js"; diff --git a/src/server.ts b/src/server.ts index 3d70798..08ab989 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,18 +1,34 @@ import { CORS_ORIGINS } from "#src/config.js"; +import { Sentry } from "#src/lib/index.js"; import routes from "#src/routes/index.js"; import bodyParser from "body-parser"; -import { errors } from "celebrate"; +import { errors, isCelebrateError } from "celebrate"; import cors from "cors"; import express from "express"; const app = express(); +// The request handler must be the first middleware on the app +app.use(Sentry.Handlers.requestHandler({ + user: ["id"], + ip: true, +})); +// TracingHandler creates a trace for every incoming request +app.use(Sentry.Handlers.tracingHandler()); + app.use(cors({ origin: CORS_ORIGINS })); app.use(bodyParser.json()); app.use(routes); +// The error handler must be before any other error middleware and after all controllers +app.use(Sentry.Handlers.errorHandler({ + shouldHandleError(error: Error): boolean { + return !isCelebrateError(error); + }, +})); + app.use(errors()); export default app;