Self-Hosting

Since ClassQuiz is open-source, it can also be self-hosted.

Warning

Although some versions are already released, I would recommend to run the latest commit (where checks pass) from the master-branch.

Requirements

Software

3rd-Parties

Required

Optional

Installation

At first, clone the repo:

git clone https://github.com/mawoka-myblock/classquiz && cd ClassQuiz

Now, you'll configure your frontend. You'll have to change the following in frontend/Dockerfile:

  • VITE_MAPBOX_ACCESS_TOKEN: A Mapbox-token which is optional.
  • VITE_HCAPTCHA: The hCaptcha-Siteky for captchas
  • VITE_CAPTCHA_ENABLED: Set it to true, if the captcha should be available
  • VITE_SENTRY: A Sentry-DSN for Sentry (optional)
  • VITE_GOOGLE_AUTH_ENABLED: Set it to true, if Google-Auth is set up. Otherwise, leave it unset.
  • VITE_GITHUB_AUTH_ENABLED: Set it to true, if GitHub-Auth is set up. Otherwise, leave it unset.

Configuration

Storage Provider

You'll have to set up a storage provider for some pictures (these getting imported from KAHOOT!). For now, you can use Deta or the local filesystem. Please note that I would NOT use the local file system because of Path Traversals. I tried to prevent these attacks, but I really wouldn't trust it, because it's a regex! You'll have to set the STORAGE_BACKEND-environment-variable to either deta or local.

If you chose Deta...

...you'll also have to set the DETA_PROJECT_KEY and the DETA_PROJECT_ID.

If you chose the local filesystem...

...you'll have to set the STORAGE_PATH environment variable. The path must be absolute (so start with a /).

Before you can start your stack, you have to set some environment-variables in your docker-compose.yml.

GitHub/Google-Auth

This step is purely optional, but it will enable users to log in using their Google/GitHub-accounts.

Google

First, go to console.cloud.google.com/apis/dashboard and create a new project and select it. Then, go to the "OAuth consent screen" and set it up. Next, go to the "Credentials"-tab and click on "Create Credentials" and create a new "OAuth Client ID". This ID should be from the application-type "Web application". Afterwards, add a new "Authorised JavaScript origin", which is just the base-domain (with https) of your ClassQuiz-installation. Then, add a new "Authorised redirect URI". This URI will have the following scheme:

https://[BASE_URL]/api/v1/users/oauth/google/auth

You're done! Not the client-secret and the client-id down, you'll need it later.

GitHub

First, go to github.com/settings/developers and create a "new OAuth App". The "Authorization callback URL" has the following schema:

https://[BASE_URL]/api/v1/users/oauth/github/auth

That's it. Click on "Register application" and generate a new client secret and save it for later, together with your client-id.

Docker-Compose File

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 # don't change
      API_URL: http://api:80 # don't change
  api:
    build:
      context: .
      dockerfile: Dockerfile
    restart: always
    depends_on:
      - db
      - redis

    environment:
      ROOT_ADDRESS: "https://classquiz.de" # Base-URL (change it)
      DB_URL: "postgresql://postgres:classquiz@db:5432/classquiz" # don't change
      MAIL_ADDRESS: "classquiz@mawoka.eu" # Email-Address (change it)
      MAIL_PASSWORD: "MAIL_PASSWORD" # Email-Password (change it)
      MAIL_USERNAME: "classquiz@mawoka.eu" # Email-Username (change it)
      MAIL_SERVER: "smtp.gmail.com" # SMTP-Server (change it)
	  MAIL_PORT: "587" # SMTP-Port
	  SKIP_EMAIL_VERIFICATION: True # Set this to skip sending emails
      MAX_WORKERS: "1" # Very important and don't change it!
      REDIS: "redis://redis:6379/0?decode_responses=True" # don't change
      SECRET_KEY: "TOP_SECRET" # openssl rand -hex 32
	  MEILISEARCH_URL: "http://meilisearch:7700" # don't change
      ACCESS_TOKEN_EXPIRE_MINUTES: 30 # don't change
      HCAPTCHA_KEY: "" # Private hCaptcha key for verification (change it)
	  STORAGE_BACKEND: "deta" # MUST BE EITHER "deta" OR "local"

	  # If STORAGE_BACKEND is "deta"
	  DETA_PROJECT_KEY: "YOUR_DETA_PROJECT_KEY"
	  DETA_PROJECT_ID: "YOUR_DETA_PROJECT_ID"

	  # If STORAGE_BACKEND is "local"
	  STORAGE_PATH: "/var/storage"

	  # 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.


  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" # Adjust the 8000 to your needs

  meilisearch:
    image: getmeili/meilisearch:latest
    restart: always
    environment:
      MEILI_NO_ANALYTICS: true
    volumes:
      - meilisearch-data:/data.ms
volumes:
  data:
  meilisearch-data:
	

Run the following command to generate and set the secret up automatically

sed -i "s/TOP_SECRET/$(openssl rand -hex 32)/g" docker-compose.yml

Now build and deploy:

docker compose build && docker compose up -d

ClassQuiz needs HTTPS/SSL to work properly!

Enjoy! ❤️

Made with ❤️ by Mawoka and with the help of others.
If you find this useful, please consider donating. More details here.

Consider following Mastodon @classquiz@fosstodon.org for updates!

Kahoot! and the K! logo are trademarks of Kahoot! AS