folders.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. import logging
  2. import os
  3. import shutil
  4. import uuid
  5. from pathlib import Path
  6. from typing import Optional
  7. from pydantic import BaseModel
  8. import mimetypes
  9. from open_webui.models.folders import (
  10. FolderForm,
  11. FolderUpdateForm,
  12. FolderModel,
  13. FolderNameIdResponse,
  14. Folders,
  15. )
  16. from open_webui.models.chats import Chats
  17. from open_webui.models.files import Files
  18. from open_webui.models.knowledge import Knowledges
  19. from open_webui.config import UPLOAD_DIR
  20. from open_webui.env import SRC_LOG_LEVELS
  21. from open_webui.constants import ERROR_MESSAGES
  22. from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status, Request
  23. from fastapi.responses import FileResponse, StreamingResponse
  24. from open_webui.utils.auth import get_admin_user, get_verified_user
  25. from open_webui.utils.access_control import has_permission
  26. log = logging.getLogger(__name__)
  27. log.setLevel(SRC_LOG_LEVELS["MODELS"])
  28. router = APIRouter()
  29. ############################
  30. # Get Folders
  31. ############################
  32. @router.get("/", response_model=list[FolderNameIdResponse])
  33. async def get_folders(user=Depends(get_verified_user)):
  34. folders = Folders.get_folders_by_user_id(user.id)
  35. # Verify folder data integrity
  36. folder_list = []
  37. for folder in folders:
  38. if folder.parent_id and not Folders.get_folder_by_id_and_user_id(
  39. folder.parent_id, user.id
  40. ):
  41. folder = Folders.update_folder_parent_id_by_id_and_user_id(
  42. folder.id, user.id, None
  43. )
  44. if folder.data:
  45. if "files" in folder.data:
  46. valid_files = []
  47. for file in folder.data["files"]:
  48. if file.get("type") == "file":
  49. if Files.check_access_by_user_id(
  50. file.get("id"), user.id, "read"
  51. ):
  52. valid_files.append(file)
  53. elif file.get("type") == "collection":
  54. if Knowledges.check_access_by_user_id(
  55. file.get("id"), user.id, "read"
  56. ):
  57. valid_files.append(file)
  58. else:
  59. valid_files.append(file)
  60. folder.data["files"] = valid_files
  61. Folders.update_folder_by_id_and_user_id(
  62. folder.id, user.id, FolderUpdateForm(data=folder.data)
  63. )
  64. folder_list.append(FolderNameIdResponse(**folder.model_dump()))
  65. return folder_list
  66. ############################
  67. # Create Folder
  68. ############################
  69. @router.post("/")
  70. def create_folder(form_data: FolderForm, user=Depends(get_verified_user)):
  71. folder = Folders.get_folder_by_parent_id_and_user_id_and_name(
  72. None, user.id, form_data.name
  73. )
  74. if folder:
  75. raise HTTPException(
  76. status_code=status.HTTP_400_BAD_REQUEST,
  77. detail=ERROR_MESSAGES.DEFAULT("Folder already exists"),
  78. )
  79. try:
  80. folder = Folders.insert_new_folder(user.id, form_data)
  81. return folder
  82. except Exception as e:
  83. log.exception(e)
  84. log.error("Error creating folder")
  85. raise HTTPException(
  86. status_code=status.HTTP_400_BAD_REQUEST,
  87. detail=ERROR_MESSAGES.DEFAULT("Error creating folder"),
  88. )
  89. ############################
  90. # Get Folders By Id
  91. ############################
  92. @router.get("/{id}", response_model=Optional[FolderModel])
  93. async def get_folder_by_id(id: str, user=Depends(get_verified_user)):
  94. folder = Folders.get_folder_by_id_and_user_id(id, user.id)
  95. if folder:
  96. return folder
  97. else:
  98. raise HTTPException(
  99. status_code=status.HTTP_404_NOT_FOUND,
  100. detail=ERROR_MESSAGES.NOT_FOUND,
  101. )
  102. ############################
  103. # Update Folder Name By Id
  104. ############################
  105. @router.post("/{id}/update")
  106. async def update_folder_name_by_id(
  107. id: str, form_data: FolderUpdateForm, user=Depends(get_verified_user)
  108. ):
  109. folder = Folders.get_folder_by_id_and_user_id(id, user.id)
  110. if folder:
  111. if form_data.name is not None:
  112. # Check if folder with same name exists
  113. existing_folder = Folders.get_folder_by_parent_id_and_user_id_and_name(
  114. folder.parent_id, user.id, form_data.name
  115. )
  116. if existing_folder and existing_folder.id != id:
  117. raise HTTPException(
  118. status_code=status.HTTP_400_BAD_REQUEST,
  119. detail=ERROR_MESSAGES.DEFAULT("Folder already exists"),
  120. )
  121. try:
  122. folder = Folders.update_folder_by_id_and_user_id(id, user.id, form_data)
  123. return folder
  124. except Exception as e:
  125. log.exception(e)
  126. log.error(f"Error updating folder: {id}")
  127. raise HTTPException(
  128. status_code=status.HTTP_400_BAD_REQUEST,
  129. detail=ERROR_MESSAGES.DEFAULT("Error updating folder"),
  130. )
  131. else:
  132. raise HTTPException(
  133. status_code=status.HTTP_404_NOT_FOUND,
  134. detail=ERROR_MESSAGES.NOT_FOUND,
  135. )
  136. ############################
  137. # Update Folder Parent Id By Id
  138. ############################
  139. class FolderParentIdForm(BaseModel):
  140. parent_id: Optional[str] = None
  141. @router.post("/{id}/update/parent")
  142. async def update_folder_parent_id_by_id(
  143. id: str, form_data: FolderParentIdForm, user=Depends(get_verified_user)
  144. ):
  145. folder = Folders.get_folder_by_id_and_user_id(id, user.id)
  146. if folder:
  147. existing_folder = Folders.get_folder_by_parent_id_and_user_id_and_name(
  148. form_data.parent_id, user.id, folder.name
  149. )
  150. if existing_folder:
  151. raise HTTPException(
  152. status_code=status.HTTP_400_BAD_REQUEST,
  153. detail=ERROR_MESSAGES.DEFAULT("Folder already exists"),
  154. )
  155. try:
  156. folder = Folders.update_folder_parent_id_by_id_and_user_id(
  157. id, user.id, form_data.parent_id
  158. )
  159. return folder
  160. except Exception as e:
  161. log.exception(e)
  162. log.error(f"Error updating folder: {id}")
  163. raise HTTPException(
  164. status_code=status.HTTP_400_BAD_REQUEST,
  165. detail=ERROR_MESSAGES.DEFAULT("Error updating folder"),
  166. )
  167. else:
  168. raise HTTPException(
  169. status_code=status.HTTP_404_NOT_FOUND,
  170. detail=ERROR_MESSAGES.NOT_FOUND,
  171. )
  172. ############################
  173. # Update Folder Is Expanded By Id
  174. ############################
  175. class FolderIsExpandedForm(BaseModel):
  176. is_expanded: bool
  177. @router.post("/{id}/update/expanded")
  178. async def update_folder_is_expanded_by_id(
  179. id: str, form_data: FolderIsExpandedForm, user=Depends(get_verified_user)
  180. ):
  181. folder = Folders.get_folder_by_id_and_user_id(id, user.id)
  182. if folder:
  183. try:
  184. folder = Folders.update_folder_is_expanded_by_id_and_user_id(
  185. id, user.id, form_data.is_expanded
  186. )
  187. return folder
  188. except Exception as e:
  189. log.exception(e)
  190. log.error(f"Error updating folder: {id}")
  191. raise HTTPException(
  192. status_code=status.HTTP_400_BAD_REQUEST,
  193. detail=ERROR_MESSAGES.DEFAULT("Error updating folder"),
  194. )
  195. else:
  196. raise HTTPException(
  197. status_code=status.HTTP_404_NOT_FOUND,
  198. detail=ERROR_MESSAGES.NOT_FOUND,
  199. )
  200. ############################
  201. # Delete Folder By Id
  202. ############################
  203. @router.delete("/{id}")
  204. async def delete_folder_by_id(
  205. request: Request, id: str, user=Depends(get_verified_user)
  206. ):
  207. if Chats.count_chats_by_folder_id_and_user_id(id, user.id):
  208. chat_delete_permission = has_permission(
  209. user.id, "chat.delete", request.app.state.config.USER_PERMISSIONS
  210. )
  211. if user.role != "admin" and not chat_delete_permission:
  212. raise HTTPException(
  213. status_code=status.HTTP_403_FORBIDDEN,
  214. detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
  215. )
  216. folders = []
  217. folders.append(Folders.get_folder_by_id_and_user_id(id, user.id))
  218. while folders:
  219. folder = folders.pop()
  220. if folder:
  221. try:
  222. folder_ids = Folders.delete_folder_by_id_and_user_id(id, user.id)
  223. for folder_id in folder_ids:
  224. Chats.delete_chats_by_user_id_and_folder_id(user.id, folder_id)
  225. return True
  226. except Exception as e:
  227. log.exception(e)
  228. log.error(f"Error deleting folder: {id}")
  229. raise HTTPException(
  230. status_code=status.HTTP_400_BAD_REQUEST,
  231. detail=ERROR_MESSAGES.DEFAULT("Error deleting folder"),
  232. )
  233. finally:
  234. # Get all subfolders
  235. subfolders = Folders.get_folders_by_parent_id_and_user_id(
  236. folder.id, user.id
  237. )
  238. folders.extend(subfolders)
  239. else:
  240. raise HTTPException(
  241. status_code=status.HTTP_404_NOT_FOUND,
  242. detail=ERROR_MESSAGES.NOT_FOUND,
  243. )