dev-1.7.0 #294

Merged
ZacharyZcR merged 73 commits from main into dev-1.7.0 2025-09-25 04:56:32 +00:00
3 changed files with 95 additions and 2 deletions
Showing only changes of commit 5f5397b924 - Show all commits
+37 -2
View File
@@ -93,11 +93,11 @@ function scheduleSessionCleanup(sessionId: string) {
// Clear existing timeout // Clear existing timeout
if (session.timeout) clearTimeout(session.timeout); if (session.timeout) clearTimeout(session.timeout);
// Set new timeout for 10 minutes of inactivity // Increase timeout to 30 minutes of inactivity
session.timeout = setTimeout(() => { session.timeout = setTimeout(() => {
fileLogger.info(`Cleaning up inactive SSH session: ${sessionId}`); fileLogger.info(`Cleaning up inactive SSH session: ${sessionId}`);
cleanupSession(sessionId); cleanupSession(sessionId);
}, 10 * 60 * 1000); // 10 minutes }, 30 * 60 * 1000); // 30 minutes - increased from 10 minutes
} }
} }
@@ -321,6 +321,41 @@ app.get("/ssh/file_manager/ssh/status", (req, res) => {
res.json({ status: "success", connected: isConnected }); res.json({ status: "success", connected: isConnected });
}); });
// SSH keepalive endpoint - extends session timeout and verifies connection
app.post("/ssh/file_manager/ssh/keepalive", (req, res) => {
const { sessionId } = req.body;
if (!sessionId) {
return res.status(400).json({ error: "Session ID is required" });
}
const session = sshSessions[sessionId];
if (!session || !session.isConnected) {
return res.status(400).json({
error: "SSH session not found or not connected",
connected: false
});
}
// Update last active time and reschedule cleanup
session.lastActive = Date.now();
scheduleSessionCleanup(sessionId);
fileLogger.debug(`SSH session keepalive: ${sessionId}`, {
operation: "ssh_keepalive",
sessionId,
lastActive: session.lastActive,
});
res.json({
status: "success",
connected: true,
message: "Session keepalive successful",
lastActive: session.lastActive
});
});
app.get("/ssh/file_manager/ssh/listFiles", (req, res) => { app.get("/ssh/file_manager/ssh/listFiles", (req, res) => {
const sessionId = req.query.sessionId as string; const sessionId = req.query.sessionId as string;
const sshConn = sshSessions[sessionId]; const sshConn = sshSessions[sessionId];
@@ -38,6 +38,7 @@ import {
moveSSHItem, moveSSHItem,
connectSSH, connectSSH,
getSSHStatus, getSSHStatus,
keepSSHAlive,
identifySSHSymlink, identifySSHSymlink,
addRecentFile, addRecentFile,
addPinnedFile, addPinnedFile,
@@ -144,6 +145,36 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) {
sshHost: currentHost!, sshHost: currentHost!,
}); });
// SSH keepalive function
const startKeepalive = useCallback(() => {
if (!sshSessionId) return;
// Clear existing timer
if (keepaliveTimerRef.current) {
clearInterval(keepaliveTimerRef.current);
}
// Send keepalive every 5 minutes (300000ms)
keepaliveTimerRef.current = setInterval(async () => {
if (sshSessionId) {
try {
await keepSSHAlive(sshSessionId);
console.log("SSH keepalive sent successfully");
} catch (error) {
console.error("SSH keepalive failed:", error);
// If keepalive fails, session might be dead - could trigger reconnect here
}
}
}, 5 * 60 * 1000); // 5 minutes
}, [sshSessionId]);
const stopKeepalive = useCallback(() => {
if (keepaliveTimerRef.current) {
clearInterval(keepaliveTimerRef.current);
keepaliveTimerRef.current = null;
}
}, []);
// Initialize SSH connection // Initialize SSH connection
useEffect(() => { useEffect(() => {
if (currentHost) { if (currentHost) {
@@ -151,6 +182,20 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) {
} }
}, [currentHost]); }, [currentHost]);
// Start/stop keepalive based on SSH session
useEffect(() => {
if (sshSessionId) {
startKeepalive();
} else {
stopKeepalive();
}
// Cleanup on unmount
return () => {
stopKeepalive();
};
}, [sshSessionId, startKeepalive, stopKeepalive]);
// Track if initial directory load is done to prevent duplicate loading // Track if initial directory load is done to prevent duplicate loading
const initialLoadDoneRef = useRef(false); const initialLoadDoneRef = useRef(false);
// Track last path change to prevent rapid navigation issues // Track last path change to prevent rapid navigation issues
@@ -158,6 +203,8 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) {
const pathChangeTimerRef = useRef<NodeJS.Timeout | null>(null); const pathChangeTimerRef = useRef<NodeJS.Timeout | null>(null);
// Track current loading request to handle cancellation // Track current loading request to handle cancellation
const currentLoadingPathRef = useRef<string>(""); const currentLoadingPathRef = useRef<string>("");
// SSH keepalive timer
const keepaliveTimerRef = useRef<NodeJS.Timeout | null>(null);
// Debounced directory loading for path changes // Debounced directory loading for path changes
const debouncedLoadDirectory = useCallback((path: string) => { const debouncedLoadDirectory = useCallback((path: string) => {
+11
View File
@@ -1002,6 +1002,17 @@ export async function getSSHStatus(
} }
} }
export async function keepSSHAlive(sessionId: string): Promise<any> {
try {
const response = await fileManagerApi.post("/ssh/keepalive", {
sessionId,
});
return response.data;
} catch (error) {
handleApiError(error, "SSH keepalive");
}
}
export async function listSSHFiles( export async function listSSHFiles(
sessionId: string, sessionId: string,
path: string, path: string,