Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,22 @@ IDENTITY_PROVIDER_ENCRYPTION_PRIVATE_KEY='{"kty":"RSA","kid":"{key_id}","use":"e
BACKEND_API_TOKEN=api_token

# If 'true', frontend debug messages will be logged even in production
PUBLIC_DEBUG=false
PUBLIC_DEBUG=false

# FlatCache disk cache settings

# An absolute path to the cache location
CACHE_DIR=/var/data/cache
# "Time-To-Live" (TTL) means each cached item will be removed after {env.CACHE_TTL} seconds, even if it's still needed.
# 86400000 seconds = 24 hours
CACHE_TTL=86400000
# "Least Recently Used" (LRU) means the cache will only keep the most recent {env.CACHE_LRU_SIZE} items.
CACHE_LRU_SIZE=1000
# The cache checks for expired items every {env.CACHE_EXPIRATION_INTERVAL} seconds and removes them.
# 3600000 seconds = 1 hour
CACHE_EXPIRATION_INTERVAL=3600000

# A DSN tells a Sentry SDK where to send events so the events are associated with the correct project.
# You can find the DSN in your project settings by navigating to [Project] > Settings > Client Keys (DSN) in sentry.io.
PUBLIC_FRONTEND_SENTRY_DSN=https://example.ingest.de.sentry.io/example
BACKEND_SENTRY_DSN=https://example.ingest.de.sentry.io/example
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ In order to install dependencies for all modules and build all modules (although

```bash
yarn install
yarn workspaces foreach -A build
yarn workspaces foreach -A --topological run build
```

When adding interdependencies between the modules, use yarn’s `workspace:` syntax:
Expand Down
2 changes: 2 additions & 0 deletions backend/vaa-strapi/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,5 @@ AWS_S3_REGION=us-east-1
## - in development it points directly to LocalStack host and is appended by the S3 bucket name in Strapi's `plugin.ts`
STATIC_CONTENT_BASE_URL=http://localhost:4566
STATIC_MEDIA_CONTENT_PATH=public/media

BACKEND_SENTRY_DSN=https://example.ingest.de.sentry.io/example
6 changes: 5 additions & 1 deletion backend/vaa-strapi/config/middlewares.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,9 @@ module.exports = ({ env }) => [
},
'strapi::session',
'strapi::favicon',
'strapi::public'
'strapi::public',
{
name: 'global::error-capture',
config: {}
}
];
9 changes: 9 additions & 0 deletions backend/vaa-strapi/config/plugins.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { staticSettings } from '@openvaa/app-shared';
// eslint-disable-next-line @typescript-eslint/no-require-imports
const aws = require('@aws-sdk/client-ses');

Expand Down Expand Up @@ -83,6 +84,14 @@ export default ({ env }) => {
'openvaa-admin-tools': {
enabled: true,
resolve: './src/plugins/openvaa-admin-tools'
},
sentry: {
enabled: staticSettings.analytics.sentryErrorReporting,
config: {
dsn: env('BACKEND_SENTRY_DSN'),
environment: env('NODE_ENV'),
sendMetadata: true
}
}
};
};
1 change: 1 addition & 0 deletions backend/vaa-strapi/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"@openvaa/app-shared": "workspace:^",
"@openvaa/strapi-admin-tools": "workspace:^",
"@strapi/plugin-documentation": "^5.9.0",
"@strapi/plugin-sentry": "^5.12.5",
"@strapi/plugin-users-permissions": "^5.9.0",
"@strapi/provider-email-nodemailer": "^5.9.0",
"@strapi/provider-upload-aws-s3": "^5.9.0",
Expand Down
13 changes: 13 additions & 0 deletions backend/vaa-strapi/src/middlewares/error-capture.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { Core } from '@strapi/strapi';
import type { Context, Next } from 'koa';

