mirror of
https://github.com/Nezumi-2711/9router.git
synced 2026-09-23 04:09:49 +00:00
Enhance token refresh logic and improve MITM server handling
- Introduced a caching mechanism for in-flight token refresh requests to prevent race conditions and reduce unnecessary API calls. - Added error handling for unrecoverable refresh errors, ensuring that the application can gracefully handle token reuse and invalidation scenarios. - Updated the MITM server management to handle port 443 conflicts, allowing users to kill processes occupying the port before starting the server. - Improved user feedback in the MitmServerCard component regarding port conflicts and admin privileges. - Refactored the ComboList component to streamline the display of media provider combos. This update aims to enhance the reliability and user experience of the token management and MITM functionalities.
This commit is contained in:
@@ -19,6 +19,7 @@ export default function MitmServerCard({ apiKeys, cloudEnabled, onStatusChange }
|
||||
const [modalError, setModalError] = useState(null);
|
||||
const [actionError, setActionError] = useState(null);
|
||||
const [mitmRouterBaseUrl, setMitmRouterBaseUrl] = useState(DEFAULT_MITM_ROUTER_BASE);
|
||||
const [port443Conflict, setPort443Conflict] = useState(null);
|
||||
|
||||
const serverIsWindows = status?.isWin === true;
|
||||
const canRunWithoutPassword = serverIsWindows || status?.hasCachedPassword || status?.needsSudoPassword === false;
|
||||
@@ -50,6 +51,8 @@ export default function MitmServerCard({ apiKeys, cloudEnabled, onStatusChange }
|
||||
|
||||
const handleAction = (action) => {
|
||||
setActionError(null);
|
||||
// Wait for status to load before deciding whether to show sudo modal
|
||||
if (!status) return;
|
||||
if (canRunWithoutPassword) {
|
||||
doAction(action, "");
|
||||
} else {
|
||||
@@ -59,7 +62,7 @@ export default function MitmServerCard({ apiKeys, cloudEnabled, onStatusChange }
|
||||
}
|
||||
};
|
||||
|
||||
const doAction = async (action, password) => {
|
||||
const doAction = async (action, password, forceKillPort443 = false) => {
|
||||
setLoading(true);
|
||||
setActionError(null);
|
||||
try {
|
||||
@@ -81,6 +84,7 @@ export default function MitmServerCard({ apiKeys, cloudEnabled, onStatusChange }
|
||||
apiKey: keyToUse,
|
||||
sudoPassword: password,
|
||||
mitmRouterBaseUrl: mitmRouterBaseUrl.trim() || DEFAULT_MITM_ROUTER_BASE,
|
||||
forceKillPort443,
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
@@ -92,11 +96,17 @@ export default function MitmServerCard({ apiKeys, cloudEnabled, onStatusChange }
|
||||
}
|
||||
if (!res.ok) {
|
||||
const data = await res.json().catch(() => ({}));
|
||||
if (data.code === "PORT_443_BUSY" && data.portOwner) {
|
||||
setShowPasswordModal(false);
|
||||
setPort443Conflict({ owner: data.portOwner, password });
|
||||
return;
|
||||
}
|
||||
setActionError(data.error || `Failed to ${action} MITM server`);
|
||||
return;
|
||||
}
|
||||
setShowPasswordModal(false);
|
||||
setSudoPassword("");
|
||||
setPort443Conflict(null);
|
||||
await fetchStatus();
|
||||
} catch (e) {
|
||||
setActionError(e.message || "Network error");
|
||||
@@ -106,6 +116,11 @@ export default function MitmServerCard({ apiKeys, cloudEnabled, onStatusChange }
|
||||
}
|
||||
};
|
||||
|
||||
const handleKillAndStart = () => {
|
||||
const pwd = port443Conflict?.password || "";
|
||||
doAction("start", pwd, true);
|
||||
};
|
||||
|
||||
const handleConfirmPassword = () => {
|
||||
if (!sudoPassword.trim()) {
|
||||
setModalError("Sudo password is required");
|
||||
@@ -218,7 +233,7 @@ export default function MitmServerCard({ apiKeys, cloudEnabled, onStatusChange }
|
||||
) : (
|
||||
<button
|
||||
onClick={() => handleAction("start")}
|
||||
disabled={loading || (serverIsWindows && !isAdmin)}
|
||||
disabled={loading || !status || (serverIsWindows && !isAdmin)}
|
||||
title={serverIsWindows && !isAdmin ? "Administrator required" : undefined}
|
||||
className="flex w-full items-center justify-center gap-1.5 rounded-lg border border-primary/30 bg-primary/10 px-4 py-2 text-xs font-medium text-primary transition-colors hover:bg-primary/20 disabled:opacity-50 sm:w-auto sm:py-1.5"
|
||||
>
|
||||
@@ -282,6 +297,33 @@ export default function MitmServerCard({ apiKeys, cloudEnabled, onStatusChange }
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Port 443 Conflict Modal */}
|
||||
{port443Conflict && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm">
|
||||
<div className="mx-4 flex w-full max-w-md flex-col gap-4 rounded-xl border border-border bg-surface p-5 shadow-xl sm:p-6">
|
||||
<h3 className="font-semibold text-text-main">Port 443 Already In Use</h3>
|
||||
<div className="flex items-start gap-3 p-3 bg-yellow-500/10 border border-yellow-500/30 rounded-lg">
|
||||
<span className="material-symbols-outlined text-yellow-500 text-[20px]">warning</span>
|
||||
<div className="flex flex-col gap-1 text-xs text-text-muted">
|
||||
<p>Port 443 đang bị process khác chiếm:</p>
|
||||
<p className="font-mono text-text-main" data-i18n-skip="true">
|
||||
{port443Conflict.owner.name} (PID {port443Conflict.owner.pid})
|
||||
</p>
|
||||
<p>Kill process này để chạy MITM Server?</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-end gap-2">
|
||||
<Button variant="ghost" size="sm" onClick={() => { setPort443Conflict(null); setLoading(false); }} disabled={loading}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant="primary" size="sm" onClick={handleKillAndStart} loading={loading}>
|
||||
Kill & Start
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ export default function MitmToolCard({
|
||||
dnsActive,
|
||||
hasCachedPassword,
|
||||
needsSudoPassword,
|
||||
isWin,
|
||||
apiKeys,
|
||||
activeProviders,
|
||||
hasActiveProviders,
|
||||
@@ -37,7 +38,7 @@ export default function MitmToolCard({
|
||||
const [currentEditingAlias, setCurrentEditingAlias] = useState(null);
|
||||
|
||||
const mitmHosts = TOOL_HOSTS[tool.id] ?? [];
|
||||
const canRunWithoutPassword = hasCachedPassword || needsSudoPassword === false;
|
||||
const canRunWithoutPassword = isWin || hasCachedPassword || needsSudoPassword === false;
|
||||
|
||||
useEffect(() => {
|
||||
if (isExpanded) loadSavedMappings();
|
||||
|
||||
Reference in New Issue
Block a user