Added storage_limit for user accounts

This commit is contained in:
Mawoka
2023-05-30 11:14:46 +02:00
parent b64ed05235
commit 44432b20c7
4 changed files with 18 additions and 115 deletions
+1
View File
@@ -50,6 +50,7 @@ class Settings(BaseSettings):
github_client_secret: Optional[str] github_client_secret: Optional[str]
custom_openid_provider: CustomOpenIDProvider | None = None custom_openid_provider: CustomOpenIDProvider | None = None
telemetry_enabled: bool = True telemetry_enabled: bool = True
free_storage_limit: int = 1074000000
# storage_backend # storage_backend
storage_backend: str | None = "deta" storage_backend: str | None = "deta"
-19
View File
@@ -65,25 +65,6 @@ async def init_editor(edit: bool, quiz_id: Optional[UUID] = None, user: User = D
return InitEditorResponse(token=edit_id) 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") @router.post("/finish")
async def finish_edit(edit_id: str, quiz_input: QuizInput): async def finish_edit(edit_id: str, quiz_input: QuizInput):
session_data = await redis.get(f"edit_session:{edit_id}") session_data = await redis.get(f"edit_session:{edit_id}")
+4 -77
View File
@@ -11,17 +11,15 @@ from random import randint
import ormar.exceptions 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 import APIRouter, Depends, HTTPException
from fastapi.responses import JSONResponse, StreamingResponse from fastapi.responses import JSONResponse, StreamingResponse
from pydantic import ValidationError, BaseModel from pydantic import ValidationError, BaseModel
import bleach
from classquiz.auth import get_current_user from classquiz.auth import get_current_user
from classquiz.config import redis, settings, storage, meilisearch 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 from classquiz.kahoot_importer.import_quiz import import_quiz
import html
import urllib.parse import urllib.parse
settings = settings() settings = settings()
@@ -29,33 +27,6 @@ settings = settings()
router = APIRouter() 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}") @router.get("/get/{quiz_id}")
async def get_quiz_from_id(quiz_id: str, user: User | None = Depends(get_current_user)): async def get_quiz_from_id(quiz_id: str, user: User | None = Depends(get_current_user)):
try: 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.") 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}") @router.post("/import/{quiz_id}")
async def import_quiz_route(quiz_id: str, user: User = Depends(get_current_user)): 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: try:
return await import_quiz(quiz_id, user) return await import_quiz(quiz_id, user)
except ValidationError: except ValidationError:
+13 -19
View File
@@ -52,17 +52,11 @@ async def download_file(file_name: str):
@router.post("/") @router.post("/")
async def upload_file(file: UploadFile = File(), user: User = Depends(get_current_user)) -> PublicStorageItem: 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() file_id = uuid4()
size = 0 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( file_obj = StorageItem(
id=file_id, id=file_id,
uploaded_at=datetime.now(), uploaded_at=datetime.now(),
@@ -137,16 +131,16 @@ async def list_images(
return return_items return return_items
class ReturnStorageUsage(BaseModel): class ReturnGetStorageLimit(BaseModel):
usage: int limit: int
limit_reached: bool
used: int
@router.get("/usage") @router.get("/limit")
async def get_storage_usage(user: User = Depends(get_current_user)) -> ReturnStorageUsage: async def get_storage_limit(user: User = Depends(get_current_user)) -> ReturnGetStorageLimit:
files: list[StorageItem] = await StorageItem.objects.filter(user=user).filter(deleted_at=None).all() user = await User.objects.get_or_none(id=user.id)
counted_size = 0 if user.storage_used > settings.free_storage_limit:
if len(files) == 0: return ReturnGetStorageLimit(limit=settings.free_storage_limit, limit_reached=True, used=user.storage_used)
raise HTTPException(status_code=404, detail="No file found") else:
for file in files: return ReturnGetStorageLimit(limit=settings.free_storage_limit, limit_reached=False, used=user.storage_used)
counted_size += file.size
return ReturnStorageUsage(usage=counted_size)