Skip to content

fix: cleanup orphan thumbnail files on folder deletion - #1450

Open
KeerthiKumarR wants to merge 3 commits into
AOSSIE-Org:mainfrom
KeerthiKumarR:fix-1159-orphan-thumbnails
Open

fix: cleanup orphan thumbnail files on folder deletion#1450
KeerthiKumarR wants to merge 3 commits into
AOSSIE-Org:mainfrom
KeerthiKumarR:fix-1159-orphan-thumbnails

Conversation

@KeerthiKumarR

@KeerthiKumarR KeerthiKumarR commented Aug 3, 2026

Copy link
Copy Markdown

Fixes #1159

Problem

Currently, when a user deletes a folder or when the sync microservice cleans up an obsolete folder, the application deletes the folder records from the database. Because SQLite enforces PRAGMA foreign_keys = ON, this correctly cascade-deletes all associated image and video records. However, this database-level cascade does not delete the physical thumbnail files on the disk, permanently leaking storage space whenever a folder is removed.

Summary of Changes

This PR introduces a python-level cleanup layer to explicitly remove thumbnail files from the physical disk before executing the database deletion query.

Modified Files

  • backend/app/utils/folders.py:
    • Added folder_util_cleanup_thumbnails(folder_ids), which identifies all recursive subfolders, fetches associated images/videos, and uses os.remove() to clean up their thumbnail files.
    • Injected this cleanup call into folder_util_delete_obsolete_folders, ensuring the background sync microservice cleans up thumbnails when a folder is deleted from the filesystem.
  • backend/app/routes/folders.py:
    • Injected the cleanup call into the delete_folders API route, ensuring thumbnails are correctly deleted when a user manually removes folders via the frontend UI.

Summary by CodeRabbit

  • Bug Fixes
    • Improved folder deletion by automatically removing associated image and video thumbnails.
    • Thumbnail cleanup now includes all descendant folders, helping prevent leftover files and unnecessary storage usage.
    • Improved handling of thumbnail cleanup errors during folder deletion.
    • Fixed folder path matching for paths containing special characters such as backslashes, percent signs, and underscores.

Copilot AI review requested due to automatic review settings August 3, 2026 09:17
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@KeerthiKumarR, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 37 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: fc064458-2956-4ff6-b6be-2fca9536d9ff

📥 Commits

Reviewing files that changed from the base of the PR and between 432584a and 94b1523.

📒 Files selected for processing (3)
  • backend/app/database/folders.py
  • backend/app/utils/folders.py
  • backend/tests/test_folders.py

Walkthrough

Folder deletion now removes image and video thumbnails for selected folders and descendants before deleting database records. Path-prefix queries now match literal backslashes, percent signs, and underscores.

Changes

Folder cleanup and path matching

Layer / File(s) Summary
Implement thumbnail cleanup
backend/app/utils/folders.py
Adds folder_util_cleanup_thumbnails, which resolves descendants, deletes image and video thumbnails, handles filesystem errors, and returns the deletion count.
Integrate cleanup into folder deletion
backend/app/routes/folders.py, backend/app/utils/folders.py
Calls thumbnail cleanup before deleting folder records in the route and obsolete-folder flow.
Escape path-prefix query values
backend/app/database/folders.py
Escapes SQL LIKE metacharacters and declares backslash as the escape character.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant delete_folders
  participant folder_util_cleanup_thumbnails
  participant Filesystem
  participant Database
  delete_folders->>folder_util_cleanup_thumbnails: requested folder IDs
  folder_util_cleanup_thumbnails->>Filesystem: delete image and video thumbnails
  Filesystem-->>folder_util_cleanup_thumbnails: deletion result
  delete_folders->>Database: delete folder records
Loading

Possibly related issues

  • 1404: The change implements thumbnail cleanup for deleted folders, including descendant folders and video thumbnails.

Suggested labels: Python

Suggested reviewers: rohan-pandeyy, takitxt

Poem

I hop through folders, tidy and bright,
Removing thumbnails from disk tonight.
Images and videos vanish clean,
Descendant paths leave no trace seen.
Then records may safely rest—
A rabbit’s cleanup quest!

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly states the primary change: cleaning up orphan thumbnail files when folders are deleted.
Linked Issues check ✅ Passed The changes implement recursive thumbnail cleanup for user-deleted and obsolete folders before database deletion, satisfying issue #1159.
Out of Scope Changes check ✅ Passed All changes support folder thumbnail cleanup, descendant resolution, and safe path matching required by issue #1159.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses issue #1159 by ensuring thumbnail files on disk are deleted before folder records are removed from the database (where ON DELETE CASCADE would otherwise orphan thumbnail files).

Changes:

  • Added folder_util_cleanup_thumbnails() to delete image/video thumbnail files for deleted folders (and their subfolders).
  • Wired thumbnail cleanup into both the API folder deletion route and the “obsolete folder” cleanup path.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
