Files
2026-07-29 16:30:14 +02:00

97 lines
3.2 KiB
Markdown

# WikiTCG
A trading card game where every card is procedurally generated from a Wikipedia
article. Players open booster packs, build decks, trade with each other, and
play real-time matches over WebSocket.
- **Backend** — FastAPI (`backend/`), PostgreSQL, SQLAlchemy + Alembic
- **Frontend** — SvelteKit with the static adapter (`frontend/`)
- **External** — Wikipedia API (card generation), Resend (email), Stripe (payments)
## Running locally
```bash
# Backend — dev server on :8000
cd backend
pip install -r requirements.txt
uvicorn main:app --reload
# Frontend — dev server on :5173
cd frontend
npm install
npm run dev
```
Tests and checks:
```bash
cd backend && pytest test_game.py # game logic
cd frontend && npm run check # svelte-check
```
## Deployment
The stack runs as three Docker Compose services (`db`, `backend`, `frontend`)
behind Nginx Proxy Manager. No service publishes a port; NPM reaches the
containers by name over an external `proxy` network. The database sits on a
second network marked `internal: true`, so it has no route off the host.
```bash
git pull && docker compose up -d --build
```
## Database and migrations
The database has **two roles, and they are not interchangeable**:
| Role | Used by | Rights |
| --- | --- | --- |
| `wikitcg_app` | the running app, via `DATABASE_URL` | SELECT/INSERT/UPDATE/DELETE — no DDL |
| `wikitcg` | Alembic only, via `MIGRATION_DATABASE_URL` | owns the schema |
Migrations therefore run as `wikitcg`, never as the app role:
```bash
docker compose run --rm backend alembic upgrade head # apply
docker compose run --rm backend alembic revision --autogenerate -m "description"
```
`alembic/env.py` reads `MIGRATION_DATABASE_URL` and **fails loudly if it is
unset**. There is deliberately no fallback to `DATABASE_URL`: the app role has no
DDL rights, so falling back would surface as a confusing permissions error partway
through a migration instead of an obvious configuration error up front.
`sqlalchemy.url` in `alembic.ini` is intentionally empty — the URL comes from the
environment so no credential lives in version control.
The split needs no sequence grants: every primary key is either a
client-generated UUID or a string.
## Environment variables
Set in `.env` at the repo root (untracked). See `backend/core/config.py`.
**Required** — the app refuses to start without these:
| Variable | Notes |
| --- | --- |
| `JWT_SECRET_KEY` | signing key for access and refresh tokens |
| `DATABASE_URL` | app role (`wikitcg_app`) |
| `RESEND_API_KEY`, `EMAIL_FROM` | transactional email |
| `STRIPE_SECRET_KEY`, `STRIPE_PUBLISHABLE_KEY`, `STRIPE_WEBHOOK_SECRET` | payments |
**Required for migrations:**
| Variable | Notes |
| --- | --- |
| `MIGRATION_DATABASE_URL` | schema owner (`wikitcg`) — Alembic only |
**Optional:**
| Variable | Default | Notes |
| --- | --- | --- |
| `FRONTEND_URL` | `http://localhost:5173` | links in outbound email |
| `CORS_ORIGINS` | `http://localhost:5173` | comma-separated |
| `WIKIRANK_USER_AGENT` | `WikiTCG/1.0` | sent to the Wikipedia API |
| `ENABLE_DOCS` | unset (disabled) | set to exactly `true` to serve `/docs`, `/redoc` and `/openapi.json`. Any other value leaves them disabled, so a new environment fails closed. |