diff --git a/docker-compose.yml b/docker-compose.yml index eb12b04..6dc6d1f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,6 +17,7 @@ services: - 'NODE_ENV=$NODE_ENV' - 'DB_TRACING=true' - 'DEBUG=$DEBUG' + - 'SENTRY_DSN=$SENTRY_DSN' volumes: - $PWD:$PWD ports: diff --git a/package-lock.json b/package-lock.json index 8020d7c..e940fb3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -370,6 +370,84 @@ "integrity": "sha512-yprFYuno9FtNsSHVlSWd+nRlmGoAbqbeCwOryP6sC/zoCjhpArcRMYp19EvpSUSizJAlsXEwJv+wcWS9XaXdMw==", "dev": true }, + "@sentry/core": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-4.0.3.tgz", + "integrity": "sha512-vK6Ytk88STVk8rBxXnhOpo3ujlpPfQwiAFZDo+h7qJ0Xci5QxijqOHHZGEoBKF2ZhQAsohtt77DUIvOgiGr6Xw==", + "requires": { + "@sentry/hub": "4.0.1", + "@sentry/minimal": "4.0.1", + "@sentry/types": "4.0.1", + "@sentry/utils": "4.0.1" + } + }, + "@sentry/hub": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-4.0.1.tgz", + "integrity": "sha512-XLpVrB8MJcquZpVstv7pXq1qjWH80mX7hln2CxWfeQTT5OmUMPGz0NbcaUyPA/QisvMYNo9eeVe2+lTnmSn5+Q==", + "requires": { + "@sentry/types": "4.0.0", + "@sentry/utils": "4.0.1" + }, + "dependencies": { + "@sentry/types": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-4.0.0.tgz", + "integrity": "sha512-UaGfdu9DTcdQjyZ7nSQXAX4Nz1EzwDY6lFMfWxvb3hq7qDvr3bg0LOSFyjELbS6RcAkXwjB8BLpicABAXao3ag==" + } + } + }, + "@sentry/minimal": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-4.0.1.tgz", + "integrity": "sha512-KKrgVATezmf/Wt1c0yH8csRwvV4s6TFGY1R3lgBwql3pKl3YYfL7RMqBke5elG9/3zWl6W4G9cleKZGAPLSvTA==", + "requires": { + "@sentry/hub": "4.0.1", + "@sentry/types": "4.0.0" + }, + "dependencies": { + "@sentry/types": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-4.0.0.tgz", + "integrity": "sha512-UaGfdu9DTcdQjyZ7nSQXAX4Nz1EzwDY6lFMfWxvb3hq7qDvr3bg0LOSFyjELbS6RcAkXwjB8BLpicABAXao3ag==" + } + } + }, + "@sentry/node": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-4.0.3.tgz", + "integrity": "sha512-xMGfo3K5mZxsLCIcVLAMp9S5G+YNx33HU5RyQMuZ5zmZjHUJ42HShTanIjbyRlHEURL0JOZsQJ/qoOQxEuEtZQ==", + "requires": { + "@sentry/core": "4.0.3", + "@sentry/hub": "4.0.1", + "@sentry/types": "4.0.1", + "@sentry/utils": "4.0.1", + "cookie": "0.3.1", + "lsmod": "1.0.0", + "md5": "2.2.1", + "stack-trace": "0.0.10" + } + }, + "@sentry/types": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-4.0.1.tgz", + "integrity": "sha512-1Cq2gk/wZuBHB//HO830nykysHEsvZpjFcoIBHyqsJ7GjjcMxRAnO8ix0aw3hRfOsiPgD3mp8QomY9DqRHbjjA==" + }, + "@sentry/utils": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-4.0.1.tgz", + "integrity": "sha512-SYbE1oe94TPnzcMlGZyCgIfo8e0NpkDo8sX1w43yHcE4HUbJ0NpK8z2FF4h52hShaog4ceLIXXSiYIKoKaJa2A==", + "requires": { + "@sentry/types": "4.0.0" + }, + "dependencies": { + "@sentry/types": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-4.0.0.tgz", + "integrity": "sha512-UaGfdu9DTcdQjyZ7nSQXAX4Nz1EzwDY6lFMfWxvb3hq7qDvr3bg0LOSFyjELbS6RcAkXwjB8BLpicABAXao3ag==" + } + } + }, "@types/babel-types": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.4.tgz", @@ -2334,6 +2412,11 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, + "charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=" + }, "chokidar": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", @@ -2892,6 +2975,11 @@ "which": "^1.2.9" } }, + "crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=" + }, "crypto-random-string": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", @@ -4486,8 +4574,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -6744,6 +6831,11 @@ "es5-ext": "~0.10.2" } }, + "lsmod": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-1.0.0.tgz", + "integrity": "sha1-mgD3bco26yP6BTUK/htYXUKZ5ks=" + }, "make-dir": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", @@ -6797,6 +6889,16 @@ "integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w=", "dev": true }, + "md5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", + "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=", + "requires": { + "charenc": "~0.0.1", + "crypt": "~0.0.1", + "is-buffer": "~1.1.1" + } + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", diff --git a/package.json b/package.json index 0f6c0ee..7bd5831 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "homepage": "https://github.com/jenkins-infra/uplink#readme", "devDependencies": { "@feathersjs/cli": "^3.8.2", + "@sentry/types": "^4.0.1", "@types/feathersjs__configuration": "^1.0.1", "@types/feathersjs__express": "^1.1.4", "@types/feathersjs__feathers": "^3.0.4", @@ -46,6 +47,7 @@ "@feathersjs/express": "^1.2.5", "@feathersjs/feathers": "^3.2.1", "@feathersjs/socketio": "^3.2.4", + "@sentry/node": "^4.0.3", "compression": "^1.7.3", "cookie-parser": "^1.4.3", "cors": "^2.8.4", diff --git a/src/app.ts b/src/app.ts index a22b1f0..df794ac 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,11 +1,24 @@ +import logger from './logger'; import path from 'path'; + +/* + * Express-relateed imports that we need because this is running primarily over + * REST + */ +import cookieParser from 'cookie-parser'; import favicon from 'serve-favicon'; import compress from 'compression'; import helmet from 'helmet'; import cors from 'cors'; -import logger from './logger'; +import * as Sentry from '@sentry/node'; +/**/ + + +/* + * Feathers specific imports for setting up the application + */ import feathers from '@feathersjs/feathers'; import configuration from '@feathersjs/configuration'; import authentication from '@feathersjs/authentication'; @@ -14,8 +27,8 @@ import oauth2 from '@feathersjs/authentication-oauth2'; import { Strategy } from 'passport-github'; import express from '@feathersjs/express'; import socketio from '@feathersjs/socketio'; +/**/ -import cookieParser from 'cookie-parser'; import middleware from './middleware'; import services from './services'; @@ -29,6 +42,25 @@ import Export from './controllers/export'; const app = express(feathers()); const settings = configuration(); +if (process.env.SENTRY_DSN) { + Sentry.init({ dsn: process.env.SENTRY_DSN }) + /* + * Cannot case to RequestHandler per Sentry's docs due to compilation error: + * + * src/app.ts:48:11 - error TS2352: Type '(req: ClientRequest, res: + * ServerResponse, next: () => void) => void' cannot be converted to type + * 'RequestHandler'. + * Types of parameters 'req' and 'req' are incompatible. + * Type 'Request' is not comparable to type 'ClientRequest'. + * Property 'aborted' is missing in type 'Request'. + * + * 48 app.use(Sentry.Handlers.requestHandler() as + * RequestHandler); + */ + app.use(Sentry.Handlers.requestHandler() as any); + logger.info('Sentry configured and installed'); +} + // Load app configuration app.configure(settings); // Enable security, CORS, compression, favicon and body parsing @@ -41,7 +73,6 @@ app.use(favicon(path.join(app.get('public'), 'favicon.ico'))); // Host the public folder app.use('/', express.static(app.get('public'))); - // Set up Plugins and providers app.configure(express.rest()); app.configure(socketio()); @@ -105,6 +136,8 @@ app.configure(services); // Set up event channels (see channels.js) app.configure(channels); +// The error handler must be before any other error middleware +app.use(Sentry.Handlers.errorHandler() as any); // Configure a middleware for 404s and the error handler app.use(express.notFound()); app.use(express.errorHandler({ logger })); diff --git a/tools/node b/tools/node index 987c1c7..a8b51cb 100755 --- a/tools/node +++ b/tools/node @@ -22,8 +22,9 @@ exec docker run --rm ${TTY_ARGS} --net host \ --mount type=tmpfs,destination=/.npm \ --mount type=tmpfs,destination=/.config \ -v ${PWD}:${PWD} \ - -e PATH=$PWD/node_modules/.bin:/usr/local/bin:$PATH \ + -e PATH="$PWD/node_modules/.bin:/usr/local/bin:$PATH" \ -e LANG=C.UTF-8 \ + -e DB_CONNECTION_STRING="${DB_CONNECTION_STRING}" \ -e LOG_LEVEL=$LOG_LEVEL \ $(printenv | grep -i \^node | awk '{ print "-e", $1 }') \ node:10 \ diff --git a/tsconfig.json b/tsconfig.json index 22b8f5c..b72ae0a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,12 @@ "esModuleInterop": true, "importHelpers" : true, "target": "es2015", - "sourceMap": true + "sourceMap": true, + "typeRoots" : [ + "./node_modules/@types", + "./node_modules/@sentry/node", + "./node_modules/@sentry/types" + ] }, "include": [ "./src/**/*"