mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-06 07:12:12 +03:00
feat(ui): system proxy exit guard via beforeunload + sendBeacon (fix2)
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useRef, useState } from "react";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import type { InterceptedRequest } from "@/mitm/inspector/types";
|
||||
import { useTrafficStream } from "./hooks/useTrafficStream";
|
||||
import { useTrafficFilters } from "./hooks/useTrafficFilters";
|
||||
import { useResizablePanels } from "./hooks/useResizablePanels";
|
||||
import { useSessionRecorder } from "./hooks/useSessionRecorder";
|
||||
import { useSystemProxyExitGuard } from "./hooks/useSystemProxyExitGuard";
|
||||
import { CaptureModesToolbar } from "./components/CaptureModesToolbar";
|
||||
import { TopBarControls } from "./components/TopBarControls";
|
||||
import { RequestStreamingList } from "./components/RequestStreamingList";
|
||||
@@ -22,6 +23,26 @@ export function TrafficInspectorPageClient() {
|
||||
const [{ listWidth, collapsed }, { startDrag, toggleCollapse }] = useResizablePanels();
|
||||
const [streamState, streamActions] = useTrafficStream(filters);
|
||||
const recorder = useSessionRecorder();
|
||||
const [captureModes, setCaptureModes] = useState<{ systemProxy?: { applied: boolean } } | null>(
|
||||
null
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
fetch("/api/tools/traffic-inspector/capture-modes")
|
||||
.then((r) => (r.ok ? r.json() : null))
|
||||
.then((data: { systemProxy?: { applied: boolean } } | null) => {
|
||||
if (!cancelled) setCaptureModes(data);
|
||||
})
|
||||
.catch(() => {
|
||||
/* best-effort */
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, []);
|
||||
|
||||
useSystemProxyExitGuard({ applied: captureModes?.systemProxy?.applied ?? false });
|
||||
|
||||
const listContainerCallback = useCallback((el: HTMLDivElement | null) => {
|
||||
listContainerRef.current = el;
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
interface UseSystemProxyExitGuardOpts {
|
||||
applied: boolean; // current state (from GET capture-modes)
|
||||
endpoint?: string; // POST /capture-modes/system-proxy
|
||||
}
|
||||
|
||||
/**
|
||||
* On unmount / page hide / beforeunload, if system proxy is applied,
|
||||
* silently fires a revert request via navigator.sendBeacon (best-effort,
|
||||
* survives unload) AND attaches a beforeunload listener that prompts the
|
||||
* user with a native confirm dialog (browser default — text is ignored
|
||||
* by most browsers but the prompt itself appears).
|
||||
*/
|
||||
export function useSystemProxyExitGuard(opts: UseSystemProxyExitGuardOpts): void {
|
||||
// 1. Track latest 'applied' in a ref so the listener always sees fresh value
|
||||
const appliedRef = useRef(opts.applied);
|
||||
useEffect(() => {
|
||||
appliedRef.current = opts.applied;
|
||||
}, [opts.applied]);
|
||||
|
||||
useEffect(() => {
|
||||
const endpoint =
|
||||
opts.endpoint ?? "/api/tools/traffic-inspector/capture-modes/system-proxy";
|
||||
const body = JSON.stringify({ action: "revert" });
|
||||
const blob = new Blob([body], { type: "application/json" });
|
||||
|
||||
const beforeUnload = (e: BeforeUnloadEvent) => {
|
||||
if (!appliedRef.current) return;
|
||||
// Best-effort revert via sendBeacon (survives navigation)
|
||||
try {
|
||||
navigator.sendBeacon(endpoint, blob);
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
// Show confirmation prompt
|
||||
e.preventDefault();
|
||||
e.returnValue = "System-wide proxy still active — leave page anyway?";
|
||||
return e.returnValue;
|
||||
};
|
||||
|
||||
window.addEventListener("beforeunload", beforeUnload);
|
||||
return () => {
|
||||
window.removeEventListener("beforeunload", beforeUnload);
|
||||
// On component unmount (SPA navigation), fire revert too
|
||||
if (appliedRef.current) {
|
||||
try {
|
||||
navigator.sendBeacon(endpoint, blob);
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
};
|
||||
}, [opts.endpoint]);
|
||||
}
|
||||
Reference in New Issue
Block a user