mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-14 02:39:06 +02:00
Merge branch 'main' into file-share
# Conflicts: # client/ios/NetBirdSDK/client.go
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
# docker run --rm -v $(pwd):/app wails-cross windows amd64
|
||||
# docker run --rm -v $(pwd):/app wails-cross windows arm64
|
||||
|
||||
FROM golang:1.25-bookworm
|
||||
FROM golang:1.26.7-bookworm
|
||||
|
||||
ARG TARGETARCH
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Multi-stage build for minimal image size
|
||||
|
||||
# Build stage
|
||||
FROM golang:alpine AS builder
|
||||
FROM golang:1.26.7-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
@@ -18,6 +18,11 @@ import { formatRemaining } from "@/lib/formatters";
|
||||
const DEFAULT_SECONDS = 360;
|
||||
const WINDOW_WIDTH = 360;
|
||||
const SOON_THRESHOLD_SECONDS = 60 * 60;
|
||||
const DEADLINE_TOLERANCE_MS = 5 * 1000;
|
||||
// The final-warning deadline reaches the Go side as RFC3339 truncated to whole
|
||||
// seconds, while the status snapshot carries millisecond precision, so an
|
||||
// unchanged deadline can look up to 999 ms newer than the exact URL value.
|
||||
const EXACT_DEADLINE_TOLERANCE_MS = 999;
|
||||
|
||||
export default function SessionExpirationDialog() {
|
||||
const { t } = useTranslation();
|
||||
@@ -29,11 +34,19 @@ export default function SessionExpirationDialog() {
|
||||
const n = Number.parseInt(raw, 10);
|
||||
return Number.isFinite(n) && n > 0 ? n : DEFAULT_SECONDS;
|
||||
}, [params]);
|
||||
const initialDeadline = useMemo(() => {
|
||||
const raw = params.get("deadline");
|
||||
if (!raw) return null;
|
||||
const n = Number.parseInt(raw, 10);
|
||||
return Number.isFinite(n) && n > 0 ? n : null;
|
||||
}, [params]);
|
||||
|
||||
const [remaining, setRemaining] = useState(initialSeconds);
|
||||
const [busy, setBusy] = useState(false);
|
||||
const busyRef = useRef(busy);
|
||||
busyRef.current = busy;
|
||||
const openedDeadlineRef = useRef(initialDeadline ?? Date.now() + initialSeconds * 1000);
|
||||
const exactDeadlineRef = useRef(initialDeadline !== null);
|
||||
const expired = remaining <= 0;
|
||||
const expiredRef = useRef(expired);
|
||||
expiredRef.current = expired;
|
||||
@@ -45,23 +58,45 @@ export default function SessionExpirationDialog() {
|
||||
|
||||
useEffect(() => {
|
||||
setRemaining(initialSeconds);
|
||||
}, [initialSeconds]);
|
||||
openedDeadlineRef.current = initialDeadline ?? Date.now() + initialSeconds * 1000;
|
||||
exactDeadlineRef.current = initialDeadline !== null;
|
||||
}, [initialSeconds, initialDeadline]);
|
||||
|
||||
// Recompute from the absolute deadline instead of decrementing per tick: webview
|
||||
// timers get suspended for tens of seconds (App Nap / hidden-window throttling),
|
||||
// so a tick counter drifts behind the wall clock by the suspended time.
|
||||
useEffect(() => {
|
||||
const id = globalThis.setInterval(() => {
|
||||
setRemaining((s) => (s <= 1 ? 0 : s - 1));
|
||||
setRemaining(Math.max(0, Math.ceil((openedDeadlineRef.current - Date.now()) / 1000)));
|
||||
}, 1000);
|
||||
return () => globalThis.clearInterval(id);
|
||||
}, [initialSeconds]);
|
||||
|
||||
// Auto-close only when the session was actually renewed elsewhere (tray action, CLI,
|
||||
// main window): the daemon keeps emitting Connected snapshots regardless of session
|
||||
// state, so the signal is the deadline jumping past the one this dialog was opened for.
|
||||
// With the exact deadline from the URL any jump past its sub-second precision loss
|
||||
// counts; the seconds-derived fallback needs a wider tolerance for the Go-side
|
||||
// truncation and mount latency.
|
||||
// Don't auto-close while busy (aborts our WaitExtend) or expired (hides the state).
|
||||
useEffect(() => {
|
||||
const off = Events.On("netbird:status", (ev: { data: { status?: string } }) => {
|
||||
if (busyRef.current || expiredRef.current) return;
|
||||
if (ev?.data?.status === "Connected") {
|
||||
WindowManager.CloseSessionExpiration().catch(console.error);
|
||||
}
|
||||
});
|
||||
const off = Events.On(
|
||||
"netbird:status",
|
||||
(ev: { data: { status?: string; sessionExpiresAt?: string | null } }) => {
|
||||
if (busyRef.current || expiredRef.current) return;
|
||||
if (ev?.data?.status !== "Connected") return;
|
||||
const raw = ev?.data?.sessionExpiresAt;
|
||||
if (!raw) return;
|
||||
const renewed = Date.parse(raw);
|
||||
if (!Number.isFinite(renewed)) return;
|
||||
const tolerance = exactDeadlineRef.current
|
||||
? EXACT_DEADLINE_TOLERANCE_MS
|
||||
: DEADLINE_TOLERANCE_MS;
|
||||
if (renewed - openedDeadlineRef.current > tolerance) {
|
||||
WindowManager.CloseSessionExpiration().catch(console.error);
|
||||
}
|
||||
},
|
||||
);
|
||||
return () => {
|
||||
off();
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"languages": [
|
||||
{"code": "en", "displayName": "English (US)", "englishName": "English (US)"},
|
||||
{"code": "uk", "displayName": "Українська", "englishName": "Ukrainian"},
|
||||
{"code": "de", "displayName": "Deutsch", "englishName": "German"},
|
||||
{"code": "hu", "displayName": "Magyar", "englishName": "Hungarian"},
|
||||
{"code": "ru", "displayName": "Русский", "englishName": "Russian"},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -292,11 +292,15 @@ func (s *WindowManager) CloseBrowserLogin() {
|
||||
}
|
||||
|
||||
// OpenSessionExpiration shows the countdown warning on the cursor's display; seconds seeds
|
||||
// the countdown. Singleton, destroyed on close.
|
||||
func (s *WindowManager) OpenSessionExpiration(seconds int) {
|
||||
// the countdown and deadlineUnixMilli (0 when unknown) is the absolute deadline the dialog
|
||||
// compares renewal snapshots against. Singleton, destroyed on close.
|
||||
func (s *WindowManager) OpenSessionExpiration(seconds int, deadlineUnixMilli int64) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
startURL := "/#/dialog/session-expiration?seconds=" + strconv.Itoa(seconds)
|
||||
if deadlineUnixMilli > 0 {
|
||||
startURL += "&deadline=" + strconv.FormatInt(deadlineUnixMilli, 10)
|
||||
}
|
||||
if s.sessionExpiration == nil {
|
||||
opts := DialogWindowOptions("session-expiration", s.title("window.title.sessionExpiration"), startURL, s.linuxIcon)
|
||||
opts.Screen = s.getScreenBasedOnCursorPosition()
|
||||
|
||||
@@ -81,7 +81,8 @@ func (t *Tray) onSystemEvent(ev *application.CustomEvent) {
|
||||
|
||||
if se.Metadata != nil && se.Metadata[authsession.MetaWarning] == "true" {
|
||||
if se.Metadata[authsession.MetaFinal] == "true" {
|
||||
t.openSessionExpiration()
|
||||
deadline, _ := authsession.ParseExpiresAt(se.Metadata[authsession.MetaExpiresAt])
|
||||
t.openSessionExpiration(deadline)
|
||||
return
|
||||
}
|
||||
t.notifySessionWarning(
|
||||
|
||||
@@ -278,12 +278,23 @@ func (t *Tray) dismissSessionWarning() {
|
||||
}
|
||||
|
||||
// openSessionExpiration fires the fallback dialog when the earlier warning notification wasn't dismissed.
|
||||
// Idempotent on the WindowManager side.
|
||||
func (t *Tray) openSessionExpiration() {
|
||||
// deadline is the absolute expiry from the warning event's metadata; when zero (older daemon,
|
||||
// malformed metadata) the cached status-snapshot deadline fills in. Idempotent on the
|
||||
// WindowManager side.
|
||||
func (t *Tray) openSessionExpiration(deadline time.Time) {
|
||||
if t.svc.WindowManager == nil {
|
||||
return
|
||||
}
|
||||
t.svc.WindowManager.OpenSessionExpiration(finalWarningCountdownSeconds)
|
||||
if deadline.IsZero() {
|
||||
t.sessionMu.Lock()
|
||||
deadline = t.sessionExpiresAt
|
||||
t.sessionMu.Unlock()
|
||||
}
|
||||
var deadlineMs int64
|
||||
if !deadline.IsZero() {
|
||||
deadlineMs = deadline.UnixMilli()
|
||||
}
|
||||
t.svc.WindowManager.OpenSessionExpiration(finalWarningCountdownSeconds, deadlineMs)
|
||||
}
|
||||
|
||||
// openSessionExtendFlow opens the SessionExpiration window seeded with the cached deadline's remaining time,
|
||||
@@ -304,5 +315,5 @@ func (t *Tray) openSessionExtendFlow() {
|
||||
if t.svc.WindowManager == nil {
|
||||
return
|
||||
}
|
||||
t.svc.WindowManager.OpenSessionExpiration(seconds)
|
||||
t.svc.WindowManager.OpenSessionExpiration(seconds, deadline.UnixMilli())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user