From 73325c9fee94a065e94190db5848e0d4d45220d1 Mon Sep 17 00:00:00 2001 From: Potenz Date: Sat, 4 Oct 2025 00:05:45 +0530 Subject: [PATCH] fix(cleanup files fix2) --- apps/Backend/src/routes/insuranceStatus.ts | 52 +-------- apps/Backend/src/utils/emptyTempFolder.ts | 129 +++++++++++++++++++++ 2 files changed, 130 insertions(+), 51 deletions(-) create mode 100644 apps/Backend/src/utils/emptyTempFolder.ts diff --git a/apps/Backend/src/routes/insuranceStatus.ts b/apps/Backend/src/routes/insuranceStatus.ts index a26cad6..9950c89 100644 --- a/apps/Backend/src/routes/insuranceStatus.ts +++ b/apps/Backend/src/routes/insuranceStatus.ts @@ -7,60 +7,10 @@ import path from "path"; import PDFDocument from "pdfkit"; import { forwardToSeleniumInsuranceClaimStatusAgent } from "../services/seleniumInsuranceClaimStatusClient"; import fsSync from "fs"; +import { emptyFolderContainingFile } from "../utils/emptyTempFolder"; const router = Router(); -/** - * Empty the folder containing `filePath`. - * - Uses only the filePath to locate the folder. - * - Deletes files and symlinks only (does not remove subfolders). - * - Has minimal safety checks to avoid catastrophic deletes. - */ -async function emptyFolderContainingFile(filePath: string) { - if (!filePath) return; - - // Resolve to absolute path and get parent folder - const absFile = path.resolve(String(filePath)); - const folder = path.dirname(absFile); - - // Safety: refuse to operate on root (or extremely short paths) - const parsed = path.parse(folder); - if (!folder || folder === parsed.root) { - throw new Error(`Refusing to clean root or empty folder: ${folder}`); - } - - // Optional light heuristic: require folder name to be non-empty and not a system root like "/tmp" - const base = path.basename(folder).toLowerCase(); - if (base.length < 2) { - throw new Error(`Refusing to clean suspicious folder: ${folder}`); - } - - // Read and remove files/symlinks only - try { - const names = await fs.readdir(folder); - for (const name of names) { - const full = path.join(folder, name); - try { - const st = await fs.lstat(full); - if (st.isFile() || st.isSymbolicLink()) { - await fs.unlink(full); - console.log(`[cleanup] removed file: ${full}`); - } else { - // If you truly know there will be no subfolders you can skip or log - console.log(`[cleanup] skipping non-file item: ${full}`); - } - } catch (innerErr) { - console.error(`[cleanup] failed to remove ${full}:`, innerErr); - // continue with other files - } - } - console.log(`[cleanup] emptied folder: ${folder}`); - } catch (err) { - console.error(`[cleanup] failed reading folder ${folder}:`, err); - throw err; - } -} - router.post( "/eligibility-check", async (req: Request, res: Response): Promise => { diff --git a/apps/Backend/src/utils/emptyTempFolder.ts b/apps/Backend/src/utils/emptyTempFolder.ts new file mode 100644 index 0000000..7ad39b1 --- /dev/null +++ b/apps/Backend/src/utils/emptyTempFolder.ts @@ -0,0 +1,129 @@ +import fs from "fs/promises"; +import path from "path"; +import * as os from "os"; + +/** + * Recursively delete files & symlinks under the parent folder of `filePath`. + * Will remove subfolders only if they become empty. + * + * Safety checks: + * - Refuses to operate on root or extremely short folders. + * - Refuses to operate on the user's home directory. + * + * Throws on critical safety failure; otherwise logs and continues on per-file errors. + */ +export async function emptyFolderContainingFile( + filePath: string | null | undefined +): Promise { + if (!filePath) return; + + const absFile = path.resolve(String(filePath)); + const folder = path.dirname(absFile); + + // Basic sanity / safety checks + if (!folder) { + throw new Error( + `Refusing to clean: resolved folder is empty for filePath=${filePath}` + ); + } + + const parsed = path.parse(folder); + if (folder === parsed.root) { + throw new Error(`Refusing to clean root folder: ${folder}`); + } + + // Don't allow cleaning the user's home directory + const home = os.homedir(); + if (home && path.resolve(home) === path.resolve(folder)) { + throw new Error(`Refusing to clean user's home directory: ${folder}`); + } + + // Heuristic: require folder basename length >= 2 (prevents cleaning very short names like '/a') + const base = path.basename(folder).toLowerCase(); + if (!base || base.length < 2) { + throw new Error(`Refusing to clean suspicious folder: ${folder}`); + } + + // Now recursively walk and remove files & symlinks. Remove directories if empty after processing. + async function removeContentsRecursively(dir: string): Promise { + // returns true if directory is empty (and therefore safe-to-delete by caller) + let entries; + try { + entries = await fs.readdir(dir, { withFileTypes: true }); + } catch (err) { + console.error(`[cleanup] failed to read directory ${dir}:`, err); + // If we can't read, don't attempt to delete it + return false; + } + + for (const entry of entries) { + const full = path.join(dir, entry.name); + try { + if (entry.isFile() || entry.isSymbolicLink()) { + await fs.unlink(full); + console.log(`[cleanup] removed file/symlink: ${full}`); + } else if (entry.isDirectory()) { + // Recurse into subdirectory + const subEmpty = await removeContentsRecursively(full); + if (subEmpty) { + // remove the now-empty directory + try { + await fs.rmdir(full); + console.log(`[cleanup] removed empty directory: ${full}`); + } catch (rmDirErr) { + // Could fail due to permissions/race; log and continue + console.error( + `[cleanup] failed to remove directory ${full}:`, + rmDirErr + ); + } + } else { + // directory not empty (or had read error) — we leave it + console.log( + `[cleanup] left directory (not empty or read error): ${full}` + ); + } + } else { + // Other types (socket, FIFO, etc.) — try unlink as fallback + try { + await fs.unlink(full); + console.log(`[cleanup] removed special entry: ${full}`); + } catch (unlinkErr) { + console.error( + `[cleanup] failed to remove special entry ${full}:`, + unlinkErr + ); + } + } + } catch (entryErr) { + console.error(`[cleanup] error handling ${full}:`, entryErr); + // continue with other entries + } + } + + // After processing entries, check if directory is now empty + try { + const after = await fs.readdir(dir); + return after.length === 0; + } catch (readAfterErr) { + console.error( + `[cleanup] failed to re-read directory ${dir}:`, + readAfterErr + ); + return false; + } + } + + // Start recursive cleaning at the folder (we do NOT delete the top folder itself, + // only its contents; if you want the folder removed too, you can call fs.rmdir after) + try { + await removeContentsRecursively(folder); + console.log(`[cleanup] finished cleaning contents of folder: ${folder}`); + } catch (err) { + console.error( + `[cleanup] unexpected error while cleaning folder ${folder}:`, + err + ); + throw err; + } +}