diff --git a/app/api/routers/database.py b/app/api/routers/database.py index f86bd6c..4b1a270 100644 --- a/app/api/routers/database.py +++ b/app/api/routers/database.py @@ -1,5 +1,4 @@ import re -from contextlib import suppress from datetime import datetime from pathlib import Path from typing import Optional @@ -12,17 +11,13 @@ from starlette.background import BackgroundTask from app import state from app.config import CACHE_DIR from app.database import DatabaseManager +from app.utils.files import remove_file_safely router = APIRouter(tags=["database"]) INVALID_SHEET_NAME_CHARS = re.compile(r"[:\\/?*\[\]]") -def _remove_file(path: Path) -> None: - with suppress(OSError): - path.unlink() - - def _resolve_requested_tables( table_name: Optional[str], table_names: Optional[list[str]], @@ -227,12 +222,12 @@ async def download_table( df.to_excel(writer, sheet_name=_make_sheet_name(name, used_sheet_names), index=False) media_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" except Exception: - _remove_file(filepath) + remove_file_safely(filepath) raise return FileResponse( path=str(filepath), filename=filename, media_type=media_type, - background=BackgroundTask(_remove_file, filepath), + background=BackgroundTask(remove_file_safely, filepath), ) diff --git a/app/api/routers/history.py b/app/api/routers/history.py index a0d6a09..479bed0 100644 --- a/app/api/routers/history.py +++ b/app/api/routers/history.py @@ -1,5 +1,4 @@ import zipfile -from contextlib import suppress from datetime import datetime from pathlib import Path from typing import Any @@ -10,17 +9,12 @@ from starlette.background import BackgroundTask from app import state from app.config import CACHE_DIR -from app.utils.files import format_size, get_dir_size +from app.utils.files import format_size, get_dir_size, remove_file_safely router = APIRouter(tags=["history"]) -def _remove_file(path: Path) -> None: - with suppress(OSError): - path.unlink() - - def _safe_filename_part(value: str) -> str: safe = "".join(char if char.isalnum() or char in {"-", "_"} else "_" for char in value) return safe.strip("_") or "history" @@ -202,14 +196,14 @@ async def download_history(record_id: str = Body(..., embed=True)): try: _zip_directory(work_dir, archive_path) except Exception: - _remove_file(archive_path) + remove_file_safely(archive_path) raise return FileResponse( path=str(archive_path), filename=filename, media_type="application/zip", - background=BackgroundTask(_remove_file, archive_path), + background=BackgroundTask(remove_file_safely, archive_path), ) @@ -238,12 +232,12 @@ async def download_history_file( try: _zip_history_item(target_path, archive_path) except Exception: - _remove_file(archive_path) + remove_file_safely(archive_path) raise return FileResponse( path=str(archive_path), filename=filename, media_type="application/zip", - background=BackgroundTask(_remove_file, archive_path), + background=BackgroundTask(remove_file_safely, archive_path), ) diff --git a/app/utils/files.py b/app/utils/files.py index dbe698f..9d64b93 100644 --- a/app/utils/files.py +++ b/app/utils/files.py @@ -1,6 +1,13 @@ from pathlib import Path +def remove_file_safely(path: Path) -> None: + try: + path.unlink() + except OSError: + pass + + def get_dir_size(path: Path) -> int: total_size = 0 try: @@ -26,4 +33,3 @@ def format_size(size_bytes: int) -> str: return f"{value:.2f} {unit}" value /= 1024.0 return f"{value:.2f} PB" - diff --git a/docs/project_context.md b/docs/project_context.md index c8d0355..ee71461 100644 --- a/docs/project_context.md +++ b/docs/project_context.md @@ -632,3 +632,9 @@ - `app/api/routers/task_runtime.py` centralizes shared task-stage updates, processing license log output, and safe history-retention cleanup for manual processing and remote processing routes. - `app/api/routers/tasks.py` and `app/api/routers/remote.py` now reuse the shared helpers instead of carrying duplicate `_set_task_stage` and `_log_license_check` implementations. - Verification performed: `.venv\Scripts\python.exe -m compileall app`, `npm run build`, and `cargo check --manifest-path src-tauri\Cargo.toml` with a temporary sidecar placeholder all passed. Generated build output, Python caches, and temporary Tauri sidecar files were removed after verification. + +## 2026-06-01: Download cleanup helper + +- `app/utils/files.py` now provides `remove_file_safely()` for best-effort temporary file cleanup. +- Database table export and history archive download routes now reuse this helper instead of carrying duplicate private `_remove_file()` functions. +- Verification performed: `.venv\Scripts\python.exe -m compileall app`, `npm run build`, and `cargo check --manifest-path src-tauri\Cargo.toml` with a temporary sidecar placeholder all passed. Generated build output, Python caches, and temporary Tauri sidecar files were removed after verification.