From fdd1218b032cf784de2779db77102e8198df2b27 Mon Sep 17 00:00:00 2001 From: ZacharyZcR Date: Thu, 25 Sep 2025 07:49:48 +0800 Subject: [PATCH] FIX: Automatically cleanup deleted files from recent/pinned lists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit File Cleanup Implementation: - Detect file-not-found errors when opening files from recent/pinned lists - Automatically remove missing files from both recent and pinned file lists - Refresh sidebar to reflect updated lists immediately after cleanup - Prevent error dialogs from appearing when files are successfully cleaned up Backend Improvements: - Enhanced SSH file manager to return proper 404 status for missing files - Added fileNotFound flag in error responses for better error detection - Improved error categorization for file access failures Frontend Error Handling: - Added onFileNotFound callback prop to FileWindow component - Implemented handleFileNotFound function in FileManagerModern - Enhanced error detection logic to catch various "file not found" scenarios - Better error messages with internationalization support Translation Additions: - fileNotFoundAndRemoved: Notify user when file is cleaned up - failedToLoadFile: Generic file loading error message - serverErrorOccurred: Server error fallback message - Chinese translations for all new error messages Technical Details: - Uses existing removeRecentFile and removePinnedFile API calls - Triggers sidebar refresh via setSidebarRefreshTrigger - Maintains backward compatibility with existing error handling - Preserves error logging for debugging purposes 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- src/backend/ssh/file-manager.ts | 14 ++++++++-- src/locales/en/translation.json | 3 ++ src/locales/zh/translation.json | 3 ++ .../Apps/File Manager/FileManagerModern.tsx | 22 +++++++++++++++ .../File Manager/components/FileWindow.tsx | 28 +++++++++++++++++-- 5 files changed, 65 insertions(+), 5 deletions(-) diff --git a/src/backend/ssh/file-manager.ts b/src/backend/ssh/file-manager.ts index 9f5edf5b..7ccc7ac8 100644 --- a/src/backend/ssh/file-manager.ts +++ b/src/backend/ssh/file-manager.ts @@ -622,9 +622,19 @@ app.get("/ssh/file_manager/ssh/readFile", (req, res) => { fileLogger.error( `SSH readFile command failed with code ${code}: ${errorData.replace(/\n/g, " ").trim()}`, ); + + // Check if it's a "file not found" error + const isFileNotFound = + errorData.includes("No such file or directory") || + errorData.includes("cannot access") || + errorData.includes("not found"); + return res - .status(500) - .json({ error: `Command failed: ${errorData}` }); + .status(isFileNotFound ? 404 : 500) + .json({ + error: `Command failed: ${errorData}`, + fileNotFound: isFileNotFound + }); } res.json({ content: data, path: filePath }); diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 3fa9396b..6f0cfe58 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -860,6 +860,9 @@ "modified": "Modified", "largeFileWarning": "Large File Warning", "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", "fileSavedSuccessfully": "File saved successfully", "autoSaveFailed": "Auto-save failed", "fileAutoSaved": "File auto-saved", diff --git a/src/locales/zh/translation.json b/src/locales/zh/translation.json index 495f784b..a79490f2 100644 --- a/src/locales/zh/translation.json +++ b/src/locales/zh/translation.json @@ -769,6 +769,9 @@ "modified": "修改时间", "largeFileWarning": "大文件警告", "largeFileWarningDesc": "此文件大小为 {{size}},以文本形式打开可能会导致性能问题。", + "fileNotFoundAndRemoved": "文件 \"{{name}}\" 未找到,已从最近访问/固定文件中移除", + "failedToLoadFile": "加载文件失败:{{error}}", + "serverErrorOccurred": "服务器错误,请稍后重试。", "failedToDeleteItem": "删除项目失败", "itemRenamedSuccessfully": "{{type}}重命名成功", "failedToRenameItem": "重命名项目失败", diff --git a/src/ui/Desktop/Apps/File Manager/FileManagerModern.tsx b/src/ui/Desktop/Apps/File Manager/FileManagerModern.tsx index 1a880bf3..d6367ef4 100644 --- a/src/ui/Desktop/Apps/File Manager/FileManagerModern.tsx +++ b/src/ui/Desktop/Apps/File Manager/FileManagerModern.tsx @@ -43,6 +43,7 @@ import { addRecentFile, addPinnedFile, removePinnedFile, + removeRecentFile, addFolderShortcut, getPinnedFiles, } from "@/ui/main-axios.ts"; @@ -757,6 +758,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { sshHost={currentHost} initialX={offsetX} initialY={offsetY} + onFileNotFound={handleFileNotFound} /> ); @@ -1553,6 +1555,26 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { await handleFileOpen(file); } + // Handle file not found - cleanup from recent and pinned lists + async function handleFileNotFound(file: FileItem) { + if (!currentHost) return; + + try { + // Remove from recent files + await removeRecentFile(currentHost.id, file.path); + + // Remove from pinned files + await removePinnedFile(currentHost.id, file.path); + + // Trigger sidebar refresh to update the UI + setSidebarRefreshTrigger(prev => prev + 1); + + console.log(`Cleaned up missing file from recent/pinned lists: ${file.path}`); + } catch (error) { + console.error("Failed to cleanup missing file:", error); + } + } + // Clear createIntent when path changes useEffect(() => { diff --git a/src/ui/Desktop/Apps/File Manager/components/FileWindow.tsx b/src/ui/Desktop/Apps/File Manager/components/FileWindow.tsx index f2b2e7b9..09e6d5e5 100644 --- a/src/ui/Desktop/Apps/File Manager/components/FileWindow.tsx +++ b/src/ui/Desktop/Apps/File Manager/components/FileWindow.tsx @@ -10,6 +10,7 @@ import { connectSSH, } from "@/ui/main-axios"; import { toast } from "sonner"; +import { useTranslation } from "react-i18next"; interface FileItem { name: string; @@ -43,6 +44,7 @@ interface FileWindowProps { sshHost: SSHHost; initialX?: number; initialY?: number; + onFileNotFound?: (file: FileItem) => void; // Callback for when file is not found // readOnly parameter removed, determined internally by FileViewer based on file type } @@ -53,6 +55,7 @@ export function FileWindow({ sshHost, initialX = 100, initialY = 100, + onFileNotFound, }: FileWindowProps) { const { closeWindow, @@ -62,6 +65,8 @@ export function FileWindow({ windows, } = useWindowManager(); + const { t } = useTranslation(); + const [content, setContent] = useState(""); const [isLoading, setIsLoading] = useState(false); const [isEditable, setIsEditable] = useState(false); @@ -192,9 +197,26 @@ export function FileWindow({ `SSH connection failed. Please check your connection to ${sshHost.name} (${sshHost.ip}:${sshHost.port})`, ); } else { - toast.error( - `Failed to load file: ${error.message || errorData?.error || "Unknown error"}`, - ); + // Check if file not found (common error messages from cat command) + const errorMessage = errorData?.error || error.message || "Unknown error"; + const isFileNotFound = + errorData?.fileNotFound || + error.response?.status === 404 || + errorMessage.includes("No such file or directory") || + errorMessage.includes("cannot access") || + errorMessage.includes("not found"); + + if (isFileNotFound && onFileNotFound) { + // Notify parent component about the missing file for cleanup + onFileNotFound(file); + toast.error(t("fileManager.fileNotFoundAndRemoved", { name: file.name })); + } else { + toast.error(t("fileManager.failedToLoadFile", { + error: errorMessage.includes("Server error occurred") ? + t("fileManager.serverErrorOccurred") : + errorMessage + })); + } } } finally { setIsLoading(false);