mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-11 03:06:54 +00:00
fix: Distributed API proxy memory leak, node switcher refresh, and copy button
Proxy memory leak (MaxListenersExceededWarning + DEP0060): createProxyMiddleware was instantiated inside the request handler on every single API call. Each new instance registered fresh 'close' listeners on the HTTP server and re-ran the http-proxy util._extend deprecated path. After ~10 requests the MaxListeners threshold was breached. Fix: declare ONE global remoteNodeProxy at startup using the router option to dynamically resolve the target URL per request. Listeners are registered once. ECONNREFUSED errors are caught in the on.error handler and returned as structured 502 JSON. Node switcher "nothing happens": EditorLayout had a single useEffect([], []) that called refreshStacks() once on mount. Changing the active node updated NodeContext state and localStorage but nothing re-triggered the stack list fetch. Fix: split into two effects — notifications polling (no dependency) and a stack-refresh effect keyed on activeNode?.id. When the node changes, stale editor/container/file state is cleared and the stacks for the new node are fetched. Copy button silently failing: navigator.clipboard.writeText() throws DOMException in non-HTTPS / non-localhost contexts (e.g. http://192.168.x.x). The uncaught async exception silently swallowed the success toast and state update. Fix: wrapped in try/catch with an execCommand('copy') textarea fallback and a final error toast if both fail.
This commit is contained in:
@@ -147,12 +147,27 @@ export default function EditorLayout() {
|
||||
}
|
||||
};
|
||||
|
||||
// Notification polling — independent of active node, runs once on mount
|
||||
useEffect(() => {
|
||||
refreshStacks();
|
||||
fetchNotifications();
|
||||
const notificationInterval = setInterval(fetchNotifications, 5000);
|
||||
return () => clearInterval(notificationInterval);
|
||||
}, []);
|
||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
// Re-fetch stacks whenever the active node changes (or becomes available on mount).
|
||||
// Also clears any stale editor/container state that belonged to the previous node.
|
||||
useEffect(() => {
|
||||
if (!activeNode) return;
|
||||
setSelectedFile(null);
|
||||
setContent('');
|
||||
setOriginalContent('');
|
||||
setEnvContent('');
|
||||
setOriginalEnvContent('');
|
||||
setContainers([]);
|
||||
setIsEditing(false);
|
||||
setActiveView('dashboard');
|
||||
refreshStacks();
|
||||
}, [activeNode?.id]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const fetchNotifications = async () => {
|
||||
try {
|
||||
|
||||
@@ -177,10 +177,31 @@ export function NodeManager() {
|
||||
|
||||
const copyToken = async () => {
|
||||
if (!generatedToken) return;
|
||||
await navigator.clipboard.writeText(generatedToken);
|
||||
setTokenCopied(true);
|
||||
toast.success('Token copied to clipboard');
|
||||
setTimeout(() => setTokenCopied(false), 2000);
|
||||
try {
|
||||
// Clipboard API requires a secure context (HTTPS or localhost)
|
||||
await navigator.clipboard.writeText(generatedToken);
|
||||
setTokenCopied(true);
|
||||
toast.success('Token copied to clipboard');
|
||||
setTimeout(() => setTokenCopied(false), 2000);
|
||||
} catch {
|
||||
// Fallback for HTTP / non-localhost deployments where Clipboard API is unavailable
|
||||
try {
|
||||
const ta = document.createElement('textarea');
|
||||
ta.value = generatedToken;
|
||||
ta.style.position = 'fixed';
|
||||
ta.style.opacity = '0';
|
||||
document.body.appendChild(ta);
|
||||
ta.focus();
|
||||
ta.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(ta);
|
||||
setTokenCopied(true);
|
||||
toast.success('Token copied to clipboard');
|
||||
setTimeout(() => setTokenCopied(false), 2000);
|
||||
} catch {
|
||||
toast.error('Could not copy automatically — please select and copy the token manually.');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusBadge = (status: string) => {
|
||||
|
||||
Reference in New Issue
Block a user