update debug bundle setting page

This commit is contained in:
Eduard Gert
2026-06-12 12:09:10 +02:00
parent f8ccbb07bb
commit 860be01ebe
18 changed files with 431 additions and 277 deletions

View File

@@ -5,13 +5,15 @@ type Props = {
children?: ReactNode;
margin?: boolean;
className?: string;
disabled?: boolean;
};
export const HelpText = ({ children, margin = true, className }: Props) => (
export const HelpText = ({ children, margin = true, className, disabled = false }: Props) => (
<span
className={cn(
"text-[.81rem] dark:text-nb-gray-300 block font-light tracking-wide",
"text-[.81rem] dark:text-nb-gray-300 block font-light tracking-wide transition-all duration-300",
margin && "mb-2",
disabled && "opacity-30 pointer-events-none",
className,
)}
>

View File

@@ -10,13 +10,19 @@ const labelVariants = cva(
type LabelProps = ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
VariantProps<typeof labelVariants> & {
as?: "label" | "div";
disabled?: boolean;
};
export const Label = forwardRef<HTMLElement, LabelProps>(function Label(
{ className, as = "label", children, ...props },
{ className, as = "label", disabled = false, children, ...props },
ref,
) {
const classes = cn(labelVariants(), className, "select-none");
const classes = cn(
labelVariants(),
className,
"select-none transition-all duration-300",
disabled && "opacity-30 pointer-events-none",
);
if (as === "div") {
return (

View File

@@ -3,7 +3,7 @@ import { Connection as ConnectionSvc, Debug as DebugSvc } from "@bindings/servic
import type { DebugBundleResult } from "@bindings/services/models.js";
import i18next from "@/lib/i18n";
import { errorDialog, formatErrorMessage } from "@/lib/errors.ts";
import { useProfile } from "@/contexts/ProfileContext.tsx";
import { startConnection } from "@/lib/connection.ts";
const NETBIRD_UPLOAD_URL = "https://upload.debug.netbird.io/upload-url";
const TRACE_LOG_FILE_COUNT = 5;
@@ -56,14 +56,21 @@ const setLogLevelBestEffort = async (level: string) => {
}
};
type LevelState = { original: string; raised: boolean };
const stopCaptureBestEffort = async () => {
try {
await DebugSvc.StopBundleCapture();
} catch {
// empty
}
};
const runTracePhase = async (
type LevelState = { original: string; raised: boolean };
type CaptureState = { started: boolean };
const raiseToTrace = async (
signal: AbortSignal,
level: LevelState,
setStage: (s: DebugStage) => void,
target: { profileName: string; username: string },
traceMinutes: number,
) => {
setStage({ kind: "preparing-trace" });
try {
@@ -75,7 +82,9 @@ const runTracePhase = async (
throwIfAborted(signal);
await DebugSvc.SetLogLevel({ level: TRACE_LOG_LEVEL });
level.raised = true;
};
const cycleConnection = async (signal: AbortSignal, setStage: (s: DebugStage) => void) => {
throwIfAborted(signal);
setStage({ kind: "reconnecting" });
try {
@@ -84,14 +93,10 @@ const runTracePhase = async (
// empty
}
throwIfAborted(signal);
await ConnectionSvc.Up(target);
const totalSec = Math.max(1, Math.min(30, traceMinutes)) * 60;
for (let remaining = totalSec; remaining > 0; remaining--) {
setStage({ kind: "capturing", remainingSec: remaining, totalSec });
await sleep(1000, signal);
}
await startConnection(undefined, signal);
};
const restoreLogLevel = async (level: LevelState, setStage: (s: DebugStage) => void) => {
setStage({ kind: "restoring-level" });
try {
await DebugSvc.SetLogLevel({ level: level.original });
@@ -101,13 +106,25 @@ const runTracePhase = async (
}
};
const waitCaptureWindow = async (
signal: AbortSignal,
setStage: (s: DebugStage) => void,
totalSec: number,
) => {
for (let remaining = totalSec; remaining > 0; remaining--) {
setStage({ kind: "capturing", remainingSec: remaining, totalSec });
await sleep(1000, signal);
}
};
const useDebugBundle = () => {
const { activeProfile, username } = useProfile();
const [anonymize, setAnonymize] = useState(false);
const [systemInfo, setSystemInfo] = useState(true);
const [upload, setUpload] = useState(true);
const [trace, setTrace] = useState(false);
const [trace, setTrace] = useState(true);
const [capture, setCapture] = useState(false);
const [traceMinutes, setTraceMinutes] = useState(1);
const [capturePackets, setCapturePackets] = useState(true);
const [stage, setStage] = useState<DebugStage>({ kind: "idle" });
const [lastBundlePath, setLastBundlePath] = useState<string>("");
const abortRef = useRef<AbortController | null>(null);
@@ -129,16 +146,43 @@ const useDebugBundle = () => {
const uploadUrl = upload ? NETBIRD_UPLOAD_URL : "";
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;
try {
if (trace) {
await runTracePhase(
signal,
level,
setStage,
{ profileName: activeProfile, username },
traceMinutes,
);
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);
@@ -161,10 +205,13 @@ const useDebugBundle = () => {
});
} catch (e) {
if (isAbort(e)) {
setStage({ kind: "cancelling" });
if (pcap.started) await stopCaptureBestEffort();
if (level.raised) await setLogLevelBestEffort(level.original);
setStage({ kind: "idle" });
return;
}
if (pcap.started) await stopCaptureBestEffort();
setStage({ kind: "idle" });
await errorDialog({
Title: i18next.t("settings.error.debugBundleTitle"),
@@ -191,8 +238,12 @@ const useDebugBundle = () => {
setUpload,
trace,
setTrace,
capture,
setCapture,
traceMinutes,
setTraceMinutes,
capturePackets,
setCapturePackets,
stage,
isRunning,
lastBundlePath,

View File

@@ -0,0 +1,112 @@
import { Events } from "@wailsio/runtime";
import { Connection, WindowManager } from "@bindings/services";
import i18next from "@/lib/i18n";
import { errorDialog, formatErrorMessage } from "@/lib/errors.ts";
export const EVENT_BROWSER_LOGIN_CANCEL = "browser-login:cancel";
export const EVENT_TRIGGER_LOGIN = "trigger-login";
let connectionInFlight = false;
export async function startConnection(onSettled?: () => void, signal?: AbortSignal): Promise<void> {
if (connectionInFlight) {
onSettled?.();
return;
}
if (signal?.aborted) {
onSettled?.();
return;
}
connectionInFlight = true;
let cancelled = false;
let offCancel: (() => void) | undefined;
let offSignal: (() => void) | undefined;
let connectError: unknown;
try {
const result = await Connection.Login({
profileName: "",
username: "",
managementUrl: "",
setupKey: "",
preSharedKey: "",
hostname: "",
hint: "",
});
if (signal?.aborted) cancelled = true;
if (!cancelled && result.needsSsoLogin) {
const uri = result.verificationUriComplete || result.verificationUri;
if (uri) {
try {
await WindowManager.OpenBrowserLogin(uri);
} catch (e) {
console.error(e);
}
}
const cancelPromise = new Promise<void>((resolve) => {
offCancel = Events.On(EVENT_BROWSER_LOGIN_CANCEL, () => {
cancelled = true;
resolve();
});
if (signal) {
const onAbort = () => {
cancelled = true;
resolve();
};
if (signal.aborted) {
onAbort();
} else {
signal.addEventListener("abort", onAbort);
offSignal = () => signal.removeEventListener("abort", onAbort);
}
}
});
const waitPromise = Connection.WaitSSOLogin({
userCode: result.userCode,
hostname: "",
});
try {
await Promise.race([waitPromise, cancelPromise]);
} finally {
WindowManager.CloseBrowserLogin().catch(console.error);
}
if (cancelled) {
waitPromise.cancel?.();
waitPromise.catch(() => {});
}
}
if (!cancelled && signal?.aborted) cancelled = true;
if (!cancelled) {
await Connection.Up({ profileName: "", username: "" });
}
} catch (e) {
WindowManager.CloseBrowserLogin().catch(console.error);
if (!cancelled) connectError = e;
} finally {
offCancel?.();
offSignal?.();
connectionInFlight = false;
onSettled?.();
}
if (connectError !== undefined) {
await errorDialog({
Title: i18next.t("connect.error.loginTitle"),
Message: formatErrorMessage(connectError),
});
return;
}
if (cancelled && signal) {
throw new DOMException("aborted", "AbortError");
}
}

View File

@@ -2,12 +2,16 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { Events } from "@wailsio/runtime";
import { Connection, WindowManager } from "@bindings/services";
import i18next from "@/lib/i18n";
import { ToggleSwitch } from "@/components/switches/ToggleSwitch.tsx";
import { useStatus } from "@/contexts/StatusContext.tsx";
import { useProfile } from "@/contexts/ProfileContext.tsx";
import { cn } from "@/lib/cn.ts";
import { errorDialog, formatErrorMessage } from "@/lib/errors.ts";
import {
startConnection,
EVENT_BROWSER_LOGIN_CANCEL,
EVENT_TRIGGER_LOGIN,
} from "@/lib/connection.ts";
import { CopyToClipboard } from "@/components/CopyToClipboard";
import { TruncatedText } from "@/components/TruncatedText";
import { shortenDns } from "@/lib/formatters";
@@ -16,89 +20,6 @@ import { Check as CheckIcon, ChevronDownIcon, Copy as CopyIcon } from "lucide-re
import * as Popover from "@radix-ui/react-popover";
import netbirdFullLogo from "@/assets/logos/netbird-full.svg";
const EVENT_BROWSER_LOGIN_CANCEL = "browser-login:cancel";
const EVENT_TRIGGER_LOGIN = "trigger-login";
let loginInFlight = false;
// onSettled (re-arm guards) must fire before the error dialog, never gated on it:
// a hanging dialog would silently drop every later login until restart.
async function startLogin(onSettled?: () => void): Promise<void> {
if (loginInFlight) {
onSettled?.();
return;
}
loginInFlight = true;
let cancelled = false;
let offCancel: (() => void) | undefined;
let loginError: unknown;
try {
const result = await Connection.Login({
profileName: "",
username: "",
managementUrl: "",
setupKey: "",
preSharedKey: "",
hostname: "",
hint: "",
});
if (result.needsSsoLogin) {
const uri = result.verificationUriComplete || result.verificationUri;
if (uri) {
try {
await WindowManager.OpenBrowserLogin(uri);
} catch (e) {
console.error(e);
}
}
const cancelPromise = new Promise<void>((resolve) => {
offCancel = Events.On(EVENT_BROWSER_LOGIN_CANCEL, () => {
cancelled = true;
resolve();
});
});
const waitPromise = Connection.WaitSSOLogin({
userCode: result.userCode,
hostname: "",
});
try {
await Promise.race([waitPromise, cancelPromise]);
} finally {
WindowManager.CloseBrowserLogin().catch(console.error);
}
if (cancelled) {
waitPromise.cancel?.();
waitPromise.catch(() => {});
return;
}
}
await Connection.Up({ profileName: "", username: "" });
} catch (e) {
WindowManager.CloseBrowserLogin().catch(console.error);
if (!cancelled) loginError = e;
} finally {
offCancel?.();
loginInFlight = false;
onSettled?.();
}
if (loginError !== undefined) {
await errorDialog({
Title: i18next.t("connect.error.loginTitle"),
Message: formatErrorMessage(loginError),
});
}
}
enum ConnectionState {
Disconnected = "disconnected",
Connecting = "connecting",
@@ -136,7 +57,7 @@ export const MainConnectionStatusSwitch = () => {
if (loginGuard.current) return;
loginGuard.current = true;
setAction("logging-in");
void startLogin(() => {
void startConnection(() => {
loginGuard.current = false;
setAction(null);
refresh().catch((err: unknown) => console.error("refresh after login failed", err));

View File

@@ -52,7 +52,7 @@ export const SettingsPage = () => {
className={"flex-1 min-h-0 overflow-hidden"}
>
<ScrollArea.Viewport className={"h-full w-full"}>
<div className={"py-8 px-7"}>
<div className={"py-6 px-7"}>
<VerticalTabs.Content value={"general"}>
<SettingsGeneral />
</VerticalTabs.Content>

View File

@@ -20,7 +20,7 @@ export const SectionGroup = ({
export const SettingsBottomBar = ({ children }: { children: ReactNode }) => (
<>
<div className={"h-[4rem] shrink-0"} aria-hidden />
<div className={"h-[3.2rem] shrink-0"} aria-hidden />
<div className={"absolute bottom-0 left-0 w-full"}>
<div
className={

View File

@@ -13,7 +13,6 @@ import HelpText from "@/components/typography/HelpText.tsx";
import { Input } from "@/components/inputs/Input";
import { Label } from "@/components/typography/Label";
import { SquareIcon } from "@/components/SquareIcon";
import { cn } from "@/lib/cn";
import { formatRemaining } from "@/lib/formatters";
import type { DebugStage } from "@/contexts/DebugBundleContext";
import { useDebugBundleContext } from "@/contexts/DebugBundleContext";
@@ -32,8 +31,12 @@ export function SettingsTroubleshooting() {
setUpload,
trace,
setTrace,
capture,
setCapture,
traceMinutes,
setTraceMinutes,
capturePackets,
setCapturePackets,
run,
stage,
cancel,
@@ -51,10 +54,6 @@ export function SettingsTroubleshooting() {
return (
<SectionGroup title={t("settings.troubleshooting.section.title")}>
<HelpText className={"-mt-2 mb-2"}>
<Trans i18nKey={"settings.troubleshooting.intro"} components={{ br: <br /> }} />
</HelpText>
<FancyToggleSwitch
value={anonymize}
onChange={setAnonymize}
@@ -79,30 +78,44 @@ export function SettingsTroubleshooting() {
label={t("settings.troubleshooting.trace.label")}
helpText={t("settings.troubleshooting.trace.help")}
/>
<div
className={cn(
"flex items-center gap-6 justify-between",
!trace && "opacity-50 pointer-events-none",
)}
>
<div className={"flex-1 max-w-md"}>
<Label as={"div"}>{t("settings.troubleshooting.duration.label")}</Label>
<HelpText margin={false}>
{t("settings.troubleshooting.duration.help")}
</HelpText>
</div>
<div className={"w-40 shrink-0"}>
<Input
type={"number"}
min={1}
max={30}
value={traceMinutes}
onChange={(e) =>
setTraceMinutes(Math.max(1, Math.min(30, Number(e.target.value) || 1)))
}
customSuffix={t("settings.troubleshooting.duration.suffix")}
disabled={!trace}
/>
<FancyToggleSwitch
value={capture}
onChange={setCapture}
label={t("settings.troubleshooting.capture.label")}
helpText={t("settings.troubleshooting.capture.help")}
/>
<div className={"flex flex-col gap-4"}>
<FancyToggleSwitch
value={capturePackets}
onChange={setCapturePackets}
label={t("settings.troubleshooting.packets.label")}
helpText={t("settings.troubleshooting.packets.help")}
disabled={!capture}
/>
<div className={"flex items-center gap-6 justify-between"}>
<div className={"flex-1 max-w-md"}>
<Label as={"div"} disabled={!capture}>
{t("settings.troubleshooting.duration.label")}
</Label>
<HelpText margin={false} disabled={!capture}>
{t("settings.troubleshooting.duration.help")}
</HelpText>
</div>
<div className={"w-40 shrink-0"}>
<Input
type={"number"}
min={1}
max={30}
value={traceMinutes}
onChange={(e) =>
setTraceMinutes(
Math.max(1, Math.min(30, Number(e.target.value) || 1)),
)
}
customSuffix={t("settings.troubleshooting.duration.suffix")}
disabled={!capture}
/>
</div>
</div>
</div>
@@ -297,14 +310,10 @@ const stageLabel = (
t: (key: string, options?: Record<string, unknown>) => string,
): string => {
switch (stage.kind) {
case "preparing-trace":
return t("settings.troubleshooting.stage.preparingTrace");
case "reconnecting":
return t("settings.troubleshooting.stage.reconnecting");
case "capturing":
return t("settings.troubleshooting.stage.capturing");
case "restoring-level":
return t("settings.troubleshooting.stage.restoring");
case "bundling":
return t("settings.troubleshooting.stage.bundling");
case "uploading":

View File

@@ -698,9 +698,6 @@
"settings.troubleshooting.section.title": {
"message": "Debug-Paket"
},
"settings.troubleshooting.intro": {
"message": "Ein Debug-Paket hilft dem NetBird-Support bei der Untersuchung von Verbindungsproblemen. <br /> Es ist eine .zip-Datei mit Logs, Systemdetails und Debug-Informationen Ihres Geräts."
},
"settings.troubleshooting.anonymize.label": {
"message": "Sensible Informationen anonymisieren"
},
@@ -717,19 +714,31 @@
"message": "Paket an NetBird-Server hochladen"
},
"settings.troubleshooting.upload.help": {
"message": "Lädt das Paket sicher hoch und gibt einen Upload-Schlüssel zurück. Teilen Sie den Schlüssel über GitHub oder Slack mit dem NetBird-Support, anstatt die Datei direkt anzuhängen."
"message": "Gibt einen Upload-Schlüssel zurück, den Sie mit dem NetBird-Support teilen können."
},
"settings.troubleshooting.trace.label": {
"message": "Trace-Logs erfassen"
"message": "Trace-Logs aktivieren"
},
"settings.troubleshooting.trace.help": {
"message": "Erhöht das Logging auf TRACE und schaltet NetBird kurz aus und wieder ein, um Verbindungs-Logs zu erfassen. Das vorherige Level wird nach Erstellung des Pakets wiederhergestellt."
"message": "Hebt das Log-Level auf TRACE an und stellt es danach wieder her."
},
"settings.troubleshooting.capture.label": {
"message": "Aufzeichnungssitzung"
},
"settings.troubleshooting.capture.help": {
"message": "Stellt die Verbindung neu her und wartet, damit Sie das Problem reproduzieren können."
},
"settings.troubleshooting.packets.label": {
"message": "Netzwerkpakete aufzeichnen"
},
"settings.troubleshooting.packets.help": {
"message": "Speichert eine .pcap-Datei des Netzwerkverkehrs während der Aufzeichnung."
},
"settings.troubleshooting.duration.label": {
"message": "Aufzeichnungsdauer"
},
"settings.troubleshooting.duration.help": {
"message": "Wie lange Trace-Logs vor der Paketerstellung erfasst werden sollen."
"message": "Wie lange die Aufzeichnungssitzung läuft."
},
"settings.troubleshooting.duration.suffix": {
"message": "Minute(n)"
@@ -770,17 +779,11 @@
"settings.troubleshooting.uploadFailed": {
"message": "Upload fehlgeschlagen. Das Paket wurde trotzdem lokal gespeichert."
},
"settings.troubleshooting.stage.preparingTrace": {
"message": "Wechsel zu Trace-Logging…"
},
"settings.troubleshooting.stage.reconnecting": {
"message": "NetBird wird neu verbunden…"
},
"settings.troubleshooting.stage.capturing": {
"message": "Logs werden erfasst"
},
"settings.troubleshooting.stage.restoring": {
"message": "Vorheriges Log-Level wird wiederhergestellt…"
"message": "Debug-Logs werden erfasst"
},
"settings.troubleshooting.stage.bundling": {
"message": "Debug-Paket wird erstellt…"

View File

@@ -931,10 +931,6 @@
"message": "Debug bundle",
"description": "Section heading: Debug bundle."
},
"settings.troubleshooting.intro": {
"message": "A debug bundle helps NetBird support investigate connection problems. <br /> It's a .zip file with logs, system details and debug information from your device.",
"description": "Intro paragraph on the Troubleshoot tab. Contains an HTML '<br />' line break — keep it exactly. '.zip' is a file extension — keep it."
},
"settings.troubleshooting.anonymize.label": {
"message": "Anonymize Sensitive Information",
"description": "Toggle label: anonymize sensitive information in the bundle."
@@ -956,23 +952,39 @@
"description": "Toggle label: upload the bundle to NetBird servers."
},
"settings.troubleshooting.upload.help": {
"message": "Securely uploads the bundle and returns an upload key. Share the key with NetBird support over GitHub or Slack instead of attaching the file directly.",
"description": "Helper text for uploading the bundle. 'GitHub' and 'Slack' are brands — keep them."
"message": "Returns an upload key to share with NetBird support.",
"description": "Helper text for uploading the bundle."
},
"settings.troubleshooting.trace.label": {
"message": "Capture Trace Logs",
"description": "Toggle label: capture trace logs. 'TRACE' is a log level."
"message": "Enable Trace Logs",
"description": "Toggle label: raise daemon log level to TRACE while the bundle is built. 'TRACE' is a log level."
},
"settings.troubleshooting.trace.help": {
"message": "Raises logging to TRACE and cycles NetBird up and down to capture connection logs. The previous level is restored after the bundle is built.",
"description": "Helper text explaining trace capture restarts NetBird briefly. 'TRACE' — keep as-is."
"message": "Raises the log level to TRACE and restores it after.",
"description": "Helper text for the trace toggle. 'TRACE' is a log level — keep as-is."
},
"settings.troubleshooting.capture.label": {
"message": "Capture Session",
"description": "Toggle label: open a capture session — reconnect NetBird, wait a duration, optionally record packets."
},
"settings.troubleshooting.capture.help": {
"message": "Reconnects and waits so you can reproduce the issue.",
"description": "Helper text for the master Capture Session toggle."
},
"settings.troubleshooting.packets.label": {
"message": "Capture Network Packets",
"description": "Toggle label: capture packets to a .pcap during the capture session."
},
"settings.troubleshooting.packets.help": {
"message": "Saves a .pcap of network traffic during the capture window.",
"description": "Helper text for the packet recording toggle. '.pcap' is a file extension — keep it."
},
"settings.troubleshooting.duration.label": {
"message": "Capture Duration",
"description": "Label for the trace-capture duration field."
},
"settings.troubleshooting.duration.help": {
"message": "How long to capture trace logs before generating the bundle.",
"message": "How long the capture session runs.",
"description": "Helper text for the capture duration."
},
"settings.troubleshooting.duration.suffix": {
@@ -1027,22 +1039,14 @@
"message": "Upload failed. The bundle is still saved locally.",
"description": "Shown when upload failed (no specific reason) but the bundle was saved locally."
},
"settings.troubleshooting.stage.preparingTrace": {
"message": "Switching to trace logging…",
"description": "Progress stage: switching to trace logging. Ends with an ellipsis."
},
"settings.troubleshooting.stage.reconnecting": {
"message": "Reconnecting NetBird…",
"description": "Progress stage: reconnecting NetBird. Ends with an ellipsis."
},
"settings.troubleshooting.stage.capturing": {
"message": "Capturing logs",
"message": "Capturing debug logs",
"description": "Progress stage: capturing logs. {elapsed} and {total} are time values (e.g. 0:30 / 2:00); keep both."
},
"settings.troubleshooting.stage.restoring": {
"message": "Restoring previous log level…",
"description": "Progress stage: restoring the previous log level. Ends with an ellipsis."
},
"settings.troubleshooting.stage.bundling": {
"message": "Generating debug bundle…",
"description": "Progress stage: generating the debug bundle. Ends with an ellipsis."

View File

@@ -698,9 +698,6 @@
"settings.troubleshooting.section.title": {
"message": "Paquete de diagnóstico"
},
"settings.troubleshooting.intro": {
"message": "Un paquete de diagnóstico ayuda al soporte de NetBird a investigar problemas de conexión. <br /> Es un archivo .zip con registros, detalles del sistema e información de depuración de su dispositivo."
},
"settings.troubleshooting.anonymize.label": {
"message": "Anonimizar información sensible"
},
@@ -717,19 +714,31 @@
"message": "Subir el paquete a los servidores de NetBird"
},
"settings.troubleshooting.upload.help": {
"message": "Sube el paquete de forma segura y devuelve una clave de subida. Comparta la clave con el soporte de NetBird por GitHub o Slack en lugar de adjuntar el archivo directamente."
"message": "Devuelve una clave de subida para compartir con el soporte de NetBird."
},
"settings.troubleshooting.trace.label": {
"message": "Capturar registros de seguimiento"
"message": "Activar registros de seguimiento"
},
"settings.troubleshooting.trace.help": {
"message": "Eleva el registro a TRACE y reinicia NetBird para capturar los registros de conexión. El nivel anterior se restaura después de generar el paquete."
"message": "Eleva el nivel de registro a TRACE y lo restaura después."
},
"settings.troubleshooting.capture.label": {
"message": "Sesión de captura"
},
"settings.troubleshooting.capture.help": {
"message": "Vuelve a conectar y espera para que pueda reproducir el problema."
},
"settings.troubleshooting.packets.label": {
"message": "Capturar paquetes de red"
},
"settings.troubleshooting.packets.help": {
"message": "Guarda un .pcap del tráfico de red durante la sesión de captura."
},
"settings.troubleshooting.duration.label": {
"message": "Duración de la captura"
},
"settings.troubleshooting.duration.help": {
"message": "Cuánto tiempo capturar registros de seguimiento antes de generar el paquete."
"message": "Cuánto tiempo se ejecuta la sesión de captura."
},
"settings.troubleshooting.duration.suffix": {
"message": "min"
@@ -770,17 +779,11 @@
"settings.troubleshooting.uploadFailed": {
"message": "Error en la subida. El paquete sigue guardado localmente."
},
"settings.troubleshooting.stage.preparingTrace": {
"message": "Cambiando al registro de seguimiento…"
},
"settings.troubleshooting.stage.reconnecting": {
"message": "Reconectando NetBird…"
},
"settings.troubleshooting.stage.capturing": {
"message": "Capturando registros"
},
"settings.troubleshooting.stage.restoring": {
"message": "Restaurando el nivel de registro anterior…"
"message": "Capturando registros de depuración"
},
"settings.troubleshooting.stage.bundling": {
"message": "Generando el paquete de diagnóstico…"

View File

@@ -698,9 +698,6 @@
"settings.troubleshooting.section.title": {
"message": "Lot de diagnostic"
},
"settings.troubleshooting.intro": {
"message": "Un lot de diagnostic aide lassistance NetBird à étudier les problèmes de connexion. <br /> Cest un fichier .zip contenant des journaux, des détails système et des informations de diagnostic provenant de votre appareil."
},
"settings.troubleshooting.anonymize.label": {
"message": "Anonymiser les informations sensibles"
},
@@ -717,19 +714,31 @@
"message": "Envoyer le lot aux serveurs NetBird"
},
"settings.troubleshooting.upload.help": {
"message": "Envoie le lot de manière sécurisée et renvoie une clé denvoi. Partagez la clé avec lassistance NetBird via GitHub ou Slack plutôt que de joindre directement le fichier."
"message": "Renvoie une clé denvoi à partager avec lassistance NetBird."
},
"settings.troubleshooting.trace.label": {
"message": "Capturer les journaux trace"
"message": "Activer les journaux trace"
},
"settings.troubleshooting.trace.help": {
"message": "Augmente la journalisation au niveau TRACE et redémarre NetBird pour capturer les journaux de connexion. Le niveau précédent est rétabli une fois le lot généré."
"message": "Élève le niveau de journalisation à TRACE, puis le rétablit ensuite."
},
"settings.troubleshooting.capture.label": {
"message": "Session de capture"
},
"settings.troubleshooting.capture.help": {
"message": "Se reconnecte et attend pour que vous puissiez reproduire le problème."
},
"settings.troubleshooting.packets.label": {
"message": "Capturer les paquets réseau"
},
"settings.troubleshooting.packets.help": {
"message": "Enregistre un .pcap du trafic réseau pendant la session de capture."
},
"settings.troubleshooting.duration.label": {
"message": "Durée de capture"
},
"settings.troubleshooting.duration.help": {
"message": "Durée de capture des journaux trace avant de générer le lot."
"message": "Durée dexécution de la session de capture."
},
"settings.troubleshooting.duration.suffix": {
"message": "min"
@@ -770,17 +779,11 @@
"settings.troubleshooting.uploadFailed": {
"message": "Échec de lenvoi. Le lot reste enregistré localement."
},
"settings.troubleshooting.stage.preparingTrace": {
"message": "Passage à la journalisation trace…"
},
"settings.troubleshooting.stage.reconnecting": {
"message": "Reconnexion de NetBird…"
},
"settings.troubleshooting.stage.capturing": {
"message": "Capture des journaux"
},
"settings.troubleshooting.stage.restoring": {
"message": "Rétablissement du niveau de journalisation précédent…"
"message": "Capture des journaux de débogage"
},
"settings.troubleshooting.stage.bundling": {
"message": "Génération du lot de diagnostic…"

View File

@@ -698,9 +698,6 @@
"settings.troubleshooting.section.title": {
"message": "Hibakeresési csomag"
},
"settings.troubleshooting.intro": {
"message": "A hibakeresési csomag segít a NetBird támogatásnak a kapcsolati problémák kivizsgálásában. <br /> Egy .zip fájl, amely naplókat, rendszerinformációkat és hibakeresési adatokat tartalmaz az eszközéről."
},
"settings.troubleshooting.anonymize.label": {
"message": "Érzékeny információk anonimizálása"
},
@@ -717,19 +714,31 @@
"message": "Csomag feltöltése a NetBird szerverekre"
},
"settings.troubleshooting.upload.help": {
"message": "Biztonságosan feltölti a csomagot, és visszaad egy feltöltési kulcsot. Ossza meg a kulcsot a NetBird támogatással a GitHubon vagy Slacken keresztül a fájl közvetlen csatolása helyett."
"message": "Egy feltöltési kulcsot ad vissza, amelyet megoszthat a NetBird támogatással."
},
"settings.troubleshooting.trace.label": {
"message": "Trace naplók rögzítése"
"message": "Trace naplók engedélyezése"
},
"settings.troubleshooting.trace.help": {
"message": "TRACE szintre emeli a naplózást, és újraindítja a NetBird kapcsolatot a kapcsolati naplók rögzítéséhez. Az előző szint a csomag elkészülte után visszaáll."
"message": "TRACE szintre emeli a naplózást, majd utána visszaállítja."
},
"settings.troubleshooting.capture.label": {
"message": "Rögzítési munkamenet"
},
"settings.troubleshooting.capture.help": {
"message": "Újra csatlakozik és vár, hogy reprodukálhassa a problémát."
},
"settings.troubleshooting.packets.label": {
"message": "Hálózati csomagok rögzítése"
},
"settings.troubleshooting.packets.help": {
"message": "A rögzítés ideje alatt elmenti a hálózati forgalom .pcap fájlját."
},
"settings.troubleshooting.duration.label": {
"message": "Rögzítés időtartama"
},
"settings.troubleshooting.duration.help": {
"message": "Mennyi ideig rögzítse a trace naplókat a csomag elkészítése előtt."
"message": "Mennyi ideig fusson a rögzítési munkamenet."
},
"settings.troubleshooting.duration.suffix": {
"message": "perc"
@@ -770,17 +779,11 @@
"settings.troubleshooting.uploadFailed": {
"message": "Feltöltés sikertelen. A csomag továbbra is el van mentve helyileg."
},
"settings.troubleshooting.stage.preparingTrace": {
"message": "Váltás trace naplózásra…"
},
"settings.troubleshooting.stage.reconnecting": {
"message": "NetBird újracsatlakoztatása…"
},
"settings.troubleshooting.stage.capturing": {
"message": "Naplók rögzítése"
},
"settings.troubleshooting.stage.restoring": {
"message": "Korábbi napló szint visszaállítása…"
"message": "Hibakeresési naplók rögzítése"
},
"settings.troubleshooting.stage.bundling": {
"message": "Hibakeresési csomag generálása…"

View File

@@ -698,9 +698,6 @@
"settings.troubleshooting.section.title": {
"message": "Pacchetto di debug"
},
"settings.troubleshooting.intro": {
"message": "Un pacchetto di debug aiuta il supporto NetBird a indagare sui problemi di connessione. <br /> È un file .zip con log, dettagli di sistema e informazioni di debug del suo dispositivo."
},
"settings.troubleshooting.anonymize.label": {
"message": "Anonimizza informazioni sensibili"
},
@@ -717,19 +714,31 @@
"message": "Carica il pacchetto sui server NetBird"
},
"settings.troubleshooting.upload.help": {
"message": "Carica il pacchetto in modo sicuro e restituisce una chiave di caricamento. Condivida la chiave con il supporto NetBird tramite GitHub o Slack invece di allegare direttamente il file."
"message": "Restituisce una chiave di caricamento da condividere con il supporto NetBird."
},
"settings.troubleshooting.trace.label": {
"message": "Acquisisci log di traccia"
"message": "Abilita log di traccia"
},
"settings.troubleshooting.trace.help": {
"message": "Aumenta il livello di log a TRACE e riavvia NetBird per acquisire i log di connessione. Il livello precedente viene ripristinato dopo la creazione del pacchetto."
"message": "Aumenta il livello di log a TRACE e lo ripristina al termine."
},
"settings.troubleshooting.capture.label": {
"message": "Sessione di acquisizione"
},
"settings.troubleshooting.capture.help": {
"message": "Si riconnette e attende per consentirle di riprodurre il problema."
},
"settings.troubleshooting.packets.label": {
"message": "Acquisisci pacchetti di rete"
},
"settings.troubleshooting.packets.help": {
"message": "Salva un .pcap del traffico di rete durante la sessione di acquisizione."
},
"settings.troubleshooting.duration.label": {
"message": "Durata acquisizione"
},
"settings.troubleshooting.duration.help": {
"message": "Per quanto tempo acquisire i log di traccia prima di generare il pacchetto."
"message": "Per quanto tempo viene eseguita la sessione di acquisizione."
},
"settings.troubleshooting.duration.suffix": {
"message": "min."
@@ -770,17 +779,11 @@
"settings.troubleshooting.uploadFailed": {
"message": "Caricamento non riuscito. Il pacchetto è comunque salvato localmente."
},
"settings.troubleshooting.stage.preparingTrace": {
"message": "Passaggio al logging di traccia…"
},
"settings.troubleshooting.stage.reconnecting": {
"message": "Riconnessione di NetBird…"
},
"settings.troubleshooting.stage.capturing": {
"message": "Acquisizione log"
},
"settings.troubleshooting.stage.restoring": {
"message": "Ripristino del livello di log precedente…"
"message": "Acquisizione log di debug"
},
"settings.troubleshooting.stage.bundling": {
"message": "Generazione del pacchetto di debug…"

View File

@@ -698,9 +698,6 @@
"settings.troubleshooting.section.title": {
"message": "Pacote de depuração"
},
"settings.troubleshooting.intro": {
"message": "Um pacote de depuração ajuda o suporte do NetBird a investigar problemas de conexão. <br /> É um arquivo .zip com logs, detalhes do sistema e informações de depuração do seu dispositivo."
},
"settings.troubleshooting.anonymize.label": {
"message": "Anonimizar informações sensíveis"
},
@@ -717,19 +714,31 @@
"message": "Enviar pacote aos servidores do NetBird"
},
"settings.troubleshooting.upload.help": {
"message": "Envia o pacote com segurança e retorna uma chave de upload. Compartilhe a chave com o suporte do NetBird via GitHub ou Slack em vez de anexar o arquivo diretamente."
"message": "Retorna uma chave de upload para compartilhar com o suporte do NetBird."
},
"settings.troubleshooting.trace.label": {
"message": "Capturar logs de trace"
"message": "Habilitar logs de trace"
},
"settings.troubleshooting.trace.help": {
"message": "Eleva o nível de log para TRACE e reinicia o NetBird para capturar os logs de conexão. O nível anterior é restaurado após a criação do pacote."
"message": "Eleva o nível de log para TRACE e o restaura em seguida."
},
"settings.troubleshooting.capture.label": {
"message": "Sessão de captura"
},
"settings.troubleshooting.capture.help": {
"message": "Reconecta e aguarda para que você possa reproduzir o problema."
},
"settings.troubleshooting.packets.label": {
"message": "Capturar pacotes de rede"
},
"settings.troubleshooting.packets.help": {
"message": "Salva um .pcap do tráfego de rede durante a sessão de captura."
},
"settings.troubleshooting.duration.label": {
"message": "Duração da captura"
},
"settings.troubleshooting.duration.help": {
"message": "Por quanto tempo capturar os logs de trace antes de gerar o pacote."
"message": "Por quanto tempo a sessão de captura é executada."
},
"settings.troubleshooting.duration.suffix": {
"message": "min"
@@ -770,17 +779,11 @@
"settings.troubleshooting.uploadFailed": {
"message": "Falha no upload. O pacote continua salvo localmente."
},
"settings.troubleshooting.stage.preparingTrace": {
"message": "Alternando para logs de trace…"
},
"settings.troubleshooting.stage.reconnecting": {
"message": "Reconectando o NetBird…"
},
"settings.troubleshooting.stage.capturing": {
"message": "Capturando logs"
},
"settings.troubleshooting.stage.restoring": {
"message": "Restaurando o nível de log anterior…"
"message": "Capturando logs de depuração"
},
"settings.troubleshooting.stage.bundling": {
"message": "Gerando o pacote de depuração…"

View File

@@ -698,9 +698,6 @@
"settings.troubleshooting.section.title": {
"message": "Отладочный пакет"
},
"settings.troubleshooting.intro": {
"message": "Отладочный пакет помогает поддержке NetBird исследовать проблемы с подключением. <br /> Это .zip-файл с журналами, сведениями о системе и отладочной информацией с вашего устройства."
},
"settings.troubleshooting.anonymize.label": {
"message": "Анонимизировать конфиденциальную информацию"
},
@@ -717,19 +714,31 @@
"message": "Загрузить пакет на серверы NetBird"
},
"settings.troubleshooting.upload.help": {
"message": "Безопасно загружает пакет и возвращает ключ загрузки. Поделитесь ключом с поддержкой NetBird через GitHub или Slack вместо того, чтобы прикреплять файл напрямую."
"message": "Возвращает ключ загрузки, который можно передать поддержке NetBird."
},
"settings.troubleshooting.trace.label": {
"message": "Собирать журналы TRACE"
"message": "Включить журналы TRACE"
},
"settings.troubleshooting.trace.help": {
"message": "Повышает уровень журналирования до TRACE и перезапускает NetBird (отключение и подключение) для сбора журналов подключения. Прежний уровень восстанавливается после создания пакета."
"message": "Повышает уровень журналирования до TRACE и затем восстанавливает прежний."
},
"settings.troubleshooting.capture.label": {
"message": "Сеанс записи"
},
"settings.troubleshooting.capture.help": {
"message": "Переподключается и ожидает, чтобы вы могли воспроизвести проблему."
},
"settings.troubleshooting.packets.label": {
"message": "Записывать сетевые пакеты"
},
"settings.troubleshooting.packets.help": {
"message": "Сохраняет .pcap сетевого трафика во время сеанса записи."
},
"settings.troubleshooting.duration.label": {
"message": "Длительность записи"
},
"settings.troubleshooting.duration.help": {
"message": "Как долго собирать журналы TRACE перед созданием пакета."
"message": "Как долго длится сеанс записи."
},
"settings.troubleshooting.duration.suffix": {
"message": "мин."
@@ -770,17 +779,11 @@
"settings.troubleshooting.uploadFailed": {
"message": "Не удалось загрузить. Пакет всё равно сохранён локально."
},
"settings.troubleshooting.stage.preparingTrace": {
"message": "Переключение на журналирование TRACE…"
},
"settings.troubleshooting.stage.reconnecting": {
"message": "Переподключение NetBird…"
},
"settings.troubleshooting.stage.capturing": {
"message": "Сбор журналов"
},
"settings.troubleshooting.stage.restoring": {
"message": "Восстановление прежнего уровня журналирования…"
"message": "Сбор отладочных журналов"
},
"settings.troubleshooting.stage.bundling": {
"message": "Создание отладочного пакета…"

View File

@@ -698,9 +698,6 @@
"settings.troubleshooting.section.title": {
"message": "调试包"
},
"settings.troubleshooting.intro": {
"message": "调试包可帮助 NetBird 支持团队排查连接问题。<br /> 它是一个 .zip 文件,包含来自您设备的日志、系统详情和调试信息。"
},
"settings.troubleshooting.anonymize.label": {
"message": "匿名化敏感信息"
},
@@ -717,19 +714,31 @@
"message": "将调试包上传到 NetBird 服务器"
},
"settings.troubleshooting.upload.help": {
"message": "安全地上传调试包并返回一个上传密钥。请通过 GitHub 或 Slack 将该密钥分享给 NetBird 支持团队,而不要直接附上文件。"
"message": "返回一个上传密钥,供您分享给 NetBird 支持团队。"
},
"settings.troubleshooting.trace.label": {
"message": "捕获跟踪日志"
"message": "启用跟踪日志"
},
"settings.troubleshooting.trace.help": {
"message": "将日志级别提升到 TRACE并使 NetBird 上下线一次以捕获连接日志。调试包生成后将恢复先前的级别。"
"message": "将日志级别提升到 TRACE之后再恢复原级别。"
},
"settings.troubleshooting.capture.label": {
"message": "捕获会话"
},
"settings.troubleshooting.capture.help": {
"message": "重新连接并等待,以便您复现问题。"
},
"settings.troubleshooting.packets.label": {
"message": "捕获网络数据包"
},
"settings.troubleshooting.packets.help": {
"message": "在捕获期间将网络流量保存为 .pcap 文件。"
},
"settings.troubleshooting.duration.label": {
"message": "捕获时长"
},
"settings.troubleshooting.duration.help": {
"message": "在生成调试包之前捕获跟踪日志的时长。"
"message": "捕获会话运行的时长。"
},
"settings.troubleshooting.duration.suffix": {
"message": "分钟"
@@ -770,17 +779,11 @@
"settings.troubleshooting.uploadFailed": {
"message": "上传失败。调试包仍已保存在本地。"
},
"settings.troubleshooting.stage.preparingTrace": {
"message": "正在切换到跟踪日志记录…"
},
"settings.troubleshooting.stage.reconnecting": {
"message": "正在重新连接 NetBird…"
},
"settings.troubleshooting.stage.capturing": {
"message": "正在捕获日志"
},
"settings.troubleshooting.stage.restoring": {
"message": "正在恢复先前的日志级别…"
"message": "正在捕获调试日志"
},
"settings.troubleshooting.stage.bundling": {
"message": "正在生成调试包…"

View File

@@ -9,6 +9,9 @@ import (
"path/filepath"
"runtime"
"strings"
"time"
"google.golang.org/protobuf/types/known/durationpb"
"github.com/netbirdio/netbird/client/proto"
"github.com/netbirdio/netbird/version"
@@ -113,6 +116,28 @@ func (s *Debug) RegisterUILog(ctx context.Context, path string) error {
return err
}
func (s *Debug) StartBundleCapture(ctx context.Context, timeoutSeconds int32) error {
cli, err := s.conn.Client()
if err != nil {
return err
}
req := &proto.StartBundleCaptureRequest{}
if timeoutSeconds > 0 {
req.Timeout = durationpb.New(time.Duration(timeoutSeconds) * time.Second)
}
_, err = cli.StartBundleCapture(ctx, req)
return err
}
func (s *Debug) StopBundleCapture(ctx context.Context) error {
cli, err := s.conn.Client()
if err != nil {
return err
}
_, err = cli.StopBundleCapture(ctx, &proto.StopBundleCaptureRequest{})
return err
}
func (s *Debug) SetLogLevel(ctx context.Context, lvl LogLevel) error {
cli, err := s.conn.Client()
if err != nil {