files.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. import logging
  2. import os
  3. import uuid
  4. from pathlib import Path
  5. from typing import Optional
  6. from pydantic import BaseModel
  7. import mimetypes
  8. from open_webui.storage.provider import Storage
  9. from open_webui.apps.webui.models.files import (
  10. FileForm,
  11. FileModel,
  12. FileModelResponse,
  13. Files,
  14. )
  15. from open_webui.apps.retrieval.main import process_file, ProcessFileForm
  16. from open_webui.config import UPLOAD_DIR
  17. from open_webui.env import SRC_LOG_LEVELS
  18. from open_webui.constants import ERROR_MESSAGES
  19. from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status
  20. from fastapi.responses import FileResponse, StreamingResponse
  21. from open_webui.utils.utils import get_admin_user, get_verified_user
  22. log = logging.getLogger(__name__)
  23. log.setLevel(SRC_LOG_LEVELS["MODELS"])
  24. router = APIRouter()
  25. ############################
  26. # Upload File
  27. ############################
  28. @router.post("/", response_model=FileModelResponse)
  29. def upload_file(file: UploadFile = File(...), user=Depends(get_verified_user)):
  30. log.info(f"file.content_type: {file.content_type}")
  31. try:
  32. unsanitized_filename = file.filename
  33. filename = os.path.basename(unsanitized_filename)
  34. # replace filename with uuid
  35. id = str(uuid.uuid4())
  36. name = filename
  37. filename = f"{id}_{filename}"
  38. contents, file_path = Storage.upload_file(file.file, filename)
  39. file_item = Files.insert_new_file(
  40. user.id,
  41. FileForm(
  42. **{
  43. "id": id,
  44. "filename": filename,
  45. "path": file_path,
  46. "meta": {
  47. "name": name,
  48. "content_type": file.content_type,
  49. "size": len(contents),
  50. },
  51. }
  52. ),
  53. )
  54. try:
  55. process_file(ProcessFileForm(file_id=id))
  56. file_item = Files.get_file_by_id(id=id)
  57. except Exception as e:
  58. log.exception(e)
  59. log.error(f"Error processing file: {file_item.id}")
  60. file_item = FileModelResponse(
  61. **{
  62. **file_item.model_dump(),
  63. "error": e,
  64. }
  65. )
  66. if file_item:
  67. return file_item
  68. else:
  69. raise HTTPException(
  70. status_code=status.HTTP_400_BAD_REQUEST,
  71. detail=ERROR_MESSAGES.DEFAULT("Error uploading file"),
  72. )
  73. except Exception as e:
  74. log.exception(e)
  75. raise HTTPException(
  76. status_code=status.HTTP_400_BAD_REQUEST,
  77. detail=ERROR_MESSAGES.DEFAULT(e),
  78. )
  79. ############################
  80. # List Files
  81. ############################
  82. @router.get("/", response_model=list[FileModelResponse])
  83. async def list_files(user=Depends(get_verified_user)):
  84. if user.role == "admin":
  85. files = Files.get_files()
  86. else:
  87. files = Files.get_files_by_user_id(user.id)
  88. return files
  89. ############################
  90. # Delete All Files
  91. ############################
  92. @router.delete("/all")
  93. async def delete_all_files(user=Depends(get_admin_user)):
  94. result = Files.delete_all_files()
  95. if result:
  96. try:
  97. Storage.delete_all_files()
  98. except Exception as e:
  99. log.exception(e)
  100. log.error(f"Error deleting files")
  101. raise HTTPException(
  102. status_code=status.HTTP_400_BAD_REQUEST,
  103. detail=ERROR_MESSAGES.DEFAULT("Error deleting files"),
  104. )
  105. return {"message": "All files deleted successfully"}
  106. else:
  107. raise HTTPException(
  108. status_code=status.HTTP_400_BAD_REQUEST,
  109. detail=ERROR_MESSAGES.DEFAULT("Error deleting files"),
  110. )
  111. ############################
  112. # Get File By Id
  113. ############################
  114. @router.get("/{id}", response_model=Optional[FileModel])
  115. async def get_file_by_id(id: str, user=Depends(get_verified_user)):
  116. file = Files.get_file_by_id(id)
  117. if file and (file.user_id == user.id or user.role == "admin"):
  118. return file
  119. else:
  120. raise HTTPException(
  121. status_code=status.HTTP_404_NOT_FOUND,
  122. detail=ERROR_MESSAGES.NOT_FOUND,
  123. )
  124. ############################
  125. # Get File Data Content By Id
  126. ############################
  127. @router.get("/{id}/data/content")
  128. async def get_file_data_content_by_id(id: str, user=Depends(get_verified_user)):
  129. file = Files.get_file_by_id(id)
  130. if file and (file.user_id == user.id or user.role == "admin"):
  131. return {"content": file.data.get("content", "")}
  132. else:
  133. raise HTTPException(
  134. status_code=status.HTTP_404_NOT_FOUND,
  135. detail=ERROR_MESSAGES.NOT_FOUND,
  136. )
  137. ############################
  138. # Update File Data Content By Id
  139. ############################
  140. class ContentForm(BaseModel):
  141. content: str
  142. @router.post("/{id}/data/content/update")
  143. async def update_file_data_content_by_id(
  144. id: str, form_data: ContentForm, user=Depends(get_verified_user)
  145. ):
  146. file = Files.get_file_by_id(id)
  147. if file and (file.user_id == user.id or user.role == "admin"):
  148. try:
  149. process_file(ProcessFileForm(file_id=id, content=form_data.content))
  150. file = Files.get_file_by_id(id=id)
  151. except Exception as e:
  152. log.exception(e)
  153. log.error(f"Error processing file: {file.id}")
  154. return {"content": file.data.get("content", "")}
  155. else:
  156. raise HTTPException(
  157. status_code=status.HTTP_404_NOT_FOUND,
  158. detail=ERROR_MESSAGES.NOT_FOUND,
  159. )
  160. ############################
  161. # Get File Content By Id
  162. ############################
  163. @router.get("/{id}/content")
  164. async def get_file_content_by_id(id: str, user=Depends(get_verified_user)):
  165. file = Files.get_file_by_id(id)
  166. if file and (file.user_id == user.id or user.role == "admin"):
  167. try:
  168. file_path = Storage.get_file(file.path)
  169. file_path = Path(file_path)
  170. # Check if the file already exists in the cache
  171. if file_path.is_file():
  172. print(f"file_path: {file_path}")
  173. headers = {
  174. "Content-Disposition": f'attachment; filename="{file.meta.get("name", file.filename)}"'
  175. }
  176. return FileResponse(file_path, headers=headers)
  177. else:
  178. raise HTTPException(
  179. status_code=status.HTTP_404_NOT_FOUND,
  180. detail=ERROR_MESSAGES.NOT_FOUND,
  181. )
  182. except Exception as e:
  183. log.exception(e)
  184. log.error(f"Error getting file content")
  185. raise HTTPException(
  186. status_code=status.HTTP_400_BAD_REQUEST,
  187. detail=ERROR_MESSAGES.DEFAULT("Error getting file content"),
  188. )
  189. else:
  190. raise HTTPException(
  191. status_code=status.HTTP_404_NOT_FOUND,
  192. detail=ERROR_MESSAGES.NOT_FOUND,
  193. )
  194. @router.get("/{id}/content/html")
  195. async def get_html_file_content_by_id(id: str, user=Depends(get_verified_user)):
  196. file = Files.get_file_by_id(id)
  197. if file and (file.user_id == user.id or user.role == "admin"):
  198. try:
  199. file_path = Storage.get_file(file.path)
  200. file_path = Path(file_path)
  201. # Check if the file already exists in the cache
  202. if file_path.is_file():
  203. print(f"file_path: {file_path}")
  204. return FileResponse(file_path)
  205. else:
  206. raise HTTPException(
  207. status_code=status.HTTP_404_NOT_FOUND,
  208. detail=ERROR_MESSAGES.NOT_FOUND,
  209. )
  210. except Exception as e:
  211. log.exception(e)
  212. log.error(f"Error getting file content")
  213. raise HTTPException(
  214. status_code=status.HTTP_400_BAD_REQUEST,
  215. detail=ERROR_MESSAGES.DEFAULT("Error getting file content"),
  216. )
  217. else:
  218. raise HTTPException(
  219. status_code=status.HTTP_404_NOT_FOUND,
  220. detail=ERROR_MESSAGES.NOT_FOUND,
  221. )
  222. @router.get("/{id}/content/{file_name}")
  223. async def get_file_content_by_id(id: str, user=Depends(get_verified_user)):
  224. file = Files.get_file_by_id(id)
  225. if file and (file.user_id == user.id or user.role == "admin"):
  226. file_path = file.path
  227. if file_path:
  228. file_path = Storage.get_file(file_path)
  229. file_path = Path(file_path)
  230. # Check if the file already exists in the cache
  231. if file_path.is_file():
  232. print(f"file_path: {file_path}")
  233. headers = {
  234. "Content-Disposition": f'attachment; filename="{file.meta.get("name", file.filename)}"'
  235. }
  236. return FileResponse(file_path, headers=headers)
  237. else:
  238. raise HTTPException(
  239. status_code=status.HTTP_404_NOT_FOUND,
  240. detail=ERROR_MESSAGES.NOT_FOUND,
  241. )
  242. else:
  243. # File path doesn’t exist, return the content as .txt if possible
  244. file_content = file.content.get("content", "")
  245. file_name = file.filename
  246. # Create a generator that encodes the file content
  247. def generator():
  248. yield file_content.encode("utf-8")
  249. return StreamingResponse(
  250. generator(),
  251. media_type="text/plain",
  252. headers={"Content-Disposition": f"attachment; filename={file_name}"},
  253. )
  254. else:
  255. raise HTTPException(
  256. status_code=status.HTTP_404_NOT_FOUND,
  257. detail=ERROR_MESSAGES.NOT_FOUND,
  258. )
  259. ############################
  260. # Delete File By Id
  261. ############################
  262. @router.delete("/{id}")
  263. async def delete_file_by_id(id: str, user=Depends(get_verified_user)):
  264. file = Files.get_file_by_id(id)
  265. if file and (file.user_id == user.id or user.role == "admin"):
  266. result = Files.delete_file_by_id(id)
  267. if result:
  268. try:
  269. Storage.delete_file(file.filename)
  270. except Exception as e:
  271. log.exception(e)
  272. log.error(f"Error deleting files")
  273. raise HTTPException(
  274. status_code=status.HTTP_400_BAD_REQUEST,
  275. detail=ERROR_MESSAGES.DEFAULT("Error deleting files"),
  276. )
  277. return {"message": "File deleted successfully"}
  278. else:
  279. raise HTTPException(
  280. status_code=status.HTTP_400_BAD_REQUEST,
  281. detail=ERROR_MESSAGES.DEFAULT("Error deleting file"),
  282. )
  283. else:
  284. raise HTTPException(
  285. status_code=status.HTTP_404_NOT_FOUND,
  286. detail=ERROR_MESSAGES.NOT_FOUND,
  287. )