feat: connect sentry to dash and api
This commit is contained in:
@@ -40,7 +40,12 @@ CLOUDFLARE_ACCESS_KEY=""
|
||||
CLOUDFLARE_ACCOUNT_ID=""
|
||||
CLOUDFLARE_BUCKET_DOMAIN=""
|
||||
CLOUDFLARE_SECRET_ACCESS_KEY=""
|
||||
SENTRY_DNS=""
|
||||
|
||||
# Sentry (server-side error tracking). Leave SENTRY_DSN unset to disable.
|
||||
# Skipped automatically in dev (NODE_ENV !== 'production') and self-hosted (PUBLIC_IS_SELFHOSTED=true).
|
||||
SENTRY_DSN=""
|
||||
SENTRY_ENVIRONMENT="production"
|
||||
SENTRY_TRACES_SAMPLE_RATE="0"
|
||||
|
||||
# BullMQ dashboard (/admin/queues) ───────────────────────────────────────────
|
||||
# In dev the dashboard is open. In production it's hidden unless ONE of the
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
"@hono/standard-validator": "^0.1.5",
|
||||
"@hono/zod-validator": "^0.7.6",
|
||||
"@scalar/hono-api-reference": "^0.9.18",
|
||||
"@sentry/profiling-node": "^7.110.1",
|
||||
"@sentry/node": "^10.53.1",
|
||||
"ai": "^6.0.158",
|
||||
"axios": "^1.4.0",
|
||||
"css-tree": "^3.2.1",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'dotenv/config';
|
||||
|
||||
import * as Sentry from '@sentry/node';
|
||||
import { consumeHandoffPayload, storeHandoffPayload } from '@api/utils/redis/oauth-handoff';
|
||||
import { isPublicCorsPath, sessionCors } from '@api/middlewares/cors';
|
||||
import { randomBytes } from 'crypto';
|
||||
@@ -78,6 +79,13 @@ export const app = new Hono()
|
||||
c.set('user', session.user);
|
||||
c.set('session', session.session);
|
||||
c.set('orgRoles', (session as { orgRoles?: Record<string, number> }).orgRoles ?? {});
|
||||
|
||||
Sentry.setUser({
|
||||
id: session.user.id,
|
||||
...(session.user.email && { email: session.user.email }),
|
||||
...(session.user.name && { username: session.user.name })
|
||||
});
|
||||
|
||||
await next();
|
||||
})
|
||||
|
||||
@@ -206,6 +214,7 @@ export const app = new Hono()
|
||||
|
||||
// Error handling
|
||||
.onError((err, c) => {
|
||||
Sentry.captureException(err);
|
||||
console.error('Error:', err);
|
||||
return c.json({ error: 'Internal Server Error' }, 500);
|
||||
});
|
||||
|
||||
@@ -78,6 +78,10 @@ const envSchema = z.object({
|
||||
|
||||
return Number.isFinite(parsed) && parsed >= 1 ? Math.trunc(parsed) : 15;
|
||||
}),
|
||||
/** Sentry error tracking (server-side). Leave SENTRY_DSN unset to disable. */
|
||||
SENTRY_DSN: z.string().optional(),
|
||||
SENTRY_ENVIRONMENT: z.string().optional(),
|
||||
SENTRY_TRACES_SAMPLE_RATE: z.string().optional(),
|
||||
/** Max fetch_documentation_url calls per org per UTC day (default 500). */
|
||||
AGENT_MAX_FETCHES_PER_ORG_PER_DAY: z
|
||||
.string()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'dotenv/config';
|
||||
import './instrument';
|
||||
|
||||
import { API_PORT } from '@api/constants';
|
||||
import { app } from '@api/app';
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Must be imported as the FIRST thing in `apps/api/src/index.ts`. The Sentry
|
||||
* Node SDK relies on auto-instrumentation that hooks into Node's `require`
|
||||
* resolution and module loaders; any code that runs before this file initializes
|
||||
* Sentry will not be traced.
|
||||
*/
|
||||
import * as Sentry from '@sentry/node';
|
||||
|
||||
const dsn = process.env.SENTRY_DSN?.trim();
|
||||
const isProd = process.env.NODE_ENV === 'production';
|
||||
const isSelfHosted = process.env.PUBLIC_IS_SELFHOSTED === 'true';
|
||||
|
||||
if (dsn && isProd && !isSelfHosted) {
|
||||
Sentry.init({
|
||||
dsn,
|
||||
environment: process.env.SENTRY_ENVIRONMENT?.trim() || 'production',
|
||||
tracesSampleRate: Number(process.env.SENTRY_TRACES_SAMPLE_RATE ?? 0),
|
||||
sendDefaultPii: false
|
||||
});
|
||||
}
|
||||
@@ -45,3 +45,10 @@ PRIVATE_APP_SUBDOMAINS=app
|
||||
POLAR_ACCESS_TOKEN=
|
||||
POLAR_WEBHOOK_SECRET=
|
||||
POLAR_TOKEN_PACK_PRODUCT_ID=
|
||||
|
||||
# Sentry (client-side error tracking). Leave PUBLIC_SENTRY_DSN unset to disable.
|
||||
PUBLIC_SENTRY_DSN=
|
||||
PUBLIC_SENTRY_ENVIRONMENT=production
|
||||
PUBLIC_SENTRY_TRACES_SAMPLE_RATE=0
|
||||
PUBLIC_SENTRY_REPLAYS_SESSION_SAMPLE_RATE=0
|
||||
PUBLIC_SENTRY_REPLAYS_ON_ERROR_SAMPLE_RATE=1
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
"@lucide/svelte": "^0.545.0",
|
||||
"@polar-sh/sdk": "^0.47.1",
|
||||
"@polar-sh/sveltekit": "^0.7.5",
|
||||
"@sentry/sveltekit": "^10.53.1",
|
||||
"@sveltejs/adapter-auto": "^3.0.0",
|
||||
"@sveltejs/adapter-node": "^2.0.0",
|
||||
"@sveltejs/adapter-vercel": "^4.0.0",
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import * as Sentry from '@sentry/sveltekit';
|
||||
import { dev } from '$app/environment';
|
||||
import { env } from '$env/dynamic/public';
|
||||
import { handleErrorWithSentry } from '@sentry/sveltekit';
|
||||
|
||||
const dsn = env.PUBLIC_SENTRY_DSN?.trim();
|
||||
const isSelfHosted = env.PUBLIC_IS_SELFHOSTED === 'true';
|
||||
|
||||
if (dsn && !dev && !isSelfHosted) {
|
||||
Sentry.init({
|
||||
dsn,
|
||||
environment: env.PUBLIC_SENTRY_ENVIRONMENT?.trim() || 'production',
|
||||
tracesSampleRate: Number(env.PUBLIC_SENTRY_TRACES_SAMPLE_RATE ?? 0),
|
||||
replaysSessionSampleRate: Number(env.PUBLIC_SENTRY_REPLAYS_SESSION_SAMPLE_RATE ?? 0),
|
||||
replaysOnErrorSampleRate: Number(env.PUBLIC_SENTRY_REPLAYS_ON_ERROR_SAMPLE_RATE ?? 1),
|
||||
integrations: [Sentry.replayIntegration()]
|
||||
});
|
||||
}
|
||||
|
||||
export const handleError = handleErrorWithSentry();
|
||||
@@ -1,6 +1,5 @@
|
||||
import { PUBLIC_IS_SELFHOSTED } from '$env/static/public';
|
||||
import { initPosthog, type PosthogBootstrapUser } from '$lib/utils/services/posthog';
|
||||
import { initSentry } from '$lib/utils/services/sentry';
|
||||
import { initUmami } from '$lib/utils/services/umami';
|
||||
import { licenseApi } from '$features/license/api/license.svelte';
|
||||
|
||||
@@ -10,7 +9,6 @@ export function setupAnalytics(user?: PosthogBootstrapUser) {
|
||||
if (isInitialized) return;
|
||||
isInitialized = true;
|
||||
|
||||
initSentry();
|
||||
initPosthog(user);
|
||||
initUmami();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { appInitApi } from '$features/app/init.svelte';
|
||||
import { authClient } from '$lib/utils/services/auth/client';
|
||||
import { capturePosthogEvent } from '$lib/utils/services/posthog';
|
||||
import { clearSentryUser } from '$lib/utils/services/sentry';
|
||||
import { goto } from '$app/navigation';
|
||||
import posthog from 'posthog-js';
|
||||
import { resolve } from '$app/paths';
|
||||
@@ -16,6 +17,7 @@ export async function logout(redirect = true) {
|
||||
|
||||
capturePosthogEvent('user_logged_out');
|
||||
posthog.reset();
|
||||
clearSentryUser();
|
||||
|
||||
if (redirect) {
|
||||
goto(resolve('/login', {}));
|
||||
|
||||
@@ -289,8 +289,6 @@ export function getApiHeaders(
|
||||
): { headers: { cookie: string; 'cio-org-id'?: string } } {
|
||||
const cioCookies = getCioCookieString(cookies);
|
||||
|
||||
console.log('cioCookies', cioCookies);
|
||||
console.log('cookies', cookies.getAll());
|
||||
const headers: { cookie: string; 'cio-org-id'?: string } = {
|
||||
cookie: cioCookies || ''
|
||||
};
|
||||
|
||||
@@ -1,26 +1,30 @@
|
||||
import * as Sentry from '@sentry/sveltekit';
|
||||
import { dev } from '$app/environment';
|
||||
// import * as Sentry from '@sentry/browser';
|
||||
// import { CaptureConsole } from '@sentry/integrations';
|
||||
import { env } from '$env/dynamic/public';
|
||||
|
||||
export const initSentry = (): void => {
|
||||
if (dev) return;
|
||||
const isEnabled = () =>
|
||||
Boolean(env.PUBLIC_SENTRY_DSN?.trim()) && !dev && env.PUBLIC_IS_SELFHOSTED !== 'true';
|
||||
|
||||
// Sentry.init({
|
||||
// dsn: 'https://c966f7e8cb1d4306be20b26bb4f0cc96@o476906.ingest.sentry.io/5999999',
|
||||
// integrations: [
|
||||
// new CaptureConsole({
|
||||
// levels: ['error']
|
||||
// }),
|
||||
// new Sentry.Replay()
|
||||
// ],
|
||||
// environment: 'production',
|
||||
// replaysSessionSampleRate: 0.5,
|
||||
// replaysOnErrorSampleRate: 1.0
|
||||
// });
|
||||
export type SentryUser = {
|
||||
id: string;
|
||||
email?: string | null;
|
||||
username?: string | null;
|
||||
fullname?: string | null;
|
||||
};
|
||||
|
||||
export const setSentryUser = (user: Record<string, unknown>): void => {
|
||||
if (dev) return;
|
||||
export const setSentryUser = (user: SentryUser): void => {
|
||||
if (!isEnabled()) return;
|
||||
|
||||
// Sentry.setUser(user);
|
||||
Sentry.setUser({
|
||||
id: user.id,
|
||||
...(user.email && { email: user.email }),
|
||||
...(user.username && { username: user.username }),
|
||||
...(user.fullname && { fullname: user.fullname })
|
||||
});
|
||||
};
|
||||
|
||||
export const clearSentryUser = (): void => {
|
||||
if (!isEnabled()) return;
|
||||
|
||||
Sentry.setUser(null);
|
||||
};
|
||||
|
||||
Generated
+1040
-106
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user