diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 97e41e3..6103bf4 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -148,6 +148,9 @@ jobs: TF_VAR_wd_instance_title: ${{ vars.INSTANCE_TITLE }} TF_VAR_wd_admin_email: ${{ vars.ADMIN_EMAIL }} TF_VAR_wd_instance_description: ${{ vars.INSTANCE_DESCR }} + TF_VAR_sentry_dsn: ${{ secrets.SENTRY_DSN }} + TF_VAR_sentry_access_client_id: ${{ secrets.SENTRY_ACCESS_CLIENT_ID }} + TF_VAR_sentry_access_client_secret: ${{ secrets.SENTRY_ACCESS_CLIENT_SECRET }} - name: retrieve Terraform state KV namespace uses: cloudflare/wrangler-action@2.0.0 @@ -161,7 +164,7 @@ jobs: env: CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }} if: ${{ env.tfstate_kv == '' }} - + - name: store VAPID keys state uses: cloudflare/wrangler-action@2.0.0 with: diff --git a/backend/src/middleware/error.ts b/backend/src/middleware/error.ts index ae0dc65..e48dfd6 100644 --- a/backend/src/middleware/error.ts +++ b/backend/src/middleware/error.ts @@ -1,12 +1,19 @@ import { internalServerError } from '../errors' +import type { Env } from 'wildebeest/backend/src/types/env' +import { initSentry } from 'wildebeest/backend/src/utils/sentry' /** * A Pages middleware function that logs errors to the console and responds with 500 errors and stack-traces. */ -export async function errorHandling(context: EventContext) { +export async function errorHandling(context: EventContext) { + const sentry = initSentry(context.request, context.env, context) + try { return await context.next() } catch (err: any) { + if (sentry !== null) { + sentry.captureException(err) + } console.error(err) return internalServerError() } diff --git a/backend/src/types/env.ts b/backend/src/types/env.ts index f5a1d59..2ea52db 100644 --- a/backend/src/types/env.ts +++ b/backend/src/types/env.ts @@ -16,4 +16,8 @@ export interface Env { ADMIN_EMAIL: string INSTANCE_DESCR: string VAPID_JWK: string + + SENTRY_DSN: string + SENTRY_ACCESS_CLIENT_ID: string + SENTRY_ACCESS_CLIENT_SECRET: string } diff --git a/backend/src/utils/sentry.ts b/backend/src/utils/sentry.ts new file mode 100644 index 0000000..ff69e4b --- /dev/null +++ b/backend/src/utils/sentry.ts @@ -0,0 +1,30 @@ +import { Toucan } from 'toucan-js' +import type { Env } from 'wildebeest/backend/src/types/env' + +export function initSentry(request: Request, env: Env, context: any) { + if (env.SENTRY_DSN === '') { + return null + } + + const headers: any = {} + + if (env.SENTRY_ACCESS_CLIENT_ID !== '' && env.SENTRY_ACCESS_CLIENT_SECRET !== '') { + headers['CF-Access-Client-ID'] = env.SENTRY_ACCESS_CLIENT_ID + headers['CF-Access-Client-Secret'] = env.SENTRY_ACCESS_CLIENT_SECRET + } + + const sentry = new Toucan({ + dsn: env.SENTRY_DSN, + context, + request, + transportOptions: { headers }, + }) + const colo = request.cf && request.cf.colo ? request.cf.colo : 'UNKNOWN' + sentry.setTag('colo', colo) + + // cf-connecting-ip should always be present, but if not we can fallback to XFF. + const ipAddress = request.headers.get('cf-connecting-ip') || request.headers.get('x-forwarded-for') + const userAgent = request.headers.get('user-agent') || '' + sentry.setUser({ ip: ipAddress, userAgent: userAgent, colo: colo }) + return sentry +} diff --git a/package.json b/package.json index f0b449a..6b2bb61 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "dependencies": { "@types/cookie": "^0.5.1", "cookie": "^0.5.0", - "http-message-signatures": "^0.1.2" + "http-message-signatures": "^0.1.2", + "toucan-js": "^3.1.0" } } diff --git a/tf/main.tf b/tf/main.tf index 5d310e8..86b3736 100644 --- a/tf/main.tf +++ b/tf/main.tf @@ -45,6 +45,19 @@ variable "wd_instance_description" { sensitive = true } +variable "sentry_dsn" { + type = string + sensitive = true +} +variable "sentry_access_client_id" { + type = string + sensitive = true +} +variable "sentry_access_client_secret" { + type = string + sensitive = true +} + terraform { required_providers { cloudflare = { @@ -99,6 +112,10 @@ resource "cloudflare_pages_project" "wildebeest_pages_project" { ADMIN_EMAIL = var.wd_admin_email INSTANCE_DESCR = var.wd_instance_description VAPID_JWK = sensitive(file("${path.module}/vapid_jwk")) + + SENTRY_DSN = var.sentry_dsn + SENTRY_ACCESS_CLIENT_ID = var.sentry_access_client_id + SENTRY_ACCESS_CLIENT_SECRET = var.sentry_access_client_secret } kv_namespaces = { KV_CACHE = sensitive(cloudflare_workers_kv_namespace.wildebeest_cache.id) diff --git a/yarn.lock b/yarn.lock index fbb53f0..05950f6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -828,6 +828,28 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@sentry/core@7.28.1": + version "7.28.1" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.28.1.tgz#c712ce17469b18b01606108817be24a99ed2116e" + integrity sha512-7wvnuvn/mrAfcugWoCG/3pqDIrUgH5t+HisMJMGw0h9Tc33KqrmqMDCQVvjlrr2pWrw/vuUCFdm8CbUHJ832oQ== + dependencies: + "@sentry/types" "7.28.1" + "@sentry/utils" "7.28.1" + tslib "^1.9.3" + +"@sentry/types@7.28.1": + version "7.28.1" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.28.1.tgz#9018b4c152b475de9bedd267237393d3c9b1253d" + integrity sha512-DvSplMVrVEmOzR2M161V5+B8Up3vR71xMqJOpWTzE9TqtFJRGPtqT/5OBsNJJw1+/j2ssMcnKwbEo9Q2EGeS6g== + +"@sentry/utils@7.28.1": + version "7.28.1" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.28.1.tgz#0a7b6aa4b09e91e4d1aded2a8c8dbaf818cee96e" + integrity sha512-75/jzLUO9HH09iC9TslNimGbxOP3jgn89P+q7uR+rp2fJfRExHVeKJZQdK0Ij4/SmE7TJ3Uh2r154N0INZEx1g== + dependencies: + "@sentry/types" "7.28.1" + tslib "^1.9.3" + "@sinclair/typebox@^0.24.1": version "0.24.51" resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz" @@ -4058,6 +4080,15 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +toucan-js@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/toucan-js/-/toucan-js-3.1.0.tgz#412cf43c259e702f46427e465adb2f588b7eea85" + integrity sha512-bRbq/HB+aSfwbsSoCNI6qyPXx2bhsscxSYxnAY63xXv9lIeOLUYfvYdOIBWfAVj9QHNST+X83GQ0lj/llvHpVg== + dependencies: + "@sentry/core" "7.28.1" + "@sentry/types" "7.28.1" + "@sentry/utils" "7.28.1" + tree-kill@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" @@ -4077,7 +4108,7 @@ ts-jest@^29.0.3: semver "7.x" yargs-parser "^21.0.1" -tslib@^1.8.1: +tslib@^1.8.1, tslib@^1.9.3: version "1.14.1" resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==