Files
netbird/client/ui/frontend/src/lib/errors.ts
Maycon Santos 91acb8147c [management,client] 0.75.0 release with new desktop UI (#6473)
- **Wails v3 application** (`client/ui`) with a React + TypeScript + Tailwind frontend replacing the Fyne UI: main connection view, exit-node switcher, networks/peers browser with detail panels, profile management, settings (general, network, SSH, security, troubleshooting, appearance), debug-bundle creation, and a first-run welcome flow.
- **Internationalization**: go-i18n bundle with 9 locales (en, de, es, fr, hu, it, pt, ru, zh-CN) shared between the tray and the frontend.
- **New system tray** implementation with per-platform theme-aware icons, including a native XEmbed host for Linux (`xembed_tray_linux.c`) and a Linux theme watcher.
- **Session handling**: auth session watcher (`client/internal/auth/sessionwatch`), pending login flow, session-expiration dialog and tray notifications, and `netbird login` improvements.
- **Daemon API extensions** (`daemon.proto`): status stream subscription, event stream, networks/exit-node selection endpoints, and richer full status — with probe throttling on the daemon side to protect against UI-driven request storms.
- **UI preferences store** persisted per profile, autostart management via the daemon (single source of truth in HKCU on Windows).
- **Build system**: Taskfile-based builds per platform (macOS, Linux, Windows), Docker cross-compilation images, MSIX/NSIS/nfpm/AppImage packaging, and a new `frontend-ui` CI workflow.

Co-authored-by: Zoltan Papp <zoltan.pmail@gmail.com>
Co-authored-by: Eduard Gert <kontakt@eduardgert.de>
Co-authored-by: braginini <bangvalo@gmail.com>
Co-authored-by: Pascal Fischer <32096965+pascal-fischer@users.noreply.github.com>
Co-authored-by: riccardom <riccardomanfrin@gmail.com>
2026-07-06 13:47:16 +02:00

61 lines
2.0 KiB
TypeScript

import { WindowManager } from "@bindings/services";
type ClassifiedError = { short: string; long: string };
const asObject = (v: unknown): Record<string, unknown> | null =>
v && typeof v === "object" ? (v as Record<string, unknown>) : null;
const parseJsonObject = (s: unknown): Record<string, unknown> | null => {
if (typeof s !== "string") return null;
const t = s.trim();
if (!t.startsWith("{") || !t.endsWith("}")) return null;
try {
return asObject(JSON.parse(t));
} catch {
return null;
}
};
const toWailsEnvelope = (e: unknown): Record<string, unknown> | null => {
const obj = asObject(e);
if (!obj) return null;
return asObject(obj.cause) ?? parseJsonObject(obj.message);
};
// Read { short, long } from wherever the classified error sits in the envelope
const toClassifiedError = (v: unknown): ClassifiedError | null => {
const o = asObject(v);
if (!o) return null;
const short = typeof o.short === "string" ? o.short : "";
const long = typeof o.long === "string" ? o.long : "";
return short || long ? { short, long } : null;
};
export const formatErrorMessage = (e: unknown): string => {
const envelope = toWailsEnvelope(e);
// Prefer the structured { short, long } the daemon classifier produced.
const classified = toClassifiedError(envelope?.cause) ?? toClassifiedError(envelope);
if (classified) {
const { short, long } = classified;
if (short && long && long !== short) return `${short} Details: ${long}`;
if (short) return short;
if (long) return long;
}
// Unclassified (a service returned the raw daemon error)
const message = envelope?.message;
if (typeof message === "string" && message) return message;
if (e instanceof Error) return e.message;
return String(e);
};
export type ErrorDialogOptions = {
Title: string;
Message: string;
};
export function errorDialog(options: ErrorDialogOptions): Promise<void> {
return WindowManager.OpenError(options.Title, options.Message);
}