One-shot deploy for fresh Ubuntu 24.04 root SSH: curl -fsSL https://gitea.ahkhan.me/apps/quiz/raw/branch/master/deploy/bootstrap.sh | bash bootstrap.sh: idempotent stage-by-stage installer for Caddy, Python venv, quiz system user, repo clone to /opt/quiz, env-var prompts, systemd unit, Caddyfile, and a healthz check. Reattaches /dev/tty so curl|bash can read the admin password interactively. quiz.service: uvicorn under the quiz system user (no shell, no SSH), ProtectSystem=full, ProtectHome=true, PrivateTmp=true, NoNewPrivileges=true. Caddyfile.tpl: reverse_proxy 127.0.0.1:8001 with auto Let's Encrypt; DOMAIN substituted at install time. examples/pool_example.json: generic demo pool, schema reference only. README rewritten around the deploy flow + class-day lifecycle.
78 lines
2.3 KiB
Markdown
78 lines
2.3 KiB
Markdown
# Live in-lecture quiz portal
|
|
|
|
FastAPI + WebSocket + SQLite quiz portal designed for ~40 students per
|
|
class session. Single-process, in-memory room manager, vanilla HTML/JS
|
|
front-end, Caddy in front for TLS.
|
|
|
|
## Quick local run
|
|
|
|
```bash
|
|
python3 -m venv .venv
|
|
. .venv/bin/activate
|
|
pip install -e '.[dev]'
|
|
cp .env.example .env # edit QUIZ_SECRET_KEY + QUIZ_ADMIN_PASSWORD
|
|
uvicorn app.main:app --host 127.0.0.1 --port 8001 --reload
|
|
```
|
|
|
|
Open `http://127.0.0.1:8001/admin/`, log in, create a quiz pool from a
|
|
JSON pool file (see `examples/pool_example.json` for the schema), create
|
|
a session, and share the join URL.
|
|
|
|
## VPS deploy (one-shot)
|
|
|
|
On a fresh Ubuntu 24.04 LTS root SSH:
|
|
|
|
```bash
|
|
curl -fsSL https://gitea.ahkhan.me/apps/quiz/raw/branch/master/deploy/bootstrap.sh | bash
|
|
```
|
|
|
|
The bootstrap:
|
|
1. apt-installs Caddy + Python venv tooling
|
|
2. Creates a `quiz` system user (no shell, no SSH)
|
|
3. Clones this repo to `/opt/quiz`
|
|
4. Builds the venv and installs the app
|
|
5. Generates `QUIZ_SECRET_KEY`, prompts for `QUIZ_ADMIN_PASSWORD`
|
|
6. Drops the systemd unit and Caddyfile
|
|
7. Starts both services
|
|
8. Curl-checks `127.0.0.1:8001/healthz`
|
|
|
|
After: `quiz.ahkhan.me` is live with auto-Let's-Encrypt cert. To override
|
|
the domain or repo URL, set `DOMAIN=` or `REPO_URL=` in the environment
|
|
before running the script.
|
|
|
|
## Class-day workflow
|
|
|
|
1. Provision Aliyun Intl HK ECS pay-as-you-go (`ecs.t6-c2m1.large`,
|
|
Ubuntu 24.04 LTS).
|
|
2. Point DNS A-record `quiz.ahkhan.me` at the new IP.
|
|
3. SSH in as root, run the curl|bash one-liner above.
|
|
4. Open `quiz.ahkhan.me/admin/`, log in, upload the week's pool JSON,
|
|
create a session.
|
|
5. Share the QR / join URL with the class.
|
|
6. After class:
|
|
`scp root@<ip>:/opt/quiz/quiz.db ./backups/quiz-YYYY-MM-DD.db`
|
|
7. Destroy the instance.
|
|
|
|
## Quiz pool files
|
|
|
|
Real pool JSON files contain answer keys and **must not be committed**
|
|
to this repo. `.gitignore` excludes `examples/*_pool.json` (only
|
|
`examples/pool_example.json` may be tracked). Author pools elsewhere
|
|
(e.g., your course-material directory) and upload at runtime via the
|
|
admin UI.
|
|
|
|
## Tests
|
|
|
|
```bash
|
|
pytest -q
|
|
pytest --cov=app
|
|
```
|
|
|
|
For the WebSocket adversarial stress harness (Node.js + Playwright,
|
|
runs in a tmux loop), see `tests/stress/README.md`.
|
|
|
|
## Spec
|
|
|
|
`SPEC.md` documents the locked v1.0 design (state machine, scoring,
|
|
identity flow, all WS message types).
|