export default (_config: Record<string, unknown>, { strapi }: { strapi: Core.Strapi }) => {
return async (_ctx: Context, next: Next) => {
try {
await next();
} catch (error) {
strapi.plugin('sentry').service('sentry').sendError(error);
throw error;
}
};
};
2 changes: 2 additions & 0 deletions docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ services:
IDENTITY_PROVIDER_ENCRYPTION_PRIVATE_KEY: ${IDENTITY_PROVIDER_ENCRYPTION_PRIVATE_KEY}
BACKEND_API_TOKEN: ${BACKEND_API_TOKEN}
PUBLIC_DEBUG: ${PUBLIC_DEBUG}
PUBLIC_FRONTEND_SENTRY_DSN: ${PUBLIC_FRONTEND_SENTRY_DSN}
awslocal:
extends:
file: ./backend/vaa-strapi/docker-compose.dev.yml
Expand Down Expand Up @@ -72,6 +73,7 @@ services:
STATIC_MEDIA_CONTENT_PATH: ${STATIC_MEDIA_CONTENT_PATH}
LOCALSTACK_ENDPOINT: http://awslocal:4566
PUBLIC_BROWSER_FRONTEND_URL: ${PUBLIC_BROWSER_FRONTEND_URL}
BACKEND_SENTRY_DSN: ${BACKEND_SENTRY_DSN}
postgres:
extends:
file: ./backend/vaa-strapi/docker-compose.dev.yml
Expand Down
4 changes: 3 additions & 1 deletion frontend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ PUBLIC_SERVER_BACKEND_URL=http://strapi:1337
# Used to reach frontend instance from a browser
PUBLIC_BROWSER_FRONTEND_URL=http://localhost:5173
# Used to reach frontend instance from a server (differs from `PUBLIC_BROWSER_FRONTEND_URL` when using Docker)
PUBLIC_SERVER_FRONTEND_URL=http://frontend:5173
PUBLIC_SERVER_FRONTEND_URL=http://frontend:5173

PUBLIC_FRONTEND_SENTRY_DSN=https://example.ingest.de.sentry.io/example
4 changes: 3 additions & 1 deletion frontend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ vite.config.ts.*.mjs
package-lock.json

# Locally stored data files
/data
/data
# Sentry Config File
.env.sentry-build-plugin
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"@openvaa/data": "workspace:^",
"@openvaa/filters": "workspace:^",
"@openvaa/matching": "workspace:^",
"@sentry/sveltekit": "^9.24.0",
"@sveltekit-i18n/parser-icu": "^1.0.8",
"intl-messageformat": "^10.7.11",
"isomorphic-dompurify": "^2.19.0",
Expand Down
32 changes: 32 additions & 0 deletions frontend/src/hooks.client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { staticSettings } from '@openvaa/app-shared';
import { constants } from '$lib/utils/constants';

let optionalHandleErrorWithSentry = () => {};

if (staticSettings.analytics.sentryErrorReporting) {
import('@sentry/sveltekit').then(Sentry => {
Sentry.init({
dsn: constants.PUBLIC_FRONTEND_SENTRY_DSN,

tracesSampleRate: 1.0,

// This sets the sample rate to be 10%. You may want this to be 100% while
// in development and sample at a lower rate in production
replaysSessionSampleRate: 0.1,

// If the entire session is not sampled, use the below sample rate to sample
// sessions when an error occurs.
replaysOnErrorSampleRate: 1.0,

// If you don't want to use Session Replay, just remove the line below:
integrations: [Sentry.replayIntegration()]
});

optionalHandleErrorWithSentry = Sentry.handleErrorWithSentry()
});
}

// If you have a custom error handler, pass it to `handleErrorWithSentry`
// Note, since optionalHandleErrorWithSentry is assigned in a promise,
Copy link
Collaborator

@gogarufi gogarufi Jun 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be a dealbreaker for conditional importing? :)

// there might be a runtime delay before Sentry handler is used to handle errors.
export const handleError = optionalHandleErrorWithSentry;
22 changes: 18 additions & 4 deletions frontend/src/hooks.server.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
import * as Sentry from '@sentry/sveltekit';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that on the server side we benefit from conditional dynamic importing of Sentry.

import { staticSettings } from '@openvaa/app-shared';
import { sequence } from '@sveltejs/kit/hooks';
import { API_ROOT } from '$lib/api/adapters/apiRoute/apiRoutes';
import { defaultLocale, loadTranslations, locales } from '$lib/i18n';
import { matchLocale, parseAcceptedLanguages } from '$lib/i18n/utils';
import { constants } from '$lib/utils/constants';
import { logDebugError } from '$lib/utils/logger';
import type { Handle, HandleServerError } from '@sveltejs/kit';

