deploy: add bootstrap.sh + Caddyfile + systemd unit + demo pool

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.
This commit is contained in:
ameer
2026-05-02 20:13:40 +08:00
parent 0480d1528c
commit 7001a51803
5 changed files with 228 additions and 26 deletions

View File

@@ -1,45 +1,77 @@
# Live In-Lecture Quiz Portal
# Live in-lecture quiz portal
FastAPI, SQLite, WebSocket, and vanilla frontend implementation for a live classroom quiz.
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.
## Install
## Quick local run
```bash
cd /home/ameer/RD/Projects/Apps/quiz
python3 -m venv .venv
. .venv/bin/activate
pip install -e '.[dev]'
cp .env.example .env
```
Edit `.env` and set real values for `QUIZ_SECRET_KEY` and `QUIZ_ADMIN_PASSWORD`.
## Run
```bash
. .venv/bin/activate
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, then create a session. Use the displayed join URL in another browser or private window for the student view.
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.
## Test
## 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
. .venv/bin/activate
pytest -q
pytest --cov=app
```
The load simulation test creates 50 student WebSocket clients and runs a 5-question quiz.
For the WebSocket adversarial stress harness (Node.js + Playwright,
runs in a tmux loop), see `tests/stress/README.md`.
## Manual Smoke Test
## Spec
```bash
export QUIZ_DB_PATH=/tmp/quiz-smoke.db QUIZ_SECRET_KEY=smoke-secret QUIZ_ADMIN_PASSWORD=smoke-pass QUIZ_PUBLIC_URL=http://127.0.0.1:8001
. .venv/bin/activate
uvicorn app.main:app --host 127.0.0.1 --port 8001
curl http://127.0.0.1:8001/healthz
```
Expected health response starts with `{"ok":true`.
`SPEC.md` documents the locked v1.0 design (state machine, scoring,
identity flow, all WS message types).