From ad3ab39c1adab4f4250a79b03cb857751344745e Mon Sep 17 00:00:00 2001 From: Alek Jarmov Date: Sun, 17 Dec 2023 18:43:06 +0100 Subject: [PATCH] refactor functions --- .idea/workspace.xml | 50 ++++++++++++++ backend/app/auth/dependencies.py | 9 ++- backend/app/auth/models.py | 3 + backend/app/exceptions.py | 7 ++ backend/app/file_transfer/exceptions.py | 10 +++ backend/app/file_transfer/router.py | 4 ++ backend/app/file_transfer/service.py | 90 ++++++++++++------------- 7 files changed, 123 insertions(+), 50 deletions(-) create mode 100644 .idea/workspace.xml create mode 100644 backend/app/exceptions.py create mode 100644 backend/app/file_transfer/exceptions.py diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..c50a908 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + 1702481645218 + + + + + + \ No newline at end of file diff --git a/backend/app/auth/dependencies.py b/backend/app/auth/dependencies.py index 658adbb..86934ef 100644 --- a/backend/app/auth/dependencies.py +++ b/backend/app/auth/dependencies.py @@ -2,7 +2,7 @@ from typing import Annotated from .service import oauth2_scheme, SECRET_KEY, ALGORITHM, get_user_by_username from app.database import get_async_session -from .schemas import TokenData + from jose import JWTError, jwt from .models import User from sqlalchemy.ext.asyncio import AsyncSession @@ -19,13 +19,12 @@ async def get_current_user( ) try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) - username: str = payload.get("sub") + username: str | None = payload.get("sub") if username is None: raise credentials_exception - token_data = TokenData(username=username) except JWTError as err: raise credentials_exception from err - user = get_user_by_username(username=token_data.username, session=session) + user = await get_user_by_username(username=username, session=session) if user is None: raise credentials_exception - return await user + return user diff --git a/backend/app/auth/models.py b/backend/app/auth/models.py index a694ecf..8f8ea30 100644 --- a/backend/app/auth/models.py +++ b/backend/app/auth/models.py @@ -24,6 +24,9 @@ class User(Base): files = relationship("File", back_populates="user", lazy="selectin") webhooks = relationship("Webhook", back_populates="user") + def has_remaining_quota(self) -> bool: + return bool(self.quota != 0) + class Role(Base): __tablename__ = "role" diff --git a/backend/app/exceptions.py b/backend/app/exceptions.py new file mode 100644 index 0000000..5e88dad --- /dev/null +++ b/backend/app/exceptions.py @@ -0,0 +1,7 @@ +from fastapi import HTTPException, status + +server_error = HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Internal server error", +) + diff --git a/backend/app/file_transfer/exceptions.py b/backend/app/file_transfer/exceptions.py new file mode 100644 index 0000000..a680562 --- /dev/null +++ b/backend/app/file_transfer/exceptions.py @@ -0,0 +1,10 @@ +from fastapi import HTTPException, status + +quota_exception = HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Quota exausted", +) +no_access_exception = HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Not authorized to access file", +) diff --git a/backend/app/file_transfer/router.py b/backend/app/file_transfer/router.py index 990e195..9281820 100644 --- a/backend/app/file_transfer/router.py +++ b/backend/app/file_transfer/router.py @@ -43,3 +43,7 @@ async def get_file( filename = await verify_file(path, current_user, session) # Return the file using FileResponse return FileResponse(FILE_PATH + path, filename=filename) + +@router.get("/test") +async def test() -> str: + return "Endpoint works" diff --git a/backend/app/file_transfer/service.py b/backend/app/file_transfer/service.py index 7ebffd1..fb298a7 100644 --- a/backend/app/file_transfer/service.py +++ b/backend/app/file_transfer/service.py @@ -1,75 +1,75 @@ from app.auth.models import User from .constants import FILE_PATH from sqlalchemy.ext.asyncio import AsyncSession -from sqlalchemy import select +from sqlalchemy import select, update import uuid from datetime import datetime, timedelta -from fastapi import HTTPException, status, UploadFile +from fastapi import UploadFile, Depends from .schemas import MetadataFileResponse from pathlib import Path from .models import File - -quota_exception = HTTPException( - status_code=status.HTTP_403_FORBIDDEN, - detail="Quota exausted", -) -no_access_exception = HTTPException( - status_code=status.HTTP_403_FORBIDDEN, - detail="Not authorized to access file", -) +from typing import Annotated +from app.auth.dependencies import get_current_user +from .exceptions import quota_exception, no_access_exception async def upload_file_unencrypted( - current_user: User, session: AsyncSession, file: UploadFile + session: AsyncSession, + file: UploadFile, + current_user: Annotated[User, Depends(get_current_user)], ) -> None: - if current_user.quota == 0: + if not current_user.has_remaining_quota(): raise quota_exception - file_path = str(uuid.uuid4()) + file.filename - path = FILE_PATH + file_path - try: - contents = file.file.read() + + file_path = f"{uuid.uuid4()}{file.filename}" + path = Path(FILE_PATH) / file_path + async with session.begin(): + contents = await file.read() with Path.open(path, "wb") as f: f.write(contents) - except Exception as e: - raise e - finally: - file.file.close() - file_db = File( - name=file.filename, - path=file_path, - encrypted=False, - size=file.size, - timestamp=datetime.now(), - expiration=datetime.now() + timedelta(days=14), - user=current_user, - ) - session.add(file_db) - current_user.quota -= 1 - await session.commit() + file_db = File( + name=file.filename, + path=file_path, + encrypted=False, + size=file.size, + timestamp=datetime.now(), + expiration=datetime.now() + timedelta(days=14), + user=current_user, + ) + + session.add(file_db) + update_statement = ( + update(User).where(User.id == current_user.id).values(quota=User.quota - 1) + ) + await session.execute(update_statement) async def get_all_files_user( - current_user: User, session: AsyncSession + current_user: Annotated[User, Depends(get_current_user)], session: AsyncSession ) -> list[MetadataFileResponse]: files = await session.execute(select(File).filter(File.user_id == current_user.id)) - file_responses = [] - for file in files.scalars(): - file_response = MetadataFileResponse( - name=file.name, - path=file.path, - size=file.size, - encrypted=file.encrypted, + return [ + MetadataFileResponse( + name=str(file.name), + path=str(file.path), + size=int(file.size), + encrypted=bool(file.encrypted), ) - file_responses.append(file_response) - - return file_responses + for file in files.scalars() + ] -async def verify_file(path: str, current_user: User, session: AsyncSession) -> str: +async def verify_file( + path: str, + current_user: Annotated[User, Depends(get_current_user)], + session: AsyncSession, +) -> str: file = await session.execute(select(File).filter(File.path == path)) file = file.scalar_one_or_none() + if file == None: + raise if file.user.id == current_user.id: return file.name raise no_access_exception