FIX: Automatically cleanup deleted files from recent/pinned lists
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 <noreply@anthropic.com>
This commit is contained in:
@@ -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 });
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -769,6 +769,9 @@
|
||||
"modified": "修改时间",
|
||||
"largeFileWarning": "大文件警告",
|
||||
"largeFileWarningDesc": "此文件大小为 {{size}},以文本形式打开可能会导致性能问题。",
|
||||
"fileNotFoundAndRemoved": "文件 \"{{name}}\" 未找到,已从最近访问/固定文件中移除",
|
||||
"failedToLoadFile": "加载文件失败:{{error}}",
|
||||
"serverErrorOccurred": "服务器错误,请稍后重试。",
|
||||
"failedToDeleteItem": "删除项目失败",
|
||||
"itemRenamedSuccessfully": "{{type}}重命名成功",
|
||||
"failedToRenameItem": "重命名项目失败",
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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<string>("");
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user