GooDrive/backend/drive/trash_cleanup.py
Louis Mazin a5fb4ad3fa nice
2026-10-14 18:47:43 +02:00

62 lines
2.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Permanent deletion shared by the trash API and scheduled retention cleanup."""
from django.db import transaction
from .models import DriveItem, UploadSession
from .services import descendant_files
from .storage_layout import disk_path, file_location
def has_active_uploads_in_tree(item):
"""Do not purge directories while a resumable upload targets a descendant."""
if not item.is_folder:
return False
pending = [item.pk]
while pending:
batch, pending = pending[:250], pending[250:]
if UploadSession.objects.filter(parent_id__in=batch).exists():
return True
pending.extend(DriveItem.objects.filter(parent_id__in=batch, is_folder=True).values_list('pk', flat=True))
return False
def delete_trashed_item(item):
"""Delete the SQL tree, then its tracked files after commit.
Untracked files and directories are not erased. Failures resolving a stored
path abort before the SQL delete; missing physical files are tolerated.
"""
if item.trashed_at is None:
raise ValueError('L’élément doit être dans la corbeille.')
if has_active_uploads_in_tree(item):
raise ValueError('Un transfert est encore en cours dans ce dossier.')
files = [disk_path(key) for key in descendant_files(item)]
# A child may have been trashed separately, into another isolated directory.
folders = []
queue = [item.pk] if item.is_folder else []
if item.is_folder:
folders.append(disk_path(item.storage_location or file_location(item.space, item.parent, item.name)))
while queue:
batch, queue = queue[:300], queue[300:]
children = list(DriveItem.objects.filter(parent_id__in=batch, is_folder=True).select_related('parent','space'))
queue.extend(child.pk for child in children)
for child in children:
folders.append(disk_path(child.storage_location or file_location(child.space, child.parent, child.name)))
with transaction.atomic():
item.delete()
def remove_files():
for path in files:
path.unlink(missing_ok=True)
for folder in sorted(set(folders), key=lambda p:len(p.parts), reverse=True):
if folder.is_dir() and not folder.is_symlink():
# Never recursively erase untracked files.
try: folder.rmdir()
except OSError: pass
# Remove empty UUID trash containers (never the shared .trash root).
trash_root = disk_path('.gooddrive-trash')
for path in [*files, *folders]:
parent = path.parent
while parent != trash_root and parent.is_relative_to(trash_root):
try: parent.rmdir()
except OSError: break
parent = parent.parent
transaction.on_commit(remove_files)