97 lines
3.2 KiB
Markdown
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. |
|