From 695b3b43c672bb3b5050de5ffa6227f577141e7d Mon Sep 17 00:00:00 2001 From: Mawoka Date: Mon, 8 Jul 2024 10:33:11 +0200 Subject: [PATCH] Remove Backups and copies --- classquiz/config (copy).py | 107 ---- classquiz/db/models (copy).py | 492 ---------------- classquiz/routers/sitemap (copy).py | 63 -- docker-compose (copy).yml | 121 ---- frontend/Dockerfile (copy) | 55 -- frontend/package (copy).json | 109 ---- .../BACKUPcommandpalette (copy).svelteBACKUP | 279 --------- .../routes/BACKUP+page (copy).svelteBACKUP | 555 ------------------ .../BACKUPselect_method (copy).svelteBACKUP | 141 ----- .../BACKUPstart_window (copy).svelteBACKUP | 113 ---- frontend/vite.config (copy).js | 37 -- 11 files changed, 2072 deletions(-) delete mode 100644 classquiz/config (copy).py delete mode 100644 classquiz/db/models (copy).py delete mode 100644 classquiz/routers/sitemap (copy).py delete mode 100644 docker-compose (copy).yml delete mode 100644 frontend/Dockerfile (copy) delete mode 100644 frontend/package (copy).json delete mode 100644 frontend/src/lib/components/BACKUPcommandpalette (copy).svelteBACKUP delete mode 100644 frontend/src/routes/BACKUP+page (copy).svelteBACKUP delete mode 100644 frontend/src/routes/account/login/BACKUPselect_method (copy).svelteBACKUP delete mode 100644 frontend/src/routes/account/login/BACKUPstart_window (copy).svelteBACKUP delete mode 100644 frontend/vite.config (copy).js diff --git a/classquiz/config (copy).py b/classquiz/config (copy).py deleted file mode 100644 index b6f7635..0000000 --- a/classquiz/config (copy).py +++ /dev/null @@ -1,107 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Marlon W (Mawoka) -# -# SPDX-License-Identifier: MPL-2.0 - -import re -from functools import lru_cache - -from redis import asyncio as redis_lib -import redis as redis_base_lib -from pydantic import BaseSettings, RedisDsn, PostgresDsn, BaseModel -import meilisearch as MeiliSearch -from typing import Optional -from arq import create_pool -from arq.connections import RedisSettings, ArqRedis - -from classquiz.storage import Storage - - -class CustomOpenIDProvider(BaseModel): - scopes: str = "openid email profile" - server_metadata_url: str - client_id: str - client_secret: str - - -class Settings(BaseSettings): - """ - Settings class for the shop app. - """ - - root_address: str = "http://127.0.0.1:8000" - redis: RedisDsn = "redis://localhost:6379/0?decode_responses=True" - skip_email_verification: bool = False - db_url: str | PostgresDsn = "postgresql://postgres:mysecretpassword@localhost:5432/classquiz" - hcaptcha_key: str | None = None - recaptcha_key: str | None = None - mail_address: str - mail_password: str - mail_username: str - mail_server: str - mail_port: int - secret_key: str - access_token_expire_minutes: int = 30 - cache_expiry: int = 86400 - sentry_dsn: str | None - meilisearch_url: str = "http://127.0.0.1:7700" - meilisearch_index: str = "classquiz" - google_client_id: Optional[str] - google_client_secret: Optional[str] - github_client_id: Optional[str] - github_client_secret: Optional[str] - custom_openid_provider: CustomOpenIDProvider | None = None - telemetry_enabled: bool = True - free_storage_limit: int = 1074000000 - pixabay_api_key: str | None = None - mods: list[str] = [] - registration_disabled: bool = False - - # storage_backend - storage_backend: str | None = "local" - - # if storage_backend == "local": - storage_path: str | None - - # if storage_backend == "s3": - s3_access_key: str | None - s3_secret_key: str | None - s3_bucket_name: str = "classquiz" - s3_base_url: str | None - - class Config: - env_file = ".env" - env_file_encoding = "utf-8" - env_nested_delimiter = "__" - - -async def initialize_arq(): - # skipcq: PYL-W0603 - global arq - arq = await create_pool(RedisSettings.from_dsn(settings.redis)) - - -@lru_cache() -def settings() -> Settings: - return Settings() - - -pool = redis_lib.ConnectionPool().from_url(settings().redis) - -redis: redis_base_lib.client.Redis = redis_lib.Redis(connection_pool=pool) -arq: ArqRedis = ArqRedis(pool_or_conn=pool) -storage: Storage = Storage( - backend=settings().storage_backend, - storage_path=settings().storage_path, - access_key=settings().s3_access_key, - secret_key=settings().s3_secret_key, - bucket_name=settings().s3_bucket_name, - base_url=settings().s3_base_url, -) - -meilisearch = MeiliSearch.Client(settings().meilisearch_url) - -ALLOWED_TAGS_FOR_QUIZ = ["b", "strong", "i", "em", "small", "mark", "del", "sub", "sup"] - -ALLOWED_MIME_TYPES = ["image/png", "video/mp4", "image/jpeg", "image/gif", "image/webp"] - -server_regex = rf"^{re.escape(settings().root_address)}/api/v1/storage/download/.{{36}}--.{{36}}$" diff --git a/classquiz/db/models (copy).py b/classquiz/db/models (copy).py deleted file mode 100644 index 2a9902f..0000000 --- a/classquiz/db/models (copy).py +++ /dev/null @@ -1,492 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Marlon W (Mawoka) -# -# SPDX-License-Identifier: MPL-2.0 - - -import os -import uuid -from datetime import datetime -from typing import Optional - -import ormar -from ormar import ReferentialAction -from pydantic import BaseModel, Json, validator -from enum import Enum -from . import metadata, database -from .quiztivity import QuizTivityPage -from sqlalchemy import func - - -class UserAuthTypes(Enum): - LOCAL = "LOCAL" - GOOGLE = "GOOGLE" - GITHUB = "GITHUB" - CUSTOM = "CUSTOM" - - -class User(ormar.Model): - """ - The user model in the database - """ - - id: uuid.UUID = ormar.UUID(primary_key=True, default=uuid.uuid4()) - email: str = ormar.String(unique=True, max_length=100) - username: str = ormar.String(unique=True, max_length=100) - password: Optional[str] = ormar.String(max_length=100, nullable=True) - verified: bool = ormar.Boolean(default=False) - verify_key: str = ormar.String(unique=True, max_length=100, nullable=True) - created_at: datetime = ormar.DateTime(default=datetime.now()) - auth_type: UserAuthTypes = ormar.Enum(enum_class=UserAuthTypes, default=UserAuthTypes.LOCAL) - google_uid: Optional[str] = ormar.String(unique=True, max_length=255, nullable=True) - avatar: bytes = ormar.LargeBinary(max_length=25000, represent_as_base64_str=True) - github_user_id: int | None = ormar.Integer(nullable=True) - require_password: bool = ormar.Boolean(default=True, nullable=False) - backup_code: str = ormar.String(max_length=64, min_length=64, nullable=False, default=os.urandom(32).hex()) - totp_secret: str = ormar.String(max_length=32, min_length=32, nullable=True, default=None) - storage_used: int = ormar.BigInteger(nullable=False, default=0, minimum=0) - - class Meta: - tablename = "users" - metadata = metadata - database = database - - class Config: - use_enum_values = True - - -class FidoCredentials(ormar.Model): - pk: int = ormar.Integer(autoincrement=True, primary_key=True) - id: bytes = ormar.LargeBinary(max_length=256) - public_key: bytes = ormar.LargeBinary(max_length=256) - sign_count: int = ormar.Integer() - user: Optional[User] = ormar.ForeignKey(User, ondelete=ReferentialAction.CASCADE) - - class Meta: - tablename = "fido_credentials" - metadata = metadata - database = database - - -class ApiKey(ormar.Model): - key: str = ormar.String(max_length=48, min_length=48, primary_key=True) - user: Optional[User] = ormar.ForeignKey(User, ondelete=ReferentialAction.CASCADE) - - class Meta: - tablename = "api_keys" - metadata = metadata - database = database - - -class UserSession(ormar.Model): - """ - The user session model for user-sessions - """ - - id: uuid.UUID = ormar.UUID(primary_key=True, default=uuid.uuid4()) - user: Optional[User] = ormar.ForeignKey(User, ondelete=ReferentialAction.CASCADE) - session_key: str = ormar.String(unique=True, max_length=64) - created_at: datetime = ormar.DateTime(default=datetime.now()) - ip_address: str = ormar.String(max_length=100, nullable=True) - user_agent: str = ormar.String(max_length=255, nullable=True) - last_seen: datetime = ormar.DateTime(default=datetime.now()) - - class Meta: - tablename = "user_sessions" - metadata = metadata - database = database - - -class ABCDQuizAnswer(BaseModel): - right: bool - answer: str - color: str | None - - -class RangeQuizAnswer(BaseModel): - min: int - max: int - min_correct: int - max_correct: int - - -class VotingQuizAnswer(BaseModel): - answer: str - image: str | None = None - color: str | None - - -class QuizQuestionType(str, Enum): - ABCD = "ABCD" - RANGE = "RANGE" - VOTING = "VOTING" - SLIDE = "SLIDE" - TEXT = "TEXT" - ORDER = "ORDER" - CHECK = "CHECK" - - -class TextQuizAnswer(BaseModel): - answer: str - case_sensitive: bool - - -class QuizQuestion(BaseModel): - question: str - time: str # in Secs - type: None | QuizQuestionType = QuizQuestionType.ABCD - answers: list[ABCDQuizAnswer] | RangeQuizAnswer | list[TextQuizAnswer] | list[VotingQuizAnswer] | str - image: str | None = None - - @validator("answers") - def answers_not_none_if_abcd_type(cls, v, values): - if values["type"] == QuizQuestionType.ABCD and not isinstance(v[0], ABCDQuizAnswer): - raise ValueError("Answers can't be none if type is ABCD") - if values["type"] == QuizQuestionType.RANGE and not isinstance(v, RangeQuizAnswer): - raise ValueError("Answer must be from type RangeQuizAnswer if type is RANGE") - if values["type"] == QuizQuestionType.VOTING and not isinstance(v[0], VotingQuizAnswer): - raise ValueError("Answer must be from type VotingQuizAnswer if type is VOTING") - if values["type"] == QuizQuestionType.TEXT and not isinstance(v[0], TextQuizAnswer): - raise ValueError("Answer must be from type TextQuizAnswer if type is TEXT") - if values["type"] == QuizQuestionType.ORDER and not isinstance(v[0], VotingQuizAnswer): - raise ValueError("Answer must be from type VotingQuizAnswer if type is ORDER") - if values["type"] == QuizQuestionType.SLIDE and not isinstance(v, str): - raise ValueError("Answer must be from type SlideElement if type is SLIDE") - if values["type"] == QuizQuestionType.CHECK and not isinstance(v[0], ABCDQuizAnswer): - raise ValueError("Answers can't be none if type is CHECK") - return v - - -class QuizInput(BaseModel): - public: bool = False - title: str - description: str - cover_image: str | None - background_color: str | None - questions: list[QuizQuestion] - background_image: str | None - - -class Quiz(ormar.Model): - id: uuid.UUID = ormar.UUID(primary_key=True, default=uuid.uuid4(), nullable=False, unique=True) - public: bool = ormar.Boolean(default=False) - title: str = ormar.Text() - description: str = ormar.Text(nullable=True) - created_at: datetime = ormar.DateTime(default=datetime.now()) - updated_at: datetime = ormar.DateTime(default=datetime.now()) - user_id: uuid.UUID = ormar.ForeignKey(User, ondelete=ReferentialAction.CASCADE) - questions: Json[list[QuizQuestion]] = ormar.JSON(nullable=False) - imported_from_kahoot: Optional[bool] = ormar.Boolean(default=False, nullable=True) - cover_image: Optional[str] = ormar.Text(nullable=True, unique=False) - background_color: str | None = ormar.Text(nullable=True, unique=False) - background_image: str | None = ormar.Text(nullable=True, unique=False) - kahoot_id: uuid.UUID | None = ormar.UUID(nullable=True, default=None) - likes: int = ormar.Integer(nullable=False, default=0, server_default="0") - dislikes: int = ormar.Integer(nullable=False, default=0, server_default="0") - plays: int = ormar.Integer(nullable=False, default=0, server_default="0") - views: int = ormar.Integer(nullable=False, default=0, server_default="0") - mod_rating: int | None = ormar.SmallInteger(nullable=True) - - class Meta: - tablename = "quiz" - metadata = metadata - database = database - - -class InstanceData(ormar.Model): - instance_id: uuid.UUID = ormar.UUID(primary_key=True, default=uuid.uuid4(), nullable=False, unique=True) - - class Meta: - tablename = "instance_data" - metadata = metadata - database = database - - -class Token(BaseModel): - """ - For JWT - """ - - access_token: str - token_type: str - - -class TokenData(BaseModel): - """ - For JWT - """ - - email: str | None = None - - -class PlayGame(BaseModel): - quiz_id: uuid.UUID | str - description: str - user_id: uuid.UUID - title: str - questions: list[QuizQuestion] - game_id: uuid.UUID - game_pin: str - started: bool = False - captcha_enabled: bool = False - cover_image: str | None - game_mode: str | None - current_question: int = -1 - background_color: str | None - background_image: str | None - custom_field: str | None - question_show: bool = False - - -class GamePlayer(BaseModel): - username: str - sid: str | None - - -class GameAnswer2(BaseModel): - username: str - right: bool - answer: str - - -class GameAnswer1(BaseModel): - id: int - answers: list[GameAnswer2] - - -class GameSession(BaseModel): - admin: str - game_id: str - # players: list[GamePlayer | None] - answers: list[GameAnswer1 | None] - - -class UpdatePassword(BaseModel): - old_password: str - new_password: str - - -class AnswerData(BaseModel): - username: str - answer: str - right: bool - time_taken: float # In milliseconds - score: int - - -class AnswerDataList(BaseModel): - # Just a method to make a top-level list - __root__: list[AnswerData] - - -class GameInLobby(BaseModel): - game_pin: str - quiz_title: str - game_id: uuid.UUID - - -# skipcq: PY-W0069 -# class UserProfileLinks(ormar.Model): -# id: int = ormar.Integer(primary_key=True, autoincrement=True) -# user: Optional[User] = ormar.ForeignKey(User) -# github_username: str | None = ormar.Text(nullable=True) -# reddit_username: str | None = ormar.Text(nullable=True) -# kahoot_user_id: str | None = ormar.Text(nullable=True) - - -class GameResults(ormar.Model): - id: uuid.UUID = ormar.UUID(primary_key=True) - quiz: uuid.UUID | Quiz = ormar.ForeignKey(Quiz, ondelete=ReferentialAction.CASCADE) - user: uuid.UUID | User = ormar.ForeignKey(User, ondelete=ReferentialAction.CASCADE) - timestamp: datetime = ormar.DateTime(default=datetime.now(), nullable=False) - player_count: int = ormar.Integer(nullable=False, default=0) - note: str | None = ormar.Text(nullable=True) - answers: Json[list[AnswerData]] = ormar.JSON(True) - player_scores: Json[dict[str, str]] = ormar.JSON(nullable=True) - custom_field_data: Json[dict[str, str]] | None = ormar.JSON(nullable=True) - title: str = ormar.Text(nullable=False) - description: str = ormar.Text(nullable=False) - questions: Json[list[QuizQuestion]] = ormar.JSON(nullable=False) - - class Meta: - tablename = "game_results" - metadata = metadata - database = database - - -class QuizTivityInput(BaseModel): - title: str - pages: list[QuizTivityPage] - - -class QuizTivity(ormar.Model): - id: uuid.UUID = ormar.UUID(primary_key=True) - title: str = ormar.Text(nullable=False) - created_at: datetime = ormar.DateTime(nullable=False, server_default=func.now()) - user: User | None = ormar.ForeignKey(User, ondelete=ReferentialAction.CASCADE) - pages: list[QuizTivityPage] = ormar.JSON(nullable=False) - - class Meta: - tablename = "quiztivitys" - metadata = metadata - database = database - - -class QuizTivityShare(ormar.Model): - id: uuid.UUID = ormar.UUID(primary_key=True) - name: str | None = ormar.Text(nullable=True) - expire_at: datetime | None = ormar.DateTime(nullable=True) - quiztivity: QuizTivity | None = ormar.ForeignKey(QuizTivity, ondelete=ReferentialAction.CASCADE) - user: User | None = ormar.ForeignKey(User, ondelete=ReferentialAction.CASCADE) - - class Meta: - tablename = "quiztivityshares" - metadata = metadata - database = database - - -class OnlyId(BaseModel): - id: uuid.UUID - - -class PublicQuizTivityShare(BaseModel): - id: uuid.UUID - name: str | None - expire_in: int | None - quiztivity: OnlyId - user: OnlyId - - @classmethod - def from_db_model(cls, data: QuizTivityShare): - expire_in = None - if data.expire_at is not None: - expire_in = int((data.expire_at - datetime.now()).seconds / 60) - return cls( - id=data.id, - name=data.name, - expire_in=expire_in, - quiztivity=OnlyId(id=data.quiztivity.id), - user=OnlyId(id=data.user.id), - ) - - -class StorageItem(ormar.Model): - id: uuid.UUID = ormar.UUID(primary_key=True) - uploaded_at: datetime = ormar.DateTime(nullable=False, default=datetime.now()) - mime_type: str = ormar.Text(nullable=False) - hash: bytes | None = ormar.LargeBinary(nullable=True, min_length=16, max_length=16) - user: User | None = ormar.ForeignKey(User, ondelete=ReferentialAction.SET_NULL) - size: int = ormar.BigInteger(nullable=False) - storage_path: str | None = ormar.Text(nullable=True) - deleted_at: datetime | None = ormar.DateTime(nullable=True, default=None) - quiztivities: list[QuizTivity] | None = ormar.ManyToMany(QuizTivity) - quizzes: list[Quiz] | None = ormar.ManyToMany(Quiz) - alt_text: str | None = ormar.Text(default=None, nullable=True) - filename: str | None = ormar.Text(default=None, nullable=True) - thumbhash: str | None = ormar.Text(default=None, nullable=True) - server: str | None = ormar.Text(default=None, nullable=True) - imported: bool = ormar.Boolean(default=False, nullable=True) - - class Meta: - tablename = "storage_items" - metadata = metadata - database = database - - -class PublicStorageItem(BaseModel): - id: uuid.UUID - uploaded_at: datetime - mime_type: str - hash: str | None - size: int - deleted_at: datetime | None - alt_text: str | None - filename: str | None - thumbhash: str | None - server: str | None - imported: bool - - @classmethod - def from_db_model(cls, data: StorageItem): - hash_data = None - if data.hash is not None: - hash_data = data.hash.hex() - return cls( - id=data.id, - uploaded_at=data.uploaded_at, - mime_type=data.mime_type, - hash=hash_data, - size=data.size, - deleted_at=data.deleted_at, - alt_text=data.alt_text, - filename=data.filename, - thumbhash=data.thumbhash, - server=data.server, - imported=data.imported, - ) - - -class PrivateStorageItem(PublicStorageItem): - quizzes: list[OnlyId] - quiztivities: list[OnlyId] - - @classmethod - def from_db_model(cls, data: StorageItem): - hash_data = None - if data.hash is not None: - hash_data = data.hash.hex() - quiztivities = [] - quizzes = [] - for quiz in data.quizzes: - quizzes.append(OnlyId(id=quiz.id)) - for quiztivity in data.quiztivities: - quiztivities.append(OnlyId(id=quiztivity.id)) - return cls( - id=data.id, - uploaded_at=data.uploaded_at, - mime_type=data.mime_type, - hash=hash_data, - size=data.size, - deleted_at=data.deleted_at, - alt_text=data.alt_text, - filename=data.filename, - quiztivities=quiztivities, - quizzes=quizzes, - thumbhash=data.thumbhash, - server=data.server, - imported=data.imported, - ) - - -class UpdateStorageItem(BaseModel): - filename: str | None - alt_text: str | None - - -class Controller(ormar.Model): - id: uuid.UUID = ormar.UUID(primary_key=True) - user: uuid.UUID | User = ormar.ForeignKey(User) - secret_key: str = ormar.String(nullable=False, max_length=24, min_length=24) - player_name: str = ormar.Text(nullable=False) - last_seen: datetime | None = ormar.DateTime(nullable=True) - first_seen: datetime | None = ormar.DateTime(nullable=True) - name: str = ormar.Text(nullable=False) - os_version: str | None = ormar.Text(nullable=True) - wanted_os_version: str = ormar.Text(nullable=True, default=None) - - class Meta: - tablename = "controller" - metadata = metadata - database = database - - -class Rating(ormar.Model): - id: uuid.UUID = ormar.UUID(primary_key=True) - user: uuid.UUID | User = ormar.ForeignKey(User) - positive: bool = ormar.Boolean(nullable=False) - created_at: datetime = ormar.DateTime(nullable=False, server_default=func.now()) - quiz: uuid.UUID | Quiz = ormar.ForeignKey(Quiz) - - class Meta: - tablename = "rating" - metadata = metadata - database = database diff --git a/classquiz/routers/sitemap (copy).py b/classquiz/routers/sitemap (copy).py deleted file mode 100644 index 441a8d3..0000000 --- a/classquiz/routers/sitemap (copy).py +++ /dev/null @@ -1,63 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Marlon W (Mawoka) -# -# SPDX-License-Identifier: MPL-2.0 - - -import datetime -import uuid - -from fastapi import APIRouter, Response -from jinja2 import Template -from pydantic import BaseModel -from classquiz.config import redis, settings -from classquiz.db import database - -settings = settings() -router = APIRouter() - - -sitemap_template = """ - -{% for entry in pages -%} - - {{ root_address}}/view/{{ entry.id.hex }} - {%- if entry.updated_at != None -%} - {{ entry.updated_at.strftime("%Y-%m-%d") }} - {% endif %} - - {%- endfor %} -""" - - -sql_statement_metadata = """ -SELECT id, title, description, updated_at from quiz where public ='t' -""" - -sql_statement_id_and_modified_only = """ -SELECT id, updated_at from quiz where public ='t' -""" - -template = Template(sitemap_template, enable_async=True) - - -class SitemapQuiz(BaseModel): - id: uuid.UUID - updated_at: datetime.datetime - - class Config: - orm_mode = True - - -@router.get("/get") -async def get_sitemap(): - redis_cache_resp = await redis.get("sitemap") - if redis_cache_resp is None: - res = await database.fetch_all(sql_statement_id_and_modified_only) - entries = [] - for i in res: - entries.append(SitemapQuiz.from_orm(i)) - rendered_sitemap = await template.render_async({"pages": entries, "root_address": settings.root_address}) - await redis.set("sitemap", rendered_sitemap, ex=86400) - return Response(content=rendered_sitemap, media_type="application/xml") - else: - return Response(content=redis_cache_resp, media_type="application/xml") diff --git a/docker-compose (copy).yml b/docker-compose (copy).yml deleted file mode 100644 index 45e3fec..0000000 --- a/docker-compose (copy).yml +++ /dev/null @@ -1,121 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Marlon W (Mawoka) -# -# SPDX-License-Identifier: MPL-2.0 - -version: "3" - -services: - frontend: - restart: always - build: - context: ./frontend - dockerfile: Dockerfile - depends_on: - - redis - - api - environment: - REDIS_URL: redis://redis:6379/0?decode_responses=True - API_URL: http://api:80 - api: - build: &build_cfg - context: . - dockerfile: Dockerfile - restart: &restart always - depends_on: &depends - - db - - redis - - environment: &env_vars - # --- DON'T CHANGE FROM HERE --- - DB_URL: "postgresql://postgres:classquiz@db:5432/classquiz" # DON'T CHANGE - REDIS: "redis://redis:6379/0?decode_responses=True" # DON'T CHANGE - SECRET_KEY: "TOP_SECRET" # Don't change it manually, use the one-liner provided in the documentation - MAX_WORKERS: "1" # Very important and DON'T CHANGE - ACCESS_TOKEN_EXPIRE_MINUTES: 30 # DON'T CHANGE - MEILISEARCH_URL: "http://meilisearch:7700" # DON'T CHANGE - # -- DON'T CHANGE TILL HERE --- - - # --- GENERAL CONFI --- - ROOT_ADDRESS: "https://classquiz.de" # CHANGE IT (without a "/" at the end) - - # --- MAIL CONFIG --- - MAIL_PORT: "587" - MAIL_ADDRESS: "email@email@email.email" - MAIL_PASSWORD: "PASSWORT" - MAIL_USERNAME: "email@email@email.email" - MAIL_SERVER: "email@email@email.emai" - SKIP_EMAIL_VERIFICATION: "True" # Does the user have to confirm its email by clicking a link? - - # --- EXTERNAL API CONFIG --- - # HCAPTCHA_KEY: "HCAPTCHA_PRIVATE_KEY" - # PIXABAY_API_KEY: "" # Get it from here: https://pixabay.com/api/docs/ - # RECAPTCHA_KEY: "" Get it from Google for the Captcha. - - # -- STORAGE CONFIG --- - STORAGE_BACKEND: "local" # Could also be s3 - STORAGE_PATH: "/app/data" # When s3 is used, this isn't needed. - # If STORAGE_BACKEND is "s3" - #S3_ACCESS_KEY: "YOUR_ACCESS_KEY" - #S3_SECRET_KEY: "YOUR_SECRET_KEY" - #S3_BASE_URL: "YOUR_S3_BASE_URL" - - # --- GOOGLE_AUTH --- - #GOOGLE_CLIENT_ID: "" # Your Google-Client ID, or leave it unset if you don't want it. - #GOOGLE_CLIENT_SECRET: "" # Your Google-Client Secret, or leave it unset if you don't want it. - - # --- GITHUB_AUTH --- - #GITHUB_CLIENT_ID: "" # Your GitHub-Client ID, or leave it unset if you don't want it. - #GITHUB_CLIENT_SECRET: "" # Your GitHub-Client Secret, or leave it unset if you don't want it. - - # --- Custom OpenID --- - #CUSTOM_OPENID_PROVIDER__CLIENT_ID: "" # Adjust if needed - #CUSTOM_OPENID_PROVIDER__CLIENT_SECRET: "" # Adjust if needed - #CUSTOM_OPENID_PROVIDER__SERVER_METADATA_URL: "/.well-known/openid-configuration" # Adjust if needed - - volumes: # Only needed if you chose the "local" storage-backend - - ./uploads:/var/storage - - redis: - image: redis:alpine - restart: always - healthcheck: - test: [ "CMD", "redis-cli","ping" ] - - db: - image: postgres:14-alpine - restart: always - healthcheck: - test: [ "CMD-SHELL", "pg_isready -U postgres" ] - interval: 5s - timeout: 5s - retries: 5 - environment: - POSTGRES_PASSWORD: "classquiz" - POSTGRES_DB: "classquiz" - volumes: - - data:/var/lib/postgresql/data - proxy: - image: caddy:alpine - restart: always - volumes: - - ./Caddyfile-docker:/etc/caddy/Caddyfile - ports: - - "8000:8080" # The 8000 can be changed. - meilisearch: - image: getmeili/meilisearch:v0.28.0 - restart: always - environment: - MEILI_NO_ANALYTICS: true - volumes: - - meilisearch-data:/data.ms - worker: - build: *build_cfg - environment: *env_vars - depends_on: *depends - restart: *restart - command: arq classquiz.worker.WorkerSettings - - -volumes: - data: - meilisearch-data: diff --git a/frontend/Dockerfile (copy) b/frontend/Dockerfile (copy) deleted file mode 100644 index 8f1192b..0000000 --- a/frontend/Dockerfile (copy) +++ /dev/null @@ -1,55 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Marlon W (Mawoka) -# -# SPDX-License-Identifier: MPL-2.0 - -### Build Step -# pull the Node.js Docker image -FROM node:19-bullseye as builder -# ENV API_URL=http://api:80 -ENV API_URL=https://mawoka.eu -#Ah, I've noticed that!!! -ENV REDIS_URL=redis://localhost:6379 -ENV VITE_MAPBOX_ACCESS_TOKEN=pk.eyJ1IjoibWF3b2thIiwiYSI6ImNsMjBob3d4ZjBhcGszYnE0bWp4aXB1ZW4ifQ.IByxV1qeIuEWpHCWsuB88A -# This Mapbox-token is restricted to the following urls: classquiz.de, classquiz.mawoka.eu, test.com -ENV VITE_HCAPTCHA=ee81b2a1-acf3-4d20-b2a4-a7ea94c7eba5 -# ENV VITE_SENTRY=https://75cb4ef1be624d8f81bbaf864b722f8a@glitch.mawoka.eu/2 -#ENV VITE_GOOGLE_AUTH_ENABLED=true -#ENV VITE_GITHUB_AUTH_ENABLED=true -#ENV VITE_CAPTCHA_ENABLED=true -#ENV VITE_REGISTRATION_DISABLED=True -#ENV VITE_PLAUSIBLE_DATA_URL= -# change working directory -WORKDIR /usr/src/app - - -# copy the package.json files from local machine to the workdir in container -COPY package*.json ./ -COPY pnpm-lock.yaml ./ - -# run npm install in our local machine -RUN corepack enable && corepack prepare pnpm@8.14.0 --activate && pnpm i - -# copy the generated modules and all other files to the container -COPY . . - -# build the application -RUN pnpm run build - -### Serve Step -# pull the Node.js Docker image -FROM node:19-bullseye-slim - -# change working directory -WORKDIR /app -COPY --from=builder /usr/src/app/package.json . -COPY --from=builder /usr/src/app/pnpm-lock.yaml . -RUN corepack enable && corepack prepare pnpm@8.14.0 --activate && pnpm i -# copy files from previous step -COPY --from=builder /usr/src/app/build . -COPY --from=builder /usr/src/app/node_modules ./node_modules - -# our app is running on port 3000 within the container, so need to expose it -EXPOSE 3000 - -# the command that starts our app -CMD ["pnpm", "run", "run:prod"] diff --git a/frontend/package (copy).json b/frontend/package (copy).json deleted file mode 100644 index 28e3072..0000000 --- a/frontend/package (copy).json +++ /dev/null @@ -1,109 +0,0 @@ -{ - "name": "frontend", - "version": "0.0.1", - "scripts": { - "dev": "vite dev", - "build": "NODE_ENV=production vite build", - "package": "vite package", - "preview": "vite preview", - "check": "svelte-check --tsconfig ./tsconfig.json", - "check:watch": "svelte-check --tsconfig ./tsconfig.json --watch", - "lint": "prettier --ignore-path .gitignore --check --plugin-search-dir=. . '!src/lib/i18n/locales/*.json' '!pnpm-lock.yaml' '!src/app.html' && eslint --ignore-path .gitignore .", - "lint-without-format-checking": "eslint --ignore-path .gitignore .", - "format": "prettier --ignore-path .gitignore --write --plugin-search-dir=. . '!src/lib/i18n/locales/*.json'", - "run:prod": "node index.js", - "translations-scan": "i18next-scanner --config i18next-scanner.config.engine.cjs src/**/*.svelte" - }, - "devDependencies": { - "@beyonk/svelte-mapbox": "^9.0.5", - "@ckeditor/ckeditor5-autoformat": "^37.1.0", - "@ckeditor/ckeditor5-basic-styles": "^37.1.0", - "@ckeditor/ckeditor5-build-balloon": "^37.1.0", - "@ckeditor/ckeditor5-editor-balloon": "^37.1.0", - "@ckeditor/ckeditor5-essentials": "^37.1.0", - "@ckeditor/ckeditor5-theme-lark": "^37.1.0", - "@felte/reporter-tippy": "^1.1.5", - "@felte/validator-yup": "^1.0.11", - "@ffmpeg/core": "^0.11.0", - "@ffmpeg/ffmpeg": "^0.11.6", - "@fontsource/marck-script": "^4.5.11", - "@sentry/browser": "^7.48.0", - "@sentry/tracing": "^7.48.0", - "@simplewebauthn/browser": "^7.2.0", - "@sveltejs/adapter-auto": "^2.0.0", - "@sveltejs/adapter-node": "^1.2.3", - "@sveltejs/kit": "^1.15.7", - "@tailwindcss/typography": "^0.5.9", - "@types/canvas-confetti": "^1.6.0", - "@types/cookie": "^0.5.1", - "@types/dompurify": "^3.0.2", - "@types/js-cookie": "^3.0.3", - "@types/luxon": "^3.3.0", - "@types/marked": "^4.3.0", - "@types/qrcode": "^1.5.0", - "@types/sortablejs": "^1.15.1", - "@types/ua-parser-js": "^0.7.36", - "@typescript-eslint/eslint-plugin": "^5.59.0", - "@typescript-eslint/parser": "^5.59.0", - "@unlazy/svelte": "^0.8.9", - "@uppy/compressor": "^1.0.2", - "@uppy/core": "^3.2.0", - "@uppy/dashboard": "^3.4.0", - "@uppy/drag-drop": "^3.0.2", - "@uppy/drop-target": "^2.0.1", - "@uppy/image-editor": "^2.1.2", - "@uppy/progress-bar": "^3.0.2", - "@uppy/status-bar": "^3.1.1", - "@uppy/svelte": "^3.0.2", - "@uppy/xhr-upload": "^3.2.0", - "autoprefixer": "^10.4.14", - "canvas-confetti": "^1.6.0", - "cookie": "^0.5.0", - "crypto-js": "^4.2.0", - "cssnano": "^6.0.0", - "dompurify": "^3.0.3", - "eslint": "^8.38.0", - "eslint-config-prettier": "^8.8.0", - "eslint-plugin-svelte3": "^4.0.0", - "felte": "^1.2.7", - "fuse.js": "^6.6.2", - "highlight.js": "^11.7.0", - "i18next": "^22.4.15", - "i18next-browser-languagedetector": "^7.0.1", - "js-cookie": "^3.0.1", - "jws": "^4.0.0", - "luxon": "^3.3.0", - "mapbox-gl": "^2.14.1", - "marked": "^5.0.0", - "mdsvex": "^0.10.6", - "minisearch": "^6.0.1", - "pikaso": "^2.7.6", - "plausible-tracker": "^0.3.8", - "postcss": "^8.4.31", - "postcss-import": "^15.1.0", - "postcss-load-config": "^4.0.1", - "prettier": "^2.8.8", - "prettier-plugin-svelte": "^2.10.1", - "qrcode": "^1.5.1", - "sass": "^1.62.0", - "socket.io-client": "^4.6.1", - "svelte": "^3.58.0", - "svelte-check": "^3.2.0", - "svelte-preprocess": "^5.0.3", - "svelte-range-slider-pips": "^2.1.1", - "svelte-tippy": "^1.3.2", - "swiper": "^8.4.7", - "tailwindcss": "^3.3.1", - "thumbhash": "^0.1.1", - "tinykeys": "^2.1.0", - "tippy.js": "^6.3.7", - "tslib": "^2.5.0", - "typescript": "~5.0.4", - "ua-parser-js": "^1.0.35", - "vite": "^4.2.3", - "vite-plugin-cross-origin-isolation": "^0.1.6", - "vite-plugin-iso-import": "^1.0.0", - "yup": "^1.1.1" - }, - "type": "module" -} diff --git a/frontend/src/lib/components/BACKUPcommandpalette (copy).svelteBACKUP b/frontend/src/lib/components/BACKUPcommandpalette (copy).svelteBACKUP deleted file mode 100644 index a178e3a..0000000 --- a/frontend/src/lib/components/BACKUPcommandpalette (copy).svelteBACKUP +++ /dev/null @@ -1,279 +0,0 @@ - - - - -{#if open} -
-
-
-

