diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index a2e3832..1acb6b1 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -29,8 +29,9 @@ } ], "security": { - "csp": null - } + "csp": "default-src 'self' ipc: http://ipc.localhost; connect-src 'self' ipc: http://ipc.localhost https://hackatime.hackclub.com https://pub-d35fbe65a5b5426bb6d62ff02a8c7d03.r2.dev wss://*.ingest.us.sentry.io https://us.i.posthog.com https://fonts.googleapis.com https://fonts.gstatic.com; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: https:; font-src 'self' data: https://fonts.gstatic.com; worker-src 'self' blob:;" + }, + "withGlobalTauri": true }, "bundle": { "active": true, diff --git a/src/App.vue b/src/App.vue index 700d859..9c06717 100644 --- a/src/App.vue +++ b/src/App.vue @@ -28,6 +28,26 @@ if (!(window as any).__hackatimeConsoleWrapped) { console.info('[CONSOLE] Console wrapper initialized - logs will be captured'); } +if (!(window as any).__hackatimeErrorHandlerSet) { + (window as any).__hackatimeErrorHandlerSet = true; + + window.addEventListener('unhandledrejection', (event) => { + console.error('[UNHANDLED REJECTION]', event.reason); + + const errorMessage = event.reason?.message || String(event.reason); + if (errorMessage.includes('callbackId') || + errorMessage.includes('IPC') || + errorMessage.includes('Load failed')) { + event.preventDefault(); + console.warn('[IPC] Suppressed IPC-related error that was already logged'); + } + }); + + window.addEventListener('error', (event) => { + console.error('[UNHANDLED ERROR]', event.error); + }); +} + interface AuthState { is_authenticated: boolean; access_token: string | null; diff --git a/src/main.ts b/src/main.ts index a3e0c8f..bcac316 100644 --- a/src/main.ts +++ b/src/main.ts @@ -38,7 +38,55 @@ Sentry.init({ profilesSampleRate: __SENTRY_ENVIRONMENT__ === 'production' ? 0.1 : 1.0, - enableLogs: true + enableLogs: true, + + beforeSend(event, hint) { + const error = hint.originalException as Error | undefined + const errorMessage = (error as any)?.message || error?.toString() || '' + + if ( + errorMessage.includes('callbackId') || + errorMessage.includes('IPC custom protocol failed') || + errorMessage.includes('Tauri will now use the postMessage interface') || + errorMessage.includes('Load failed') && errorMessage.includes('localhost') || + errorMessage.includes('ipc://localhost') || + errorMessage.includes('project.editors.some') || + errorMessage.includes('project.total_heartbeats.toLocaleString') || + errorMessage.includes('el.__vnode') || + errorMessage.includes('patchElement') && errorMessage.includes('null') || + event.exception?.values?.some(value => + value.value?.includes('callbackId') || + value.value?.includes('[callbackId, data]') || + value.value?.includes('project.editors.some') || + value.value?.includes('project.total_heartbeats') || + value.value?.includes('el.__vnode') || + (value.value?.includes('patchElement') && value.value?.includes('null')) + ) + ) { + console.log('[SENTRY] Filtered out known benign error:', errorMessage) + return null + } + + return event + }, + + ignoreErrors: [ + 'IPC custom protocol failed', + 'callbackId', + 'Load failed', + 'ipc://localhost', + 'undefined is not an object (evaluating \'[callbackId, data]\')', + 'undefined is not an object (evaluating \'project.editors.some\')', + 'undefined is not an object (evaluating \'project.total_heartbeats.toLocaleString\')', + 'null is not an object (evaluating \'el.__vnode = n2\')', + 'null is not an object (evaluating \'el.__vnode\')', + /IPC custom protocol/, + /callbackId/, + /project\.editors\.some/, + /project\.total_heartbeats/, + /el\.__vnode/, + /patchElement/, + ] }) app.mount('#app') diff --git a/src/views/Projects.vue b/src/views/Projects.vue index 8220137..4905327 100644 --- a/src/views/Projects.vue +++ b/src/views/Projects.vue @@ -128,26 +128,26 @@