if (staticSettings.analytics.sentryErrorReporting) {
Sentry.init({
dsn: constants.PUBLIC_FRONTEND_SENTRY_DSN,
tracesSampleRate: 1
});
}
// Handle and handleError based on sveltekit-i18n examples: https://github.com/sveltekit-i18n/lib/blob/master/examples/locale-router-advanced/src/hooks.server.js

/** Set to `true` to show debug log in console */
const DEBUG = false;

export const handle: Handle = (async ({ event, resolve }) => {
const optionalSetryHandle: Handle = staticSettings.analytics.sentryErrorReporting ? Sentry.sentryHandle() : ({ event, resolve }) => {
return resolve(event);
};

export const handle: Handle = sequence(optionalSetryHandle, (async ({ event, resolve }) => {
const { params, route, url, request, isDataRequest } = event;
const { pathname, search } = url;
const requestedLocale = params.lang;
Expand Down Expand Up @@ -111,17 +125,17 @@ export const handle: Handle = (async ({ event, resolve }) => {
transformPageChunk: ({ html }) => html.replace('%lang%', `${servedLocale}`)
}
);
}) satisfies Handle;
}) satisfies Handle);

export const handleError = (async ({ error, event }) => {
export const handleError = staticSettings.analytics.sentryErrorReporting ? Sentry.handleErrorWithSentry((async ({ error, event }) => {
const { locals } = event;
const currentLocale = locals?.currentLocale;
logDebugError('handleError', error);
if (currentLocale) await loadTranslations(currentLocale, 'error');
return {
message: '500'
};
}) satisfies HandleServerError;
}) satisfies HandleServerError) : () => {};

/** Show debug message if `DEBUG` is `true` */
function debug(message: unknown, error?: unknown) {
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/lib/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ export const constants: Record<string, string> = {
PUBLIC_SERVER_BACKEND_URL: env.PUBLIC_SERVER_BACKEND_URL,
PUBLIC_IDENTITY_PROVIDER_CLIENT_ID: env.PUBLIC_IDENTITY_PROVIDER_CLIENT_ID,
PUBLIC_IDENTITY_PROVIDER_AUTHORIZATION_ENDPOINT: env.PUBLIC_IDENTITY_PROVIDER_AUTHORIZATION_ENDPOINT,
PUBLIC_DEBUG: env.PUBLIC_DEBUG
PUBLIC_DEBUG: env.PUBLIC_DEBUG,
PUBLIC_FRONTEND_SENTRY_DSN: env.PUBLIC_FRONTEND_SENTRY_DSN
};
15 changes: 14 additions & 1 deletion frontend/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { sentrySvelteKit } from '@sentry/sveltekit';
import { staticSettings } from '@openvaa/app-shared';
import { sveltekit } from '@sveltejs/kit/vite';
import viteTsConfigPaths from 'vite-tsconfig-paths';
import type { UserConfig } from 'vite';
Expand All @@ -6,7 +8,18 @@ const config: UserConfig = {
resolve: {
preserveSymlinks: true
},
plugins: [sveltekit(), viteTsConfigPaths()],

plugins: [
staticSettings.analytics.sentryErrorReporting && sentrySvelteKit({
sourceMapsUploadOptions: {
org: 'openvaa',
project: 'openvaa-frontend'
}
}),
sveltekit(),
viteTsConfigPaths()
],

server: {
port: Number(process.env.FRONTEND_PORT)
}
Expand Down
3 changes: 2 additions & 1 deletion packages/app-shared/src/settings/staticSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ export const staticSettings: StaticSettings = {
}
],
analytics: {
trackEvents: false
trackEvents: false,
sentryErrorReporting: false
},
preRegistration: {
enabled: false
Expand Down
4 changes: 4 additions & 0 deletions packages/app-shared/src/settings/staticSettings.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ export type StaticSettings = {
* Whether to collect anonymous usage data about all UI actions, including answers to statements. This will only have an effect if the analytics platform is defined.
*/
readonly trackEvents: boolean;
/**
* Whether to report application errors to Sentry.
*/
readonly sentryErrorReporting: boolean;
};
/**
* Settings related to Candidate App pre-registration.
Expand Down
Loading
Loading