- {bg_text} -

- -
-
- {#each visible_items as vi, i} -
(selected = i)} - on:mousedown={execute_action} - > -
-

{vi.title}

-

- /{vi.command} - {#if vi.args} - {#each vi.args as arg} - {{arg}}{/each} - {/if} -

-
-

{vi.description}

-
- {/each} -
-
-
-{/if} diff --git a/frontend/src/routes/BACKUP+page (copy).svelteBACKUP b/frontend/src/routes/BACKUP+page (copy).svelteBACKUP deleted file mode 100644 index d9406b4..0000000 --- a/frontend/src/routes/BACKUP+page (copy).svelteBACKUP +++ /dev/null @@ -1,555 +0,0 @@ - - - - - - ClassQuiz - {$t('index_page.meta.title')} - - ClassQuiz - Home - - - - - - - - - - - - - - - - - -
-
-
-

ClassQuiz

-

{$t('index_page.slogan')}

-
-
-
-

{$t('index_page.how_does_classquiz_work')}

- -
-

- {$t('index_page.get_a_quiz')} -

-
-
-
-
- {#if selected_create_thing === SelectedCreateThing.Create} - Screenshot of the import-page showing an URL to Kahoot! entered - {:else if selected_create_thing === SelectedCreateThing.Find} - Screenshot of the search-page showing one found quiz for the term 'Country' - {:else if selected_create_thing === SelectedCreateThing.Import} - Screenshot of the import-page showing an URL to Kahoot! entered - {:else} -

Shouldn't happen!

- {/if} -
-
-
-
{ - selected_create_thing = SelectedCreateThing.Create; - }} - class:shadow-2xl={selected_create_thing === SelectedCreateThing.Create} - class:opacity-70={selected_create_thing !== SelectedCreateThing.Create} - > -
- - - -
-
{$t('words.create')}
-

{$t('index_page.create_a_quiz_from_scratch')}

-
-
{ - selected_create_thing = SelectedCreateThing.Find; - }} - class:shadow-2xl={selected_create_thing === SelectedCreateThing.Find} - class:opacity-70={selected_create_thing !== SelectedCreateThing.Find} - > -
- - - -
-
{$t('words.find')}
-

