diff --git a/classquiz/config.py b/classquiz/config.py index b079f27..9216f81 100644 --- a/classquiz/config.py +++ b/classquiz/config.py @@ -50,6 +50,7 @@ class Settings(BaseSettings): github_client_secret: Optional[str] custom_openid_provider: CustomOpenIDProvider | None = None telemetry_enabled: bool = True + free_storage_limit: int = 1074000000 # storage_backend storage_backend: str | None = "deta" diff --git a/classquiz/routers/editor.py b/classquiz/routers/editor.py index 302fc2d..7704eab 100644 --- a/classquiz/routers/editor.py +++ b/classquiz/routers/editor.py @@ -65,25 +65,6 @@ async def init_editor(edit: bool, quiz_id: Optional[UUID] = None, user: User = D return InitEditorResponse(token=edit_id) -class GetPowData(BaseModel): - data: str - - -@router.get("/pow", response_model=GetPowData) -async def get_pow_data(edit_id: str): - session_data = await redis.get(f"edit_session:{edit_id}") - if session_data is None: - raise HTTPException(status_code=401, detail="Edit ID not found!") - random_str = os.urandom(8).hex() - await redis.set(f"edit_session:{edit_id}:pow", random_str, ex=3800) - return GetPowData(data=random_str) - - -class UploadImageReturn(BaseModel): - id: str - pow_data: str - - @router.post("/finish") async def finish_edit(edit_id: str, quiz_input: QuizInput): session_data = await redis.get(f"edit_session:{edit_id}") diff --git a/classquiz/routers/quiz.py b/classquiz/routers/quiz.py index 2ab1c06..e15287f 100644 --- a/classquiz/routers/quiz.py +++ b/classquiz/routers/quiz.py @@ -11,17 +11,15 @@ from random import randint import ormar.exceptions -from classquiz.helpers import get_meili_data, generate_spreadsheet +from classquiz.helpers import generate_spreadsheet from fastapi import APIRouter, Depends, HTTPException from fastapi.responses import JSONResponse, StreamingResponse from pydantic import ValidationError, BaseModel -import bleach from classquiz.auth import get_current_user from classquiz.config import redis, settings, storage, meilisearch -from classquiz.db.models import Quiz, QuizInput, User, PlayGame, GameInLobby, QuizQuestion +from classquiz.db.models import Quiz, User, PlayGame, GameInLobby, QuizQuestion from classquiz.kahoot_importer.import_quiz import import_quiz -import html import urllib.parse settings = settings() @@ -29,33 +27,6 @@ settings = settings() router = APIRouter() -@router.post("/create", deprecated=True) -async def create_quiz_lol(quiz_input: QuizInput, user: User = Depends(get_current_user)): - imgur_regex = r"^https://i\.imgur\.com\/.{7}.(jpg|png|gif)$" - server_regex = rf"^{re.escape(settings.root_address)}/api/v1/storage/download/.{36}--.{36}$" - quiz_input.title = html.unescape(bleach.clean(quiz_input.title, tags=[], strip=True)) - quiz_input.description = html.unescape(bleach.clean(quiz_input.description, tags=[], strip=True)) - for question in quiz_input.questions: - if question.image == "": - question.image = None - if ( - question.image is not None - and not re.match(imgur_regex, question.image) - and not re.match(server_regex, question.image) - ): - raise HTTPException(status_code=400, detail="image url is not valid") - if quiz_input.cover_image == "": - quiz_input.cover_image = None - - if quiz_input.cover_image is not None and not bool(re.match(server_regex, quiz_input.cover_image)): - raise HTTPException(status_code=400, detail="image url is not valid") - quiz = Quiz(**quiz_input.dict(), user_id=user.id, id=uuid.uuid4()) - await redis.delete("global_quiz_count") - if quiz_input.public: - meilisearch.index(settings.meilisearch_index).add_documents([await get_meili_data(quiz)]) - return await quiz.save() - - @router.get("/get/{quiz_id}") async def get_quiz_from_id(quiz_id: str, user: User | None = Depends(get_current_user)): try: @@ -213,54 +184,10 @@ async def get_quiz_list(user: User = Depends(get_current_user), page_size: int | raise HTTPException(status_code=400, detail="Invalid page(size). page(size) have to be greater than 0.") -@router.put("/update/{quiz_id}", deprecated=True) -async def update_quiz(quiz_id: str, quiz_input: QuizInput, user: User = Depends(get_current_user)): - imgur_regex = r"^https://i\.imgur\.com\/.{7}.(jpg|png|gif)$" - server_regex = rf"^{re.escape(settings.root_address)}/api/v1/storage/download/.{{36}}--.{{36}}$" - for question in quiz_input.questions: - if question.image == "": - question.image = None - if ( - question.image is not None - and not bool(re.match(server_regex, question.image)) - and not bool(re.match(imgur_regex, question.image)) - ): - raise HTTPException(status_code=400, detail="image url is not valid") - try: - quiz_id = uuid.UUID(quiz_id) - except ValueError: - raise HTTPException(status_code=400, detail="badly formed quiz id") - # Check Cover-Image - - if quiz_input.cover_image == "": - quiz_input.cover_image = None - - if quiz_input.cover_image is not None and not bool(re.match(server_regex, quiz_input.cover_image)): - raise HTTPException(status_code=400, detail="image url is not valid") - - quiz = await Quiz.objects.get_or_none(id=quiz_id, user_id=user.id) - if quiz is None: - return JSONResponse(status_code=404, content={"detail": "quiz not found"}) - else: - quiz_input.description = html.unescape(bleach.clean(quiz_input.description, tags=[], strip=True)) - quiz_input.title = html.unescape(bleach.clean(quiz_input.title, tags=[], strip=True)) - meilisearch.index(settings.meilisearch_index).update_documents([await get_meili_data(quiz)]) - if quiz.public and not quiz_input.public: - meilisearch.index(settings.meilisearch_index).delete_document(str(quiz.id)) - if not quiz.public and quiz_input.public: - meilisearch.index(settings.meilisearch_index).add_documents([await get_meili_data(quiz)]) - quiz.title = quiz_input.title - quiz.cover_image = quiz_input.cover_image - quiz.public = quiz_input.public - quiz.description = quiz_input.description - quiz.updated_at = datetime.now() - quiz.questions = quiz_input.dict()["questions"] - - return await quiz.update() - - @router.post("/import/{quiz_id}") async def import_quiz_route(quiz_id: str, user: User = Depends(get_current_user)): + if user.storage_used > settings.free_storage_limit: + raise HTTPException(status_code=409, detail="Storage limit reached") try: return await import_quiz(quiz_id, user) except ValidationError: diff --git a/classquiz/routers/storage.py b/classquiz/routers/storage.py index 937866b..f6c2d9e 100644 --- a/classquiz/routers/storage.py +++ b/classquiz/routers/storage.py @@ -52,17 +52,11 @@ async def download_file(file_name: str): @router.post("/") async def upload_file(file: UploadFile = File(), user: User = Depends(get_current_user)) -> PublicStorageItem: + if user.storage_used > settings.free_storage_limit: + raise HTTPException(status_code=409, detail="Storage limit reached") file_id = uuid4() size = 0 - # if file.file.name is None: - # size = len(await file.read()) - # print("MemorySize", size) - # else: - # f = file.file - # a = file.file.fileno() - # os.path.getsize(file.file.name) - # print("DiskSize", size, "name:", file.file.name, "a:", file.file.tell(), "size") file_obj = StorageItem( id=file_id, uploaded_at=datetime.now(), @@ -137,16 +131,16 @@ async def list_images( return return_items -class ReturnStorageUsage(BaseModel): - usage: int +class ReturnGetStorageLimit(BaseModel): + limit: int + limit_reached: bool + used: int -@router.get("/usage") -async def get_storage_usage(user: User = Depends(get_current_user)) -> ReturnStorageUsage: - files: list[StorageItem] = await StorageItem.objects.filter(user=user).filter(deleted_at=None).all() - counted_size = 0 - if len(files) == 0: - raise HTTPException(status_code=404, detail="No file found") - for file in files: - counted_size += file.size - return ReturnStorageUsage(usage=counted_size) +@router.get("/limit") +async def get_storage_limit(user: User = Depends(get_current_user)) -> ReturnGetStorageLimit: + user = await User.objects.get_or_none(id=user.id) + if user.storage_used > settings.free_storage_limit: + return ReturnGetStorageLimit(limit=settings.free_storage_limit, limit_reached=True, used=user.storage_used) + else: + return ReturnGetStorageLimit(limit=settings.free_storage_limit, limit_reached=False, used=user.storage_used)