backend/app/utils/folders.py Adds thumbnail cleanup helper and invokes it before DB folder deletion in obsolete-folder flow.
backend/app/routes/folders.py Invokes thumbnail cleanup before db_delete_folders_batch in the delete-folders endpoint.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread backend/app/utils/folders.py
Comment thread backend/app/utils/folders.py
Comment thread backend/app/utils/folders.py
Comment thread backend/app/routes/folders.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/app/utils/folders.py`:
- Line 35: Update db_get_folder_ids_by_path_prefix to escape literal % and _
characters in the supplied path and add an explicit ESCAPE character to its LIKE
query, preserving descendant-prefix matching. Add a regression test covering
folder names containing both % and _, ensuring lookup and subsequent thumbnail
deletion affect only matching descendants.
- Around line 45-60: The thumbnail cleanup loops in delete_folders and
folder_util_delete_obsolete_folders must not allow database deletion after an
os.remove OSError. Propagate the cleanup failure or otherwise return a failure
state that both callers honor before deleting records; preserve already-missing
files as a benign case when supported.
- Around line 21-65: Add focused tests for folder_util_cleanup_thumbnails
covering image and video thumbnails across descendant folders, literal wildcard
characters in folder paths, missing thumbnail files, and unlink failures. Also
test folder_util_delete_obsolete_folders with the same thumbnail scenarios and
verify folders containing undeleted children are handled correctly, ensuring
both cleanup callers are covered.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 40f69c61-746c-4fee-9d32-1a41fd19cb54

📥 Commits

Reviewing files that changed from the base of the PR and between 3cd313c and 2520f36.

📒 Files selected for processing (2)
  • backend/app/routes/folders.py
  • backend/app/utils/folders.py

Comment thread backend/app/utils/folders.py
Comment thread backend/app/utils/folders.py
Comment thread backend/app/utils/folders.py Outdated
@KeerthiKumarR

Copy link
Copy Markdown
Author

@rohan-pandeyy
PTAL when you got time!

@rohan-pandeyy

Copy link
Copy Markdown
Member

Address all the coderabbit suggestions

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
backend/app/database/folders.py (2)

370-371: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Wrap the replacement chain and remove trailing whitespace.

Line 370 exceeds the PEP 8 79-character guideline. Line 371 contains trailing whitespace. Keep the replacement order, but split the expression and leave the blank line empty.

As per path instructions, Python files must follow PEP 8.

Proposed formatting
-        escaped_path = root_path.replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_")
-        
+        escaped_path = (
+            root_path.replace("\\", "\\\\")
+            .replace("%", "\\%")
+            .replace("_", "\\_")
+        )
+
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/database/folders.py` around lines 370 - 371, Reformat the
escaped_path assignment in the surrounding folder-path logic so the existing
replacement order remains unchanged while the expression is wrapped within 79
characters. Remove trailing whitespace and ensure the following blank line is
truly empty.

Source: Path instructions


369-377: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Add regression tests for literal path-prefix matching.

Because Line 370 escapes \, %, and _ before Line 375 applies LIKE, add automated tests for each character and combinations. Verify that descendant paths match, while wildcard overmatches and sibling prefixes do not. Include the path + os.sep call used by backend/app/utils/folders.py.

As per path instructions, critical functionality must have automated, comprehensive tests.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/database/folders.py` around lines 369 - 377, Add regression tests
for the folder prefix query around the escaped_path handling and its callers in
the folders utility, covering literal backslash, percent, underscore, and
combinations of these characters. Verify descendant paths match, wildcard-like
overmatches and sibling prefixes do not, and include cases using the path +
os.sep form.

Source: Path instructions

backend/app/utils/folders.py (1)

51-53: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use a bare raise when re-raising OSError.

raise e adds the handler’s raise site to the traceback and triggers Ruff TRY201. Keep as e for logging, but replace both raise e statements with raise.

Proposed fix
-                raise e
+                raise
...
-                raise e
+                raise

As per path instructions, use idiomatic Python exception handling.

Also applies to: 64-66

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/utils/folders.py` around lines 51 - 53, Update the OSError
handlers in the thumbnail-removal logic to keep the existing logging with the
bound exception and replace both explicit `raise e` statements with bare
`raise`, preserving the original traceback and satisfying idiomatic exception
handling.

Sources: Path instructions, Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@backend/app/database/folders.py`:
- Around line 370-371: Reformat the escaped_path assignment in the surrounding
folder-path logic so the existing replacement order remains unchanged while the
expression is wrapped within 79 characters. Remove trailing whitespace and
ensure the following blank line is truly empty.
- Around line 369-377: Add regression tests for the folder prefix query around
the escaped_path handling and its callers in the folders utility, covering
literal backslash, percent, underscore, and combinations of these characters.
Verify descendant paths match, wildcard-like overmatches and sibling prefixes do
not, and include cases using the path + os.sep form.

In `@backend/app/utils/folders.py`:
- Around line 51-53: Update the OSError handlers in the thumbnail-removal logic
to keep the existing logging with the bound exception and replace both explicit
`raise e` statements with bare `raise`, preserving the original traceback and
satisfying idiomatic exception handling.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1514b520-9cd7-4c41-8eb1-78e145ca1953

📥 Commits

Reviewing files that changed from the base of the PR and between 2520f36 and 432584a.

📒 Files selected for processing (2)
  • backend/app/database/folders.py
  • backend/app/utils/folders.py

@KeerthiKumarR

KeerthiKumarR commented Aug 3, 2026

Copy link
Copy Markdown
Author

Address all the coderabbit suggestions

Hey @rohan-pandeyy , I've addressed all of CodeRabbit's feedback!

  • The wildcards are correctly escaped in the SQLite query.
  • The OS exception handling has been made safer.
  • I've added regression tests for edge cases.
    PTAL when you got time and let me know if any changes are required!

@gitcordapp

gitcordapp Bot commented Aug 4, 2026

Copy link
Copy Markdown

Link your account with Gitcord

Thanks for opening this PR, @KeerthiKumarR!

To receive Discord notifications and contributor tracking for this organization:

  1. Join Discord: https://discord.gg/hjUhu33uAn
  2. In Discord, run /link KeerthiKumarR
  3. Paste the verification code into your GitHub bio (or a public gist)
  4. Click Verify in Discord (or run /verify-link KeerthiKumarR)

Once linked, Gitcord can notify you about reviews, merges, and more.

Posted by Gitcord

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BUG: Orphan thumbnail files permanently leak disk space when folders are deleted

3 participants