{$t('index_page.find_or_explore')}

-
- -
-
-
- -
-
-

- {$t('index_page.play_quiz')} -

-
- -
-
-
- {#if selected_play_thing === SelectedPlayThing.Select} - Screenshot of the screen where an answer can be selected - {:else if selected_play_thing === SelectedPlayThing.Results} - Screenshot of the results with a table showing how many players chose which answer - {:else if selected_play_thing === SelectedPlayThing.Winners} - Screenshot of the import-page showing an URL to Kahoot! entered - {:else} -

Shouldn't happen!

- {/if} -
-
-
-
{ - selected_play_thing = SelectedPlayThing.Select; - }} - class:shadow-2xl={selected_play_thing === SelectedPlayThing.Select} - class:opacity-70={selected_play_thing !== SelectedPlayThing.Select} - > -
- - - -
-
{$t('index_page.select_answer')}
-

{$t('index_page.choose_answer_wisely')}

-
-
{ - selected_play_thing = SelectedPlayThing.Results; - }} - class:shadow-2xl={selected_play_thing === SelectedPlayThing.Results} - class:opacity-70={selected_play_thing !== SelectedPlayThing.Results} - > -
- - - -
-
{$t('index_page.view_results')}
-

{$t('index_page.check_if_chosen_wisely')}

-
-
{ - selected_play_thing = SelectedPlayThing.Winners; - }} - class:shadow-2xl={selected_play_thing === SelectedPlayThing.Winners} - class:opacity-70={selected_play_thing !== SelectedPlayThing.Winners} - > -
- - - -
-
{$t('index_page.list_winners')}
-

{$t('index_page.get_ranking_and_winners')}

-
-
-
-
- -
-
-

- {$t('index_page.why_classquiz')} -

-
- -
-
-
-

- {classquiz_reasons[selected_classquiz_reason].content} -

-
-
-
- {#each classquiz_reasons as reason, index} -
{ - selected_classquiz_reason = index; - }} - class:shadow-2xl={selected_classquiz_reason === index} - class:opacity-70={selected_classquiz_reason !== index} - > -
{reason.headline}
-
- {/each} -
-
-
-
-{#if newsletterModalOpen} -
- -
-{/if} -