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