fix: keep folder parent references acyclic (#28748)
Moving a folder under one of its own subfolders was accepted. A folder in a parent loop is never a root, so it and everything under it silently disappeared from the sidebar, and there was no way to get it back from the UI. The move is now rejected with a 400, folders whose parent chain loops are put back at the root on the next folder list, and the folder tree traversals skip ids they have already visited so existing data in that state stays workable.
This commit is contained in:
@@ -202,10 +202,14 @@ class FolderTable:
|
||||
try:
|
||||
async with get_async_db_context(db) as db:
|
||||
folders = []
|
||||
seen_ids = {id}
|
||||
|
||||
async def get_children(folder):
|
||||
children = await self.get_folders_by_parent_id_and_user_id(folder.id, user_id, db=db)
|
||||
for child in children:
|
||||
if child.id in seen_ids:
|
||||
continue
|
||||
seen_ids.add(child.id)
|
||||
await get_children(child)
|
||||
folders.append(child)
|
||||
|
||||
@@ -267,15 +271,17 @@ class FolderTable:
|
||||
if not folder:
|
||||
return []
|
||||
|
||||
folder_ids = [folder.id]
|
||||
folder_ids = {folder.id}
|
||||
folders = [FolderModel.model_validate(folder)]
|
||||
while folders:
|
||||
current_folder = folders.pop()
|
||||
children = await self.get_folders_by_parent_id_and_user_id(current_folder.id, user_id, db=db)
|
||||
folder_ids.extend(child.id for child in children)
|
||||
folders.extend(children)
|
||||
for child in children:
|
||||
if child.id not in folder_ids:
|
||||
folder_ids.add(child.id)
|
||||
folders.append(child)
|
||||
|
||||
return folder_ids
|
||||
return list(folder_ids)
|
||||
|
||||
async def update_folder_parent_id_by_id_and_user_id(
|
||||
self,
|
||||
@@ -385,11 +391,15 @@ class FolderTable:
|
||||
return folder_ids
|
||||
|
||||
folder_ids.append(folder.id)
|
||||
seen_ids = {folder.id}
|
||||
|
||||
# Delete all children folders
|
||||
async def delete_children(folder):
|
||||
folder_children = await self.get_folders_by_parent_id_and_user_id(folder.id, user_id, db=db)
|
||||
for folder_child in folder_children:
|
||||
if folder_child.id in seen_ids:
|
||||
continue
|
||||
seen_ids.add(folder_child.id)
|
||||
await delete_children(folder_child)
|
||||
folder_ids.append(folder_child.id)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user