Skip to content
Merged
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
1 change: 1 addition & 0 deletions .github/workflows/release-desktop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ jobs:
GH_CLIENT_ID: ${{ secrets.GH_CLIENT_ID }}
NEXT_PUBLIC_WEB_URL: ${{ secrets.NEXT_PUBLIC_WEB_URL }}
NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL }}
SENTRY_DSN_DESKTOP: ${{ secrets.SENTRY_DSN_DESKTOP }}
run: bun run compile:app

# Build the Electron app for macOS
Expand Down
10 changes: 10 additions & 0 deletions apps/desktop/electron.vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ export default defineConfig({
process.env.GOOGLE_CLIENT_ID,
),
"process.env.GH_CLIENT_ID": JSON.stringify(process.env.GH_CLIENT_ID),
"process.env.SENTRY_DSN_DESKTOP": JSON.stringify(
process.env.SENTRY_DSN_DESKTOP,
),
},

build: {
Expand All @@ -103,6 +106,7 @@ export default defineConfig({
"electron",
"better-sqlite3", // Native module - must stay external
"node-pty", // Native module - must stay external
/^@sentry\/electron/,
],
},
},
Expand Down Expand Up @@ -161,6 +165,9 @@ export default defineConfig({
"import.meta.env.NEXT_PUBLIC_POSTHOG_HOST": JSON.stringify(
process.env.NEXT_PUBLIC_POSTHOG_HOST,
),
"import.meta.env.SENTRY_DSN_DESKTOP": JSON.stringify(
process.env.SENTRY_DSN_DESKTOP,
),
},

server: {
Expand Down Expand Up @@ -205,6 +212,9 @@ export default defineConfig({
input: {
index: resolve("src/renderer/index.html"),
},

// Externalize Sentry - it uses IPC to communicate with main process
external: [/^@sentry\/electron/],
},
},
},
Expand Down
1 change: 1 addition & 0 deletions apps/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"@monaco-editor/react": "^4.7.0",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-label": "^2.1.8",
"@sentry/electron": "^7.5.0",
"@superset/local-db": "workspace:*",
"@superset/shared": "workspace:*",
"@superset/trpc": "workspace:*",
Expand Down
34 changes: 33 additions & 1 deletion apps/desktop/src/lib/trpc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,38 @@ const t = initTRPC.create({
isServer: true,
});

/**
* Middleware that captures errors with Sentry
*/
const sentryMiddleware = t.middleware(async ({ next, path, type }) => {
const result = await next();

if (!result.ok) {
try {
const Sentry = await import("@sentry/electron/main");
const error = result.error;

// Get the original error if it's wrapped in a TRPCError
const originalError = error.cause instanceof Error ? error.cause : error;

Sentry.captureException(originalError, {
tags: {
trpc_path: path,
trpc_type: type,
trpc_code: error.code,
},
extra: {
trpc_message: error.message,
},
});
} catch {
// Sentry not available
}
}

return result;
});

export const router = t.router;
export const publicProcedure = t.procedure;
export const publicProcedure = t.procedure.use(sentryMiddleware);
export const trpc = createTRPCReact<AppRouter>();
2 changes: 2 additions & 0 deletions apps/desktop/src/main/env.main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const env = createEnv({
GH_CLIENT_ID: z.string().min(1),
NEXT_PUBLIC_POSTHOG_KEY: z.string().optional(),
NEXT_PUBLIC_POSTHOG_HOST: z.string().default("https://us.i.posthog.com"),
SENTRY_DSN_DESKTOP: z.string().optional(),
},

runtimeEnv: {
Expand All @@ -33,6 +34,7 @@ export const env = createEnv({
GH_CLIENT_ID: process.env.GH_CLIENT_ID,
NEXT_PUBLIC_POSTHOG_KEY: process.env.NEXT_PUBLIC_POSTHOG_KEY,
NEXT_PUBLIC_POSTHOG_HOST: process.env.NEXT_PUBLIC_POSTHOG_HOST,
SENTRY_DSN_DESKTOP: process.env.SENTRY_DSN_DESKTOP,
},
emptyStringAsUndefined: true,
skipValidation: !!process.env.SKIP_ENV_VALIDATION,
Expand Down
4 changes: 4 additions & 0 deletions apps/desktop/src/main/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import { initSentry } from "./lib/sentry";

initSentry();

import path from "node:path";
import { app, BrowserWindow } from "electron";
import { makeAppSetup } from "lib/electron-app/factories/app/setup";
Expand Down
28 changes: 28 additions & 0 deletions apps/desktop/src/main/lib/sentry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { env } from "../env.main";

let sentryInitialized = false;

export async function initSentry(): Promise<void> {
if (sentryInitialized) return;

if (!env.SENTRY_DSN_DESKTOP) {
return;
}

try {
// Dynamic import to avoid bundler issues
const Sentry = await import("@sentry/electron/main");

Sentry.init({
dsn: env.SENTRY_DSN_DESKTOP,
environment: env.NODE_ENV,
tracesSampleRate: env.NODE_ENV === "development" ? 1.0 : 0.1,
sendDefaultPii: false,
});

sentryInitialized = true;
console.log("[sentry] Initialized in main process");
} catch (error) {
console.error("[sentry] Failed to initialize:", error);
}
}
2 changes: 2 additions & 0 deletions apps/desktop/src/renderer/env.renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const envSchema = z.object({
NEXT_PUBLIC_WEB_URL: z.url().default("https://app.superset.sh"),
NEXT_PUBLIC_POSTHOG_KEY: z.string().optional(),
NEXT_PUBLIC_POSTHOG_HOST: z.string().default("https://us.i.posthog.com"),
SENTRY_DSN_DESKTOP: z.string().optional(),
});

/**
Expand All @@ -38,6 +39,7 @@ const rawEnv = {
NEXT_PUBLIC_POSTHOG_HOST: import.meta.env.NEXT_PUBLIC_POSTHOG_HOST as
| string
| undefined,
SENTRY_DSN_DESKTOP: import.meta.env.SENTRY_DSN_DESKTOP as string | undefined,
};

export const env = process.env.SKIP_ENV_VALIDATION
Expand Down
4 changes: 2 additions & 2 deletions apps/desktop/src/renderer/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
- default-src 'self': Only allow resources from same origin
- script-src 'self' 'wasm-unsafe-eval' https://*.posthog.com: Allow scripts from same origin + WebAssembly (for xterm ImageAddon) + PostHog
- style-src 'self' 'unsafe-inline': Allow styles from same origin + inline (needed for CSS-in-JS)
- connect-src 'self' ws: wss: https://*.posthog.com: Allow WebSocket connections for HMR + PostHog analytics
- connect-src 'self' ws: wss: https://*.posthog.com https://*.sentry.io sentry-ipc:: Allow WebSocket connections for HMR + PostHog analytics + Sentry error reporting
- img-src 'self' data: https://*.public.blob.vercel-storage.com: Allow images from same origin + data URIs + Vercel blob storage (avatars)
- font-src 'self': Allow fonts from same origin
-->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'wasm-unsafe-eval' https://*.posthog.com; style-src 'self' 'unsafe-inline'; connect-src 'self' ws: wss: https://*.posthog.com; img-src 'self' data: https://*.public.blob.vercel-storage.com; font-src 'self';" />
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'wasm-unsafe-eval' https://*.posthog.com; style-src 'self' 'unsafe-inline'; connect-src 'self' ws: wss: https://*.posthog.com https://*.sentry.io sentry-ipc:; img-src 'self' data: https://*.public.blob.vercel-storage.com; font-src 'self';" />
</head>

<body>
Expand Down
4 changes: 4 additions & 0 deletions apps/desktop/src/renderer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import { initSentry } from "./lib/sentry";

initSentry();

import React from "react";
import ReactDom from "react-dom/client";

Expand Down
29 changes: 29 additions & 0 deletions apps/desktop/src/renderer/lib/sentry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { env } from "../env.renderer";

let sentryInitialized = false;

export async function initSentry(): Promise<void> {
if (sentryInitialized) return;

if (!env.SENTRY_DSN_DESKTOP) {
return;
}

try {
// Dynamic import to avoid bundler issues
const Sentry = await import("@sentry/electron/renderer");

Sentry.init({
dsn: env.SENTRY_DSN_DESKTOP,
environment: env.NODE_ENV,
tracesSampleRate: env.NODE_ENV === "development" ? 1.0 : 0.1,
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
});

sentryInitialized = true;
console.log("[sentry] Initialized in renderer process");
} catch (error) {
console.error("[sentry] Failed to initialize in renderer:", error);
}
}
2 changes: 1 addition & 1 deletion biome.jsonc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"$schema": "https://biomejs.dev/schemas/2.3.8/schema.json",
"$schema": "https://biomejs.dev/schemas/2.3.10/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
Expand Down
Loading