mirror of
https://github.com/shuaiplus/nodewarden.git
synced 2026-06-20 13:00:39 +00:00
fix: improve network status handling and probe logic
This commit is contained in:
@@ -23,7 +23,6 @@ export default function NetworkStatusBadge() {
|
||||
const Icon = status === 'online' ? Wifi : WifiOff;
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
let timer = 0;
|
||||
|
||||
const checkService = async () => {
|
||||
@@ -31,10 +30,7 @@ export default function NetworkStatusBadge() {
|
||||
setCurrentNetworkStatus('offline');
|
||||
return;
|
||||
}
|
||||
const reachable = await probeNodeWardenService();
|
||||
if (!cancelled) {
|
||||
setCurrentNetworkStatus(reachable ? 'online' : 'offline');
|
||||
}
|
||||
await probeNodeWardenService();
|
||||
};
|
||||
|
||||
const scheduleNextCheck = () => {
|
||||
@@ -62,7 +58,6 @@ export default function NetworkStatusBadge() {
|
||||
document.addEventListener('visibilitychange', handleVisibilityChange);
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
unsubscribe();
|
||||
window.clearTimeout(timer);
|
||||
window.removeEventListener('online', handleOnline);
|
||||
|
||||
@@ -9,6 +9,7 @@ import type {
|
||||
TokenSuccess,
|
||||
} from '../types';
|
||||
import type { AccountPasskeyAssertion, AccountPasskeyPrfKeySet } from '../account-passkeys';
|
||||
import { recordNodeWardenReachable, recordNodeWardenUnreachable } from '../network-status';
|
||||
import { parseJson, type AuthedFetch, type SessionSetter } from './shared';
|
||||
|
||||
const SESSION_KEY = 'nodewarden.web.session.v4';
|
||||
@@ -474,6 +475,7 @@ export function createAuthedFetch(getSession: () => SessionState | null, setSess
|
||||
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
||||
try {
|
||||
const response = await fetch(input, { ...init, headers });
|
||||
recordNodeWardenReachable();
|
||||
if (response.status !== 429 && (response.status < 500 || response.status >= 600)) {
|
||||
return response;
|
||||
}
|
||||
@@ -484,6 +486,7 @@ export function createAuthedFetch(getSession: () => SessionState | null, setSess
|
||||
} catch (error) {
|
||||
lastError = error;
|
||||
if (attempt === maxAttempts - 1) {
|
||||
recordNodeWardenUnreachable();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,20 +279,16 @@ export async function hydrateLockedSession(
|
||||
fallbackProfile: Profile | null = null
|
||||
): Promise<{ session: SessionState | null; profile: Profile | null }> {
|
||||
const hasOfflineUnlock = hasOfflineUnlockRecord(session.email);
|
||||
let serviceReachable = true;
|
||||
if (hasOfflineUnlock) {
|
||||
serviceReachable = await probeNodeWardenService();
|
||||
if (!serviceReachable) {
|
||||
if (hasOfflineUnlock && browserReportsOffline()) {
|
||||
return {
|
||||
session,
|
||||
profile: fallbackProfile || loadOfflineProfileSnapshot(session.email),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const refreshedSession = await maybeRefreshSession(session);
|
||||
if (!refreshedSession?.accessToken) {
|
||||
if (hasOfflineUnlock && !serviceReachable) {
|
||||
if (hasOfflineUnlock && (browserReportsOffline() || !(await probeNodeWardenService()))) {
|
||||
return {
|
||||
session,
|
||||
profile: fallbackProfile || loadOfflineProfileSnapshot(session.email),
|
||||
@@ -571,15 +567,9 @@ export async function performUnlock(
|
||||
}
|
||||
};
|
||||
|
||||
if (hasOfflineUnlock) {
|
||||
if (browserReportsOffline()) {
|
||||
if (hasOfflineUnlock && browserReportsOffline()) {
|
||||
return unlockOffline();
|
||||
}
|
||||
const serviceReachable = await probeNodeWardenService();
|
||||
if (!serviceReachable) {
|
||||
return unlockOffline();
|
||||
}
|
||||
}
|
||||
|
||||
let token: TokenSuccess | { TwoFactorProviders?: unknown; error_description?: string; error?: string };
|
||||
try {
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
export type NetworkStatus = 'online' | 'offline';
|
||||
|
||||
const STATUS_PROBE_TIMEOUT_MS = 3500;
|
||||
const STATUS_PROBE_TIMEOUT_MS = 8000;
|
||||
const STATUS_PROBE_CACHE_MS = 5000;
|
||||
const PROBE_FAILURES_BEFORE_OFFLINE = 2;
|
||||
const listeners = new Set<(status: NetworkStatus) => void>();
|
||||
let currentStatus: NetworkStatus = getInitialNetworkStatus();
|
||||
let pendingProbe: Promise<boolean> | null = null;
|
||||
let lastProbeAt = 0;
|
||||
let lastProbeResult = currentStatus === 'online';
|
||||
let consecutiveProbeFailures = 0;
|
||||
|
||||
export function browserReportsOffline(): boolean {
|
||||
return typeof navigator !== 'undefined' && navigator.onLine === false;
|
||||
@@ -35,8 +37,23 @@ export function subscribeNetworkStatus(listener: (status: NetworkStatus) => void
|
||||
};
|
||||
}
|
||||
|
||||
export function recordNodeWardenReachable(): void {
|
||||
consecutiveProbeFailures = 0;
|
||||
lastProbeResult = true;
|
||||
setCurrentNetworkStatus('online');
|
||||
}
|
||||
|
||||
export function recordNodeWardenUnreachable(): void {
|
||||
lastProbeResult = false;
|
||||
consecutiveProbeFailures += 1;
|
||||
if (browserReportsOffline() || consecutiveProbeFailures >= PROBE_FAILURES_BEFORE_OFFLINE) {
|
||||
setCurrentNetworkStatus('offline');
|
||||
}
|
||||
}
|
||||
|
||||
export async function probeNodeWardenService(): Promise<boolean> {
|
||||
if (browserReportsOffline()) {
|
||||
consecutiveProbeFailures = PROBE_FAILURES_BEFORE_OFFLINE;
|
||||
setCurrentNetworkStatus('offline');
|
||||
return false;
|
||||
}
|
||||
@@ -68,8 +85,11 @@ export async function probeNodeWardenService(): Promise<boolean> {
|
||||
.catch(() => false)
|
||||
.then((result) => {
|
||||
lastProbeAt = Date.now();
|
||||
lastProbeResult = result;
|
||||
setCurrentNetworkStatus(result ? 'online' : 'offline');
|
||||
if (result) {
|
||||
recordNodeWardenReachable();
|
||||
} else {
|
||||
recordNodeWardenUnreachable();
|
||||
}
|
||||
return result;
|
||||
})
|
||||
.finally(() => {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { Send } from './types';
|
||||
import { getCurrentNetworkStatus } from './network-status';
|
||||
import type { DecryptSendsArgs, DecryptVaultCoreArgs, DecryptVaultCoreResult } from './vault-decrypt';
|
||||
|
||||
type WorkerSuccess<T> = { id: number; ok: true; result: T };
|
||||
@@ -13,7 +12,6 @@ const pending = new Map<number, { resolve: (value: any) => void; reject: (error:
|
||||
function getWorker(): Worker | null {
|
||||
if (typeof Worker === 'undefined') return null;
|
||||
if (worker) return worker;
|
||||
if (getCurrentNetworkStatus() === 'offline') return null;
|
||||
worker = new Worker(new URL('../workers/vault-decrypt.worker.ts', import.meta.url), { type: 'module' });
|
||||
worker.addEventListener('message', (event: MessageEvent<WorkerResponse<unknown>>) => {
|
||||
const message = event.data;
|
||||
|
||||
Reference in New Issue
Block a user