rev 4
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import (
|
||||
APIRouter,
|
||||
@@ -42,8 +43,10 @@ MAX_MB_SIZE = 1024
|
||||
async def upload_file(
|
||||
file: UploadFile = FastAPIFile(...),
|
||||
folder: str = Form(default=""),
|
||||
project_id: uuid.UUID | None = Form(default=None),
|
||||
current_user: User = Depends(get_current_user),
|
||||
storage: StorageService = Depends(get_storage),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
) -> FileInfoResponse:
|
||||
# Validate max file size (matches old behavior).
|
||||
file.file.seek(0, 2)
|
||||
@@ -54,11 +57,18 @@ async def upload_file(
|
||||
if size_bytes > max_size:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"File size exceeds the maximum limit of {MAX_MB_SIZE} MB.",
|
||||
detail=f"Размер файла превышает допустимый лимит в {MAX_MB_SIZE} МБ.",
|
||||
)
|
||||
|
||||
user_folder = get_user_folder(current_user)
|
||||
resolved_folder = f"{user_folder}/{folder}" if folder else f"{user_folder}/user_upload"
|
||||
inferred_project_id = project_id
|
||||
if inferred_project_id is None and folder.startswith("projects/"):
|
||||
project_token = folder.removeprefix("projects/").split("/", 1)[0]
|
||||
try:
|
||||
inferred_project_id = uuid.UUID(project_token)
|
||||
except ValueError:
|
||||
inferred_project_id = None
|
||||
|
||||
key = await storage.upload_fileobj(
|
||||
fileobj=file.file,
|
||||
@@ -68,8 +78,23 @@ async def upload_file(
|
||||
content_type=file.content_type,
|
||||
)
|
||||
|
||||
service = FileService(db)
|
||||
file_entry = await service.create_file(
|
||||
requester=current_user,
|
||||
data=FileCreate(
|
||||
project_id=inferred_project_id,
|
||||
original_filename=file.filename or "upload.bin",
|
||||
path=key,
|
||||
storage_backend=get_settings().storage_backend.upper(),
|
||||
mime_type=file.content_type or "application/octet-stream",
|
||||
size_bytes=size_bytes,
|
||||
file_format=Path(file.filename or "upload.bin").suffix.lstrip(".") or None,
|
||||
is_uploaded=True,
|
||||
),
|
||||
)
|
||||
info = await storage.get_file_info(key)
|
||||
return FileInfoResponse(
|
||||
file_id=file_entry.id,
|
||||
file_path=info.file_path,
|
||||
file_url=info.file_url,
|
||||
file_size=info.file_size,
|
||||
@@ -82,17 +107,24 @@ async def get_file_info(
|
||||
file_path: str = Query(...),
|
||||
current_user: User = Depends(get_current_user),
|
||||
storage: StorageService = Depends(get_storage),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
) -> FileInfoResponse:
|
||||
if not current_user.is_staff:
|
||||
user_prefix = f"{get_user_folder(current_user)}/"
|
||||
if not file_path.startswith(user_prefix):
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Forbidden")
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Доступ запрещён")
|
||||
|
||||
if not await storage.exists(file_path):
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Not found")
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Не найдено")
|
||||
|
||||
service = FileService(db)
|
||||
file = await service.get_file_by_path(file_path)
|
||||
if file is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Не найдено")
|
||||
|
||||
info = await storage.get_file_info(file_path)
|
||||
return FileInfoResponse(
|
||||
file_id=file.id,
|
||||
file_path=info.file_path,
|
||||
file_url=info.file_url,
|
||||
file_size=info.file_size,
|
||||
@@ -110,7 +142,7 @@ async def get_local_file(
|
||||
settings = get_settings()
|
||||
full_path = (settings.local_storage_dir / file_path).resolve()
|
||||
if not full_path.exists():
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Not found")
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Не найдено")
|
||||
|
||||
return FileResponse(full_path)
|
||||
|
||||
@@ -145,14 +177,42 @@ async def retrieve_file_entry(
|
||||
service = FileService(db)
|
||||
file = await service.get_file(file_id)
|
||||
if file is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Not found")
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Не найдено")
|
||||
|
||||
if not current_user.is_staff and file.owner_id != current_user.id:
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Forbidden")
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Доступ запрещён")
|
||||
|
||||
return FileRead.model_validate(file)
|
||||
|
||||
|
||||
@router.get("/files/{file_id}/resolve/", response_model=FileInfoResponse)
|
||||
async def resolve_file_entry(
|
||||
file_id: uuid.UUID,
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
storage: StorageService = Depends(get_storage),
|
||||
) -> FileInfoResponse:
|
||||
service = FileService(db)
|
||||
file = await service.get_file(file_id)
|
||||
if file is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Не найдено")
|
||||
|
||||
if not current_user.is_staff and file.owner_id != current_user.id:
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Доступ запрещён")
|
||||
|
||||
if not await storage.exists(file.path):
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Не найдено")
|
||||
|
||||
info = await storage.get_file_info(file.path)
|
||||
return FileInfoResponse(
|
||||
file_id=file.id,
|
||||
file_path=file.path,
|
||||
file_url=info.file_url,
|
||||
file_size=info.file_size,
|
||||
filename=file.original_filename or info.filename,
|
||||
)
|
||||
|
||||
|
||||
@router.patch("/files/{file_id}/", response_model=FileRead)
|
||||
async def patch_file_entry(
|
||||
file_id: uuid.UUID,
|
||||
@@ -163,10 +223,10 @@ async def patch_file_entry(
|
||||
service = FileService(db)
|
||||
file = await service.get_file(file_id)
|
||||
if file is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Not found")
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Не найдено")
|
||||
|
||||
if not current_user.is_staff and file.owner_id != current_user.id:
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Forbidden")
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Доступ запрещён")
|
||||
|
||||
file = await service.update_file(file, body)
|
||||
return FileRead.model_validate(file)
|
||||
@@ -181,10 +241,10 @@ async def delete_file_entry(
|
||||
service = FileService(db)
|
||||
file = await service.get_file(file_id)
|
||||
if file is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Not found")
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Не найдено")
|
||||
|
||||
if not current_user.is_staff and file.owner_id != current_user.id:
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Forbidden")
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Доступ запрещён")
|
||||
|
||||
await service.delete_file(file)
|
||||
return Response(status_code=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
@@ -51,6 +51,7 @@ class FileUpdate(Schema):
|
||||
|
||||
|
||||
class FileInfoResponse(Schema):
|
||||
file_id: UUID
|
||||
file_path: str
|
||||
file_url: str
|
||||
file_size: int | None = None
|
||||
|
||||
@@ -22,6 +22,9 @@ class FileService:
|
||||
async def get_file(self, file_id: uuid.UUID) -> File | None:
|
||||
return await self._repo.get_by_id(file_id)
|
||||
|
||||
async def get_file_by_path(self, path: str) -> File | None:
|
||||
return await self._repo.get_by_path(path)
|
||||
|
||||
async def create_file(self, *, requester: User, data: FileCreate) -> File:
|
||||
return await self._repo.create(requester=requester, data=data)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user