From 4c781888963b53130bc68cf8f712f73ed46d995e Mon Sep 17 00:00:00 2001 From: Ryanba <92616678+Gujiassh@users.noreply.github.com> Date: Wed, 18 Mar 2026 16:31:26 +0800 Subject: [PATCH] fix(gateway): remove generated markdown on upload delete (#1170) * fix(gateway): remove generated markdown on upload delete Keep thread upload storage consistent by deleting the generated markdown companion when the original convertible upload is removed. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Sisyphus Co-authored-by: Willem Jiang Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- backend/app/gateway/routers/uploads.py | 5 ++++- backend/tests/test_uploads_router.py | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/backend/app/gateway/routers/uploads.py b/backend/app/gateway/routers/uploads.py index c43c820..d808a34 100644 --- a/backend/app/gateway/routers/uploads.py +++ b/backend/app/gateway/routers/uploads.py @@ -187,7 +187,10 @@ async def delete_uploaded_file(thread_id: str, filename: str) -> dict: raise HTTPException(status_code=403, detail="Access denied") try: - file_path.unlink() + if file_path.suffix.lower() in CONVERTIBLE_EXTENSIONS: + companion_markdown = file_path.with_suffix(".md") + companion_markdown.unlink(missing_ok=True) + file_path.unlink(missing_ok=True) logger.info(f"Deleted file: {filename}") return {"success": True, "message": f"Deleted {filename}"} except Exception as e: diff --git a/backend/tests/test_uploads_router.py b/backend/tests/test_uploads_router.py index 1e43ce5..649d0d3 100644 --- a/backend/tests/test_uploads_router.py +++ b/backend/tests/test_uploads_router.py @@ -96,3 +96,17 @@ def test_upload_files_rejects_dotdot_and_dot_filenames(tmp_path): # Only the safely normalised file should exist assert [f.name for f in thread_uploads_dir.iterdir()] == ["passwd"] + + +def test_delete_uploaded_file_removes_generated_markdown_companion(tmp_path): + thread_uploads_dir = tmp_path / "uploads" + thread_uploads_dir.mkdir(parents=True) + (thread_uploads_dir / "report.pdf").write_bytes(b"pdf-bytes") + (thread_uploads_dir / "report.md").write_text("converted", encoding="utf-8") + + with patch.object(uploads, "get_uploads_dir", return_value=thread_uploads_dir): + result = asyncio.run(uploads.delete_uploaded_file("thread-aio", "report.pdf")) + + assert result == {"success": True, "message": "Deleted report.pdf"} + assert not (thread_uploads_dir / "report.pdf").exists() + assert not (thread_uploads_dir / "report.md").exists()