From 192c3109c042565ab3623d116ea82d20622e72f1 Mon Sep 17 00:00:00 2001 From: Eduard Gert Date: Tue, 16 Jun 2026 13:45:54 +0200 Subject: [PATCH] add more ui logs and fix sonarqube lint --- .../src/components/CopyToClipboard.tsx | 3 +- .../src/components/LanguagePicker.tsx | 2 +- .../src/components/buttons/Button.tsx | 2 +- .../frontend/src/components/inputs/Input.tsx | 3 +- .../src/contexts/DebugBundleContext.tsx | 178 +++++++++++------- .../frontend/src/contexts/SettingsContext.tsx | 11 +- .../frontend/src/contexts/ViewModeContext.tsx | 11 +- client/ui/frontend/src/lib/i18n.ts | 7 +- .../main/MainConnectionStatusSwitch.tsx | 8 +- .../frontend/src/modules/main/MainHeader.tsx | 12 +- 10 files changed, 147 insertions(+), 90 deletions(-) diff --git a/client/ui/frontend/src/components/CopyToClipboard.tsx b/client/ui/frontend/src/components/CopyToClipboard.tsx index f812203d3..d04dc3e57 100644 --- a/client/ui/frontend/src/components/CopyToClipboard.tsx +++ b/client/ui/frontend/src/components/CopyToClipboard.tsx @@ -50,7 +50,8 @@ export const CopyToClipboard = ({ setCopied(true); if (copyTimer.current) clearTimeout(copyTimer.current); copyTimer.current = setTimeout(() => setCopied(false), 500); - } catch { + } catch (e) { + console.warn("copy to clipboard failed", e); } }; diff --git a/client/ui/frontend/src/components/LanguagePicker.tsx b/client/ui/frontend/src/components/LanguagePicker.tsx index 1a11e7478..a915bd1aa 100644 --- a/client/ui/frontend/src/components/LanguagePicker.tsx +++ b/client/ui/frontend/src/components/LanguagePicker.tsx @@ -31,7 +31,7 @@ export function LanguagePicker() { .then((list) => { if (!cancelled) setLanguages(list); }) - .catch(() => {}); + .catch((err: unknown) => console.error("load languages failed", err)); return () => { cancelled = true; }; diff --git a/client/ui/frontend/src/components/buttons/Button.tsx b/client/ui/frontend/src/components/buttons/Button.tsx index 40dd8b1cc..ea69b64ce 100644 --- a/client/ui/frontend/src/components/buttons/Button.tsx +++ b/client/ui/frontend/src/components/buttons/Button.tsx @@ -163,7 +163,7 @@ export const Button = forwardRef(function Button if (copyTimer.current) clearTimeout(copyTimer.current); copyTimer.current = setTimeout(() => setCopied(false), 1500); }) - .catch(() => {}); + .catch((e: unknown) => console.warn("copy to clipboard failed", e)); } onClick?.(e); }} diff --git a/client/ui/frontend/src/components/inputs/Input.tsx b/client/ui/frontend/src/components/inputs/Input.tsx index f5f538e76..1b4e3d506 100644 --- a/client/ui/frontend/src/components/inputs/Input.tsx +++ b/client/ui/frontend/src/components/inputs/Input.tsx @@ -279,7 +279,8 @@ export const Input = forwardRef(function Input( setCopied(true); if (copyTimer.current) clearTimeout(copyTimer.current); copyTimer.current = setTimeout(() => setCopied(false), 1500); - } catch { + } catch (e) { + console.warn("copy to clipboard failed", e); } }; diff --git a/client/ui/frontend/src/contexts/DebugBundleContext.tsx b/client/ui/frontend/src/contexts/DebugBundleContext.tsx index a62d9af64..a0a131fbf 100644 --- a/client/ui/frontend/src/contexts/DebugBundleContext.tsx +++ b/client/ui/frontend/src/contexts/DebugBundleContext.tsx @@ -8,9 +8,6 @@ import { startConnection } from "@/lib/connection.ts"; const NETBIRD_UPLOAD_URL = "https://upload.debug.netbird.io/upload-url"; const TRACE_LOG_FILE_COUNT = 5; const PLAIN_LOG_FILE_COUNT = 1; -// Lowercase logrus level name sent to Debug.SetLogLevel (the Go binding -// upper-cases before the proto enum lookup). Raising to trace is what drives -// the daemon's verbose logging and the GUI's gui-client.log during a bundle. const TRACE_LOG_LEVEL = "trace"; const DEFAULT_LOG_LEVEL = "info"; @@ -51,22 +48,53 @@ const throwIfAborted = (signal: AbortSignal) => { const setLogLevelBestEffort = async (level: string) => { try { await DebugSvc.SetLogLevel({ level }); - } catch { - // empty + } catch (e) { + console.warn("[DebugBundle] best-effort set log level failed", e); } }; const stopCaptureBestEffort = async () => { try { await DebugSvc.StopBundleCapture(); - } catch { - // empty + } catch (e) { + console.warn("[DebugBundle] best-effort stop packet capture failed", e); } }; type LevelState = { original: string; raised: boolean }; type CaptureState = { started: boolean }; +type BundleOptions = { + trace: boolean; + capture: boolean; + capturePackets: boolean; + hasWindow: boolean; + totalSec: number; + uploadUrl: string; + anonymize: boolean; + systemInfo: boolean; +}; + +const startCaptureBestEffort = async (totalSec: number, pcap: CaptureState) => { + try { + // Mirror the CLI's safety margin: window + 30s, server caps at 10m. + await DebugSvc.StartBundleCapture(totalSec + 30); + pcap.started = true; + } catch (e) { + console.warn("[DebugBundle] start packet capture failed", e); + } +}; + +const cleanupBestEffort = async (pcap: CaptureState, level: LevelState, restoreLevel: boolean) => { + if (pcap.started) { + await stopCaptureBestEffort(); + pcap.started = false; + } + if (restoreLevel && level.raised) { + await setLogLevelBestEffort(level.original); + } +}; + const raiseToTrace = async ( signal: AbortSignal, level: LevelState, @@ -76,8 +104,8 @@ const raiseToTrace = async ( try { const cur = await DebugSvc.GetLogLevel(); if (cur?.level) level.original = cur.level; - } catch { - // empty + } catch (e) { + console.warn("[DebugBundle] read current log level failed", e); } throwIfAborted(signal); await DebugSvc.SetLogLevel({ level: TRACE_LOG_LEVEL }); @@ -89,8 +117,8 @@ const cycleConnection = async (signal: AbortSignal, setStage: (s: DebugStage) => setStage({ kind: "reconnecting" }); try { await ConnectionSvc.Down(); - } catch { - // empty + } catch (e) { + console.warn("[DebugBundle] disconnect before capture failed", e); } throwIfAborted(signal); await startConnection(undefined, signal); @@ -101,8 +129,8 @@ const restoreLogLevel = async (level: LevelState, setStage: (s: DebugStage) => v try { await DebugSvc.SetLogLevel({ level: level.original }); level.raised = false; - } catch { - // empty + } catch (e) { + console.warn("[DebugBundle] restore log level failed", e); } }; @@ -117,6 +145,58 @@ const waitCaptureWindow = async ( } }; +const runBundleFlow = async ( + signal: AbortSignal, + opts: BundleOptions, + level: LevelState, + pcap: CaptureState, + setStage: (s: DebugStage) => void, + setLastBundlePath: (p: string) => void, +) => { + if (opts.trace) { + await raiseToTrace(signal, level, setStage); + } + throwIfAborted(signal); + + if (opts.capture) { + await cycleConnection(signal, setStage); + } + throwIfAborted(signal); + + if (opts.hasWindow && opts.capturePackets) { + await startCaptureBestEffort(opts.totalSec, pcap); + } + throwIfAborted(signal); + + if (opts.hasWindow) { + await waitCaptureWindow(signal, setStage, opts.totalSec); + } + + if (pcap.started) { + await stopCaptureBestEffort(); + pcap.started = false; + } + + if (level.raised) { + await restoreLogLevel(level, setStage); + } + + throwIfAborted(signal); + setStage({ kind: "bundling" }); + const logFileCount = opts.trace ? TRACE_LOG_FILE_COUNT : PLAIN_LOG_FILE_COUNT; + + if (opts.uploadUrl) setStage({ kind: "uploading" }); + const result = await DebugSvc.Bundle({ + anonymize: opts.anonymize, + systemInfo: opts.systemInfo, + uploadUrl: opts.uploadUrl, + logFileCount, + }); + throwIfAborted(signal); + if (result.path) setLastBundlePath(result.path); + setStage({ kind: "done", result, uploadAttempted: Boolean(opts.uploadUrl) }); +}; + const useDebugBundle = () => { const [anonymize, setAnonymize] = useState(false); const [systemInfo, setSystemInfo] = useState(true); @@ -150,74 +230,30 @@ const useDebugBundle = () => { abortRef.current = ctrl; const signal = ctrl.signal; - const uploadUrl = upload ? NETBIRD_UPLOAD_URL : ""; + const totalSec = Math.max(1, Math.min(30, traceMinutes)) * 60; const level: LevelState = { original: DEFAULT_LOG_LEVEL, raised: false }; const pcap: CaptureState = { started: false }; - const totalSec = Math.max(1, Math.min(30, traceMinutes)) * 60; - const hasWindow = capture && totalSec > 0; + const opts: BundleOptions = { + trace, + capture, + capturePackets, + hasWindow: capture && totalSec > 0, + totalSec, + uploadUrl: upload ? NETBIRD_UPLOAD_URL : "", + anonymize, + systemInfo, + }; try { - if (trace) { - await raiseToTrace(signal, level, setStage); - } - throwIfAborted(signal); - - if (capture) { - await cycleConnection(signal, setStage); - } - throwIfAborted(signal); - - if (hasWindow && capturePackets) { - try { - // Mirror the CLI's safety margin: window + 30s, server caps at 10m. - await DebugSvc.StartBundleCapture(totalSec + 30); - pcap.started = true; - } catch { - // empty - } - } - throwIfAborted(signal); - - if (hasWindow) { - await waitCaptureWindow(signal, setStage, totalSec); - } - - if (pcap.started) { - await stopCaptureBestEffort(); - pcap.started = false; - } - - if (level.raised) { - await restoreLogLevel(level, setStage); - } - - throwIfAborted(signal); - setStage({ kind: "bundling" }); - const logFileCount = trace ? TRACE_LOG_FILE_COUNT : PLAIN_LOG_FILE_COUNT; - - if (uploadUrl) setStage({ kind: "uploading" }); - const result = await DebugSvc.Bundle({ - anonymize, - systemInfo, - uploadUrl, - logFileCount, - }); - throwIfAborted(signal); - if (result.path) setLastBundlePath(result.path); - setStage({ - kind: "done", - result, - uploadAttempted: Boolean(uploadUrl), - }); + await runBundleFlow(signal, opts, level, pcap, setStage, setLastBundlePath); } catch (e) { if (isAbort(e)) { setStage({ kind: "cancelling" }); - if (pcap.started) await stopCaptureBestEffort(); - if (level.raised) await setLogLevelBestEffort(level.original); + await cleanupBestEffort(pcap, level, true); setStage({ kind: "idle" }); return; } - if (pcap.started) await stopCaptureBestEffort(); + await cleanupBestEffort(pcap, level, false); setStage({ kind: "idle" }); await errorDialog({ Title: i18next.t("settings.error.debugBundleTitle"), diff --git a/client/ui/frontend/src/contexts/SettingsContext.tsx b/client/ui/frontend/src/contexts/SettingsContext.tsx index d222d1d28..08eff8afd 100644 --- a/client/ui/frontend/src/contexts/SettingsContext.tsx +++ b/client/ui/frontend/src/contexts/SettingsContext.tsx @@ -124,7 +124,7 @@ const useSettingsState = () => { const save = useCallback( async (profileName: string, next: Config, preSharedKey?: string) => { - const preSharedKeyWrite = preSharedKey !== undefined ? { preSharedKey } : {}; + const preSharedKeyWrite = preSharedKey === undefined ? {} : { preSharedKey }; try { await SettingsSvc.SetConfig({ ...next, @@ -194,9 +194,9 @@ const useSettingsState = () => { const merged: Config = { ...loaded.data, ...partial }; const next: Config = - opts?.preSharedKey !== undefined - ? { ...merged, preSharedKeySet: opts.preSharedKey !== "" } - : merged; + opts?.preSharedKey === undefined + ? merged + : { ...merged, preSharedKeySet: opts.preSharedKey !== "" }; setLoaded({ profileName: loaded.profileName, data: next }); await save(loaded.profileName, next, opts?.preSharedKey); }, @@ -235,8 +235,9 @@ export const AutostartSettingsProvider = ({ children }: { children: ReactNode }) const enabled = supported ? await Autostart.IsEnabled() : false; if (cancelled) return; setAutostart({ supported, enabled }); - })().catch(() => { + })().catch((err: unknown) => { if (cancelled) return; + console.warn("[SettingsContext] load autostart state failed", err); setAutostart({ supported: false, enabled: false }); }); return () => { diff --git a/client/ui/frontend/src/contexts/ViewModeContext.tsx b/client/ui/frontend/src/contexts/ViewModeContext.tsx index c551e7e95..822c0ec03 100644 --- a/client/ui/frontend/src/contexts/ViewModeContext.tsx +++ b/client/ui/frontend/src/contexts/ViewModeContext.tsx @@ -43,7 +43,7 @@ export const ViewModeProvider = ({ children }: { children: ReactNode }) => { setMode(saved); } }) - .catch(() => {}); + .catch((err: unknown) => console.warn("[ViewModeContext] load preferences failed", err)); return () => { cancelled = true; }; @@ -54,10 +54,15 @@ export const ViewModeProvider = ({ children }: { children: ReactNode }) => { if (modeRef.current === mode) return; modeRef.current = mode; (async () => { - const size = await Window.Size().catch(() => null); + const size = await Window.Size().catch((err: unknown) => { + console.warn("[ViewModeContext] read window size failed", err); + return null; + }); const width = VIEW_WIDTH[mode]; const height = size?.height ?? 640; - await Window.SetSize(width, height).catch(() => {}); + await Window.SetSize(width, height).catch((err: unknown) => + console.warn("[ViewModeContext] set window size failed", err), + ); setMode(mode); const pref = mode === "advanced" ? ViewModePref.ViewModeAdvanced : ViewModePref.ViewModeDefault; diff --git a/client/ui/frontend/src/lib/i18n.ts b/client/ui/frontend/src/lib/i18n.ts index e71ef4ad6..fbf46c901 100644 --- a/client/ui/frontend/src/lib/i18n.ts +++ b/client/ui/frontend/src/lib/i18n.ts @@ -53,11 +53,14 @@ export async function initI18n(): Promise { firstRun = true; language = detectBrowserLanguage(available) ?? "en"; } - } catch { + } catch (e) { + console.warn("read preferences for language failed, defaulting to en", e); } if (firstRun) { - Preferences.SetLanguage(language as LanguageCode).catch(() => {}); + Preferences.SetLanguage(language as LanguageCode).catch((err: unknown) => + console.warn("persist detected language failed", err), + ); } await i18next.use(initReactI18next).init({ diff --git a/client/ui/frontend/src/modules/main/MainConnectionStatusSwitch.tsx b/client/ui/frontend/src/modules/main/MainConnectionStatusSwitch.tsx index 96b8a4021..80d98294b 100644 --- a/client/ui/frontend/src/modules/main/MainConnectionStatusSwitch.tsx +++ b/client/ui/frontend/src/modules/main/MainConnectionStatusSwitch.tsx @@ -197,7 +197,9 @@ export const MainConnectionStatusSwitch = () => { console.error("emit browser-login cancel failed", err), ); } - WindowManager.CloseBrowserLogin().catch(() => {}); + WindowManager.CloseBrowserLogin().catch((err: unknown) => + console.warn("close browser-login window failed", err), + ); setAction("disconnect"); try { await Connection.Down(); @@ -356,7 +358,9 @@ const IpRow = ({ value }: { value: string }) => { await navigator.clipboard.writeText(value); setCopied(true); setTimeout(() => setCopied(false), 500); - } catch {} + } catch (e) { + console.warn("copy IP to clipboard failed", e); + } }; return (