From 6d67f88582cbacfdab5a359b10bf53d46d710995 Mon Sep 17 00:00:00 2001 From: FTCHD <144691102+FTCHD@users.noreply.github.com> Date: Sun, 31 Aug 2025 01:44:04 +0200 Subject: [PATCH] update browser sentry Signed-off-by: FTCHD <144691102+FTCHD@users.noreply.github.com> --- lib/fs.ts | 2 + lib/menu.ts | 30 ++++++---- lib/rclone/api.ts | 6 ++ lib/rclone/init.ts | 7 +++ lib/tray.ts | 2 + main.ts | 46 +++++++++++---- package-lock.json | 102 ++++++++++++++++++++++++++++++++++ package.json | 2 + src-tauri/src/lib.rs | 1 + src/components/PathFinder.tsx | 3 + src/pages/Settings.tsx | 4 ++ 11 files changed, 181 insertions(+), 24 deletions(-) diff --git a/lib/fs.ts b/lib/fs.ts index 8418aff..b3dbab2 100644 --- a/lib/fs.ts +++ b/lib/fs.ts @@ -1,3 +1,4 @@ +import * as Sentry from '@sentry/browser' import { readDir } from '@tauri-apps/plugin-fs' export async function isDirectoryEmpty(path: string): Promise { @@ -5,6 +6,7 @@ export async function isDirectoryEmpty(path: string): Promise { const entries = await readDir(path) return entries.length === 0 } catch (err) { + Sentry.captureException(err) console.error('Error checking directory:', err) return false } diff --git a/lib/menu.ts b/lib/menu.ts index 5167099..b86fb8b 100644 --- a/lib/menu.ts +++ b/lib/menu.ts @@ -1,3 +1,4 @@ +import * as Sentry from '@sentry/browser' import { Menu, MenuItem, PredefinedMenuItem, Submenu } from '@tauri-apps/api/menu' import { getCurrentWindow } from '@tauri-apps/api/window' import { ask, message, open } from '@tauri-apps/plugin-dialog' @@ -48,9 +49,10 @@ async function parseRemotes(remotes: string[]) { await message(`Successfully unmounted ${remote} from ${mountPoint}`, { title: 'Success', }) - } catch (err) { - console.error('Unmount operation failed:', err) - await message(`Failed to unmount ${remote}: ${err}`, { + } catch (error) { + Sentry.captureException(error) + console.error('Unmount operation failed:', error) + await message(`Failed to unmount ${remote}: ${error}`, { kind: 'error', title: 'Unmount Error', }) @@ -71,9 +73,10 @@ async function parseRemotes(remotes: string[]) { if (mountPoint) { try { await openPath(mountPoint) - } catch (err) { - console.error('Error opening path:', err) - await message(`Failed to open ${mountPoint} (${err})`, { + } catch (error) { + Sentry.captureException(error) + console.error('Error opening path:', error) + await message(`Failed to open ${mountPoint} (${error})`, { title: 'Open Error', kind: 'error', }) @@ -127,8 +130,8 @@ async function parseRemotes(remotes: string[]) { try { directoryExists = await exists(selectedPath) - } catch (err) { - console.error('Error checking if directory exists:', err) + } catch (error) { + console.error('Error checking if directory exists:', error) } console.log('directoryExists', directoryExists) @@ -157,6 +160,7 @@ async function parseRemotes(remotes: string[]) { try { await mkdir(selectedPath) } catch (error) { + Sentry.captureException(error) console.error('Error creating directory:', error) await message( 'Failed to create mount directory. Try creating it manually first.', @@ -206,10 +210,11 @@ async function parseRemotes(remotes: string[]) { } await rebuildTrayMenu() - } catch (err) { + } catch (error) { // await resetMainWindow() - console.error('Mount operation failed:', err) - await message(`Failed to mount ${remote}: ${err}`, { + Sentry.captureException(error) + console.error('Mount operation failed:', error) + await message(`Failed to mount ${remote}: ${error}`, { title: 'Mount Error', }) } finally { @@ -236,7 +241,8 @@ async function parseRemotes(remotes: string[]) { 'browse.html?url=' + encodeURIComponent(`http://localhost:5572/[${remote}:]/`), }) - } catch { + } catch (error) { + Sentry.captureException(error) await ask('Could not open browse window. Please try again.', { title: 'Error', kind: 'error', diff --git a/lib/rclone/api.ts b/lib/rclone/api.ts index 35bf6a7..de87af3 100644 --- a/lib/rclone/api.ts +++ b/lib/rclone/api.ts @@ -1,3 +1,4 @@ +import * as Sentry from '@sentry/browser' import { fetch } from '@tauri-apps/plugin-http' import { platform } from '@tauri-apps/plugin-os' import { useStore } from '../store' @@ -31,6 +32,7 @@ export async function getVersion() { headers: getAuthHeader(), }) .catch((e) => { + Sentry.captureException(e) console.log('error', e) throw e }) @@ -62,6 +64,7 @@ export async function listRemotes() { headers: getAuthHeader(), }) .catch((e) => { + Sentry.captureException(e) console.log('error', e) throw e }) @@ -382,6 +385,7 @@ export async function mountRemote({ }) .then((res) => res.json() as Promise<{ remotes: string[] } | Promise<{ error: string }>>) .catch((e) => { + Sentry.captureException(e) console.log('error', e) throw e }) @@ -409,6 +413,7 @@ export async function unmountRemote({ }) .then((res) => res.json()) .catch((e) => { + Sentry.captureException(e) console.log('error', e) throw e }) @@ -429,6 +434,7 @@ export async function unmountAllRemotes() { }) .then((res) => res.json()) .catch((e) => { + Sentry.captureException(e) console.log('error', e) throw e }) diff --git a/lib/rclone/init.ts b/lib/rclone/init.ts index cd5b45e..56d09c0 100644 --- a/lib/rclone/init.ts +++ b/lib/rclone/init.ts @@ -1,3 +1,4 @@ +import * as Sentry from '@sentry/browser' import { invoke } from '@tauri-apps/api/core' import { BaseDirectory, appLocalDataDir } from '@tauri-apps/api/path' import { tempDir } from '@tauri-apps/api/path' @@ -202,6 +203,7 @@ export async function provisionRclone() { }) console.log('[provisionRclone] tempDirExists', tempDirExists) } catch (error) { + Sentry.captureException(error) console.error('[provisionRclone] failed to check if rclone temp dir exists', error) } @@ -213,6 +215,7 @@ export async function provisionRclone() { }) console.log('[provisionRclone] removed rclone temp dir') } catch (error) { + Sentry.captureException(error) console.error('[provisionRclone] failed to remove rclone temp dir', error) await message('Failed to provision rclone.') return @@ -225,6 +228,7 @@ export async function provisionRclone() { }) console.log('[provisionRclone] created rclone temp dir') } catch (error) { + Sentry.captureException(error) console.error('[provisionRclone] failed to create rclone temp dir', error) await message('Failed to provision rclone.') return @@ -237,6 +241,7 @@ export async function provisionRclone() { await writeFile(zipPath, new Uint8Array(downloadedFile)) console.log('[provisionRclone] wrote zip file') } catch (error) { + Sentry.captureException(error) console.error('[provisionRclone] failed to write zip file', error) await message('Failed to provision rclone.') return @@ -249,6 +254,7 @@ export async function provisionRclone() { }) console.log('[provisionRclone] successfully unzipped file') } catch (error) { + Sentry.captureException(error) console.error('[provisionRclone] failed to unzip file', error) await message('Failed to provision rclone.') return @@ -270,6 +276,7 @@ export async function provisionRclone() { throw new Error('Could not find rclone binary in zip') } } catch (error) { + Sentry.captureException(error) console.error('[provisionRclone] failed to check if rclone binary exists', error) await message('Failed to provision rclone.') } diff --git a/lib/tray.ts b/lib/tray.ts index 3d55776..d91160e 100644 --- a/lib/tray.ts +++ b/lib/tray.ts @@ -1,3 +1,4 @@ +import * as Sentry from '@sentry/browser' import { Menu } from '@tauri-apps/api/menu' import { MenuItem } from '@tauri-apps/api/menu' import { resolveResource } from '@tauri-apps/api/path' @@ -65,6 +66,7 @@ export async function initTray(): Promise { action: onTrayAction, }) } catch (error) { + Sentry.captureException(error) console.error('[initTray] failed to create tray') console.error(error) } diff --git a/main.ts b/main.ts index ac09640..e03edb3 100644 --- a/main.ts +++ b/main.ts @@ -1,3 +1,4 @@ +import * as Sentry from '@sentry/browser' import { getVersion as getUiVersion } from '@tauri-apps/api/app' import { getCurrentWindow } from '@tauri-apps/api/window' import { ask, message } from '@tauri-apps/plugin-dialog' @@ -6,6 +7,7 @@ import { platform } from '@tauri-apps/plugin-os' import { exit, relaunch } from '@tauri-apps/plugin-process' import { check } from '@tauri-apps/plugin-updater' import { CronExpressionParser } from 'cron-parser' +import { defaultOptions } from 'tauri-plugin-sentry-api' import { validateLicense } from './lib/license' import notify from './lib/notify' import { @@ -22,25 +24,40 @@ import { usePersistedStore, useStore } from './lib/store' import { initLoadingTray, initTray, rebuildTrayMenu } from './lib/tray' import type { ScheduledTask } from './types/task' +try { + Sentry.init({ + ...defaultOptions, + sendDefaultPii: false, + }) +} catch { + console.error('Error initializing Sentry') +} + // forward console logs in webviews to the tauri logger, so they show up in terminal function forwardConsole( fnName: 'log' | 'debug' | 'info' | 'warn' | 'error', logger: (message: string) => Promise ) { - const original = console[fnName] - console[fnName] = (message, ...args) => { - original(message, ...args) - logger( - `${message} ${args?.map((arg) => (typeof arg === 'string' ? arg : JSON.stringify(arg))).join(' ')}` - ) - } + try { + const original = console[fnName] + console[fnName] = (message, ...args) => { + original(message, ...args) + logger( + `${message} ${args?.map((arg) => (typeof arg === 'string' ? arg : JSON.stringify(arg))).join(' ')}` + ) + } + } catch {} } -forwardConsole('log', trace) -forwardConsole('debug', debug) -forwardConsole('info', info) -forwardConsole('warn', warn) -forwardConsole('error', error) +try { + forwardConsole('log', trace) + forwardConsole('debug', debug) + forwardConsole('info', info) + forwardConsole('warn', warn) + forwardConsole('error', error) +} catch (error) { + console.error('Could not enable console logs', error) +} async function waitForHydration() { console.log('waiting for store hydration') @@ -125,6 +142,7 @@ async function startRclone() { // ':5572', ]) } catch (error) { + Sentry.captureException(error) await ask(error.message || 'Failed to start rclone, please try again later.', { title: 'Error', kind: 'error', @@ -149,6 +167,7 @@ async function startRclone() { console.log('close', event) if (event.code === 1 || event.code === 143) { + Sentry.captureException(new Error('Rclone has crashed')) await message('Rclone has crashed', { title: 'Error', kind: 'error', @@ -215,6 +234,7 @@ async function startupMounts() { }) } catch (error) { console.error('Error mounting remote:', error) + Sentry.captureException(error) await message(`Failed to mount ${remote} on startup.`, { title: 'Automount Error', kind: 'error', @@ -285,6 +305,7 @@ async function resumeTasks() { } } catch (error) { console.error('Error scheduling task:', error) + Sentry.captureException(error) } } @@ -370,6 +391,7 @@ async function handleTask(task: ScheduledTask) { break } } catch (err) { + Sentry.captureException(err) console.error('Failed to start task:', err) usePersistedStore.getState().updateScheduledTask(task.id, { isRunning: false, diff --git a/package-lock.json b/package-lock.json index e6c9bde..73afb0c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.7.1", "dependencies": { "@heroui/react": "^2.7.11", + "@sentry/browser": "^10.8.0", "@tauri-apps/api": "~2.7.0", "@tauri-apps/plugin-autostart": "^2.5.0", "@tauri-apps/plugin-clipboard-manager": "^2.3.0", @@ -33,6 +34,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.28.1", + "tauri-plugin-sentry-api": "^0.4.1", "use-broadcast-ts": "^2.0.1", "use-debounce": "^10.0.5", "zustand": "^5.0.7" @@ -5092,6 +5094,81 @@ "win32" ] }, + "node_modules/@sentry-internal/browser-utils": { + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-10.8.0.tgz", + "integrity": "sha512-FaQX9eefc8sh3h3ZQy16U73KiH0xgDldXnrFiWK6OeWg8X4bJpnYbLqEi96LgHiQhjnnz+UQP1GDzH5oFuu5fA==", + "license": "MIT", + "dependencies": { + "@sentry/core": "10.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry-internal/feedback": { + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-10.8.0.tgz", + "integrity": "sha512-n7SqgFQItq4QSPG7bCjcZcIwK6AatKnnmSDJ/i6e8jXNIyLwkEuY2NyvTXACxVdO/kafGD5VmrwnTo3Ekc1AMg==", + "license": "MIT", + "dependencies": { + "@sentry/core": "10.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry-internal/replay": { + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-10.8.0.tgz", + "integrity": "sha512-9+qDEoEjv4VopLuOzK1zM4LcvcUsvB5N0iJ+FRCM3XzzOCbebJOniXTQbt5HflJc3XLnQNKFdKfTfgj8M/0RKQ==", + "license": "MIT", + "dependencies": { + "@sentry-internal/browser-utils": "10.8.0", + "@sentry/core": "10.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry-internal/replay-canvas": { + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-10.8.0.tgz", + "integrity": "sha512-jC4OOwiNgrlIPeXIPMLkaW53BSS1do+toYHoWzzO5AXGpN6jRhanoSj36FpVuH2N3kFnxKVfVxrwh8L+/3vFWg==", + "license": "MIT", + "dependencies": { + "@sentry-internal/replay": "10.8.0", + "@sentry/core": "10.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry/browser": { + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-10.8.0.tgz", + "integrity": "sha512-2J7HST8/ixCaboq17yFn/j/OEokXSXoCBMXRrFx4FKJggKWZ90e2Iau5mP/IPPhrW+W9zCptCgNMY0167wS4qA==", + "license": "MIT", + "dependencies": { + "@sentry-internal/browser-utils": "10.8.0", + "@sentry-internal/feedback": "10.8.0", + "@sentry-internal/replay": "10.8.0", + "@sentry-internal/replay-canvas": "10.8.0", + "@sentry/core": "10.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry/core": { + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.8.0.tgz", + "integrity": "sha512-scYzM/UOItu4PjEq6CpHLdArpXjIS0laHYxE4YjkIbYIH6VMcXGQbD/FSBClsnCr1wXRnlXfXBzj0hrQAFyw+Q==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@swc/helpers": { "version": "0.5.17", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", @@ -7316,6 +7393,31 @@ "node": ">=14.0.0" } }, + "node_modules/tauri-plugin-sentry-api": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/tauri-plugin-sentry-api/-/tauri-plugin-sentry-api-0.4.1.tgz", + "integrity": "sha512-KgfL7JhN51TOA3e0UYDu6UkdwgH+wC1/RC3qIHJOZIAsvyTfde4uifUFB11GfgXfHgY33xUkIGgqWKkaK5lg6Q==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "@tauri-apps/api": "2.0.0-beta.8", + "tslib": "^2.6.0" + } + }, + "node_modules/tauri-plugin-sentry-api/node_modules/@tauri-apps/api": { + "version": "2.0.0-beta.8", + "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.0.0-beta.8.tgz", + "integrity": "sha512-fN5u+9HsSfhRaXGOdD2kPGbqDgyX+nkm1XF/4d/LNuM4WiNfvHjjRAqWQYBhQsg1aF9nsTPmSW+tmy+Yn5T5+A==", + "license": "Apache-2.0 OR MIT", + "engines": { + "node": ">= 18", + "npm": ">= 6.6.0", + "yarn": ">= 1.19.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/tauri" + } + }, "node_modules/terser": { "version": "5.37.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz", diff --git a/package.json b/package.json index f3abebd..4207b1d 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ }, "dependencies": { "@heroui/react": "^2.7.11", + "@sentry/browser": "^10.8.0", "@tauri-apps/api": "~2.7.0", "@tauri-apps/plugin-autostart": "^2.5.0", "@tauri-apps/plugin-clipboard-manager": "^2.3.0", @@ -44,6 +45,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.28.1", + "tauri-plugin-sentry-api": "^0.4.1", "use-broadcast-ts": "^2.0.1", "use-debounce": "^10.0.5", "zustand": "^5.0.7" diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 04ea191..b2c0a63 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -82,6 +82,7 @@ pub fn run() { let _guard = minidump::init(&client); let mut app = tauri::Builder::default() + .plugin(tauri_plugin_sentry::init_with_no_injection(&client)) .plugin(tauri_plugin_clipboard_manager::init()) .plugin(tauri_plugin_updater::Builder::new().build()) .plugin(tauri_plugin_os::init()) diff --git a/src/components/PathFinder.tsx b/src/components/PathFinder.tsx index e60be73..1353f33 100644 --- a/src/components/PathFinder.tsx +++ b/src/components/PathFinder.tsx @@ -1,6 +1,7 @@ import { Autocomplete, Tooltip } from '@heroui/react' import { AutocompleteItem, Button } from '@heroui/react' import { cn } from '@heroui/react' +import * as Sentry from '@sentry/browser' import { open } from '@tauri-apps/plugin-dialog' import { readDir } from '@tauri-apps/plugin-fs' import { platform } from '@tauri-apps/plugin-os' @@ -356,6 +357,7 @@ export function PathField({ setPath(selected as string) } } catch (err) { + Sentry.captureException(err) console.error('Failed to open folder picker:', err) setError('Failed to open folder picker') } @@ -585,6 +587,7 @@ export function MultiPathField({ } } } catch (err) { + Sentry.captureException(err) console.error('Failed to open folder picker:', err) setError('Failed to open folder picker') } diff --git a/src/pages/Settings.tsx b/src/pages/Settings.tsx index 2ee8743..1e56a50 100644 --- a/src/pages/Settings.tsx +++ b/src/pages/Settings.tsx @@ -15,6 +15,7 @@ import { Textarea, cn, } from '@heroui/react' +import * as Sentry from '@sentry/browser' import { getTauriVersion, getVersion as getUiVersion } from '@tauri-apps/api/app' import { appDataDir, @@ -280,6 +281,7 @@ function GeneralSection() { timeout: 30000, }) } catch (e) { + Sentry.captureException(e) console.error(e) setUpdateButtonText('Failed to check') return @@ -295,6 +297,7 @@ function GeneralSection() { setUpdate(receivedUpdate) setUpdateButtonText('Tap to update') } catch (e) { + Sentry.captureException(e) console.error(e) } finally { setIsWorkingUpdate(false) @@ -328,6 +331,7 @@ function GeneralSection() { } }) } catch (error) { + Sentry.captureException(error) console.error(error) setIsWorkingUpdate(false) setUpdateButtonText('Tap to retry')