# MTG Commander Deck Builder — Project State ## Overview Self-hosted, AI-powered Magic: The Gathering Commander deck builder. Runs as a Docker Compose stack managed via Portainer, exposed externally via Cloudflare Tunnel + Traefik. **Stack:** FastAPI (Python 3.12) · React/Vite (TypeScript) · PostgreSQL 16 · Redis 7 · Nginx · Docker Compose --- ## Build Progress ### ✅ Steps 1-7 — Complete All backend, frontend, and deployment steps are complete. App is live and accessible. --- ## Live Deployment - **URL:** https://commander.bussenet.ca - **Admin login:** busse.daniel@gmail.com / Admin1234 - **Portainer stack:** commander-forge (ID: 54) - **Stack type:** Pre-built images only — no build directives --- ## Deployment Architecture ### Networking - Cloudflare Tunnel → `http://localhost:80` → Traefik → nginx (`traefik-public` network) - Traefik routes by hostname label using `traefik.docker.network=traefik-public` - All other services (backend, db, cache, frontend) on `commander-forge_internal` network - Cloudflared runs on host network — must use `localhost` not container hostnames ### Image Management All three custom images are built manually on the server and Portainer uses pre-built images: ```bash sudo docker build -t commander-forge-nginx:latest --no-cache --pull http://192.168.0.62:3001/Dan/Commander-Deck-App.git#master:nginx sudo docker build -t commander-forge-frontend:latest --no-cache http://192.168.0.62:3001/Dan/Commander-Deck-App.git#master:frontend sudo docker build -t commander-forge-backend:latest --no-cache --pull http://192.168.0.62:3001/Dan/Commander-Deck-App.git#master:backend ``` After rebuilding, redeploy the stack in Portainer to pick up the new images. ### Why pre-built images? Portainer's repository-based builds aggressively cache the git source. Even with `--no-cache --pull`, Portainer's internal git clone cache serves stale code. Pre-built images bypass this entirely. --- ## Known Fixes Applied - `passlib` replaced with `bcrypt==4.1.3` in requirements.txt and security.py - `npm ci` replaced with `npm install` in frontend Dockerfile (Windows-generated lockfile missing Linux binaries) - `package-lock.json` added to repo - nginx baked into its own image via `nginx/Dockerfile` (Portainer pre-creates volume mount paths as directories) - `docker-compose.yml` uses Traefik labels + `traefik.docker.network=traefik-public` - `DATABASE_URL` and `REDIS_URL` passed explicitly as env vars - `UserRole` enum members renamed to lowercase (`pending/approved/admin`) to match database values - `admin_bootstrap.py` and `deps.py` updated to use lowercase `UserRole.admin`, `UserRole.pending`, etc. - Portainer git source caches aggressively — always use `--no-cache --pull` and pre-built images --- ## Infrastructure | Service | URL | Notes | |---------|-----|-------| | Commander Forge | https://commander.bussenet.ca | Main app | | Portainer | https://portainer.bussenet.ca | Stack management | | Gitea | https://gitea.bussenet.ca | Primary git (SSH port 2222) | | GitHub mirror | https://github.com/danbusse/Commander-Deck-App | Private, Claude's file access path | | Vault | https://vault.bussenet.ca | Secrets store | | Portainer MCP | https://mcp-portainer.bussenet.ca/sse | Custom image with entrypoint fix | ### Portainer MCP Custom `mcp-portainer:latest` image built from `ghcr.io/serraniel/portainer-mcp-docker:http` with a fixed entrypoint that passes `--` before the portainer-mcp command. Tools written to `/tmp/tools.yaml`. PORTAINER_SERVER must be set without protocol prefix (e.g. `192.168.0.62:9443`) since the MCP binary always prepends `https://`. ### Cloudflare Tunnel - Tunnel ID: `3a032a2b-aa42-46a2-b749-e3fa9166ac59` - Only region1 (`198.41.192.x`) is reachable — region2 (`198.41.200.x`) times out (ISP routing issue, outside our control) - Tunnel maintains 1 of 4 connections; services are accessible but logs show constant reconnect attempts - Cloudflared runs on host network — all tunnel ingress entries use `localhost` not container IPs ### GitHub API Access Claude can read/write files to the GitHub mirror using token stored in Vault at `secret/github.claude-api-token`. This is the primary mechanism for Claude to update project files between sessions. --- ## Environment Variables (Portainer stack env) | Variable | Value | Notes | |----------|-------|-------| | SECRET_KEY | changeme | ⚠️ Needs replacing | | POSTGRES_PASSWORD | changeme | ⚠️ Needs replacing | | POSTGRES_DB | mtgdb | | | POSTGRES_USER | mtg | | | DATABASE_URL | postgresql+asyncpg://mtg:changeme@db:5432/mtgdb | ⚠️ Update with new password | | REDIS_URL | redis://cache:6379 | | | ANTHROPIC_API_KEY | (in Vault at secret/anthropic) | | | ADMIN_EMAIL | busse.daniel@gmail.com | | | ADMIN_PASSWORD | Admin1234 | | --- ## Git Workflow Two remotes are configured: - `origin` → Gitea (`ssh://git@192.168.0.62:2222/Dan/Commander-Deck-App.git`) - `github` → GitHub (`https://github.com/danbusse/Commander-Deck-App.git`) Always push to both: ```bash git push origin master git push github master ``` --- ## Next Session — Start Here **Priority 1 — Harden credentials:** - Generate new SECRET_KEY: `openssl rand -hex 32` - Set strong POSTGRES_PASSWORD - Update DATABASE_URL to match - Update these in Portainer stack env vars **Priority 2 — Test end to end:** - Try building a deck via Generate mode - Test collection import with an Archidekt export - Verify admin approval flow for a new registered user **Priority 3 — Commit docker-compose.yml changes to repo:** The docker-compose.yml in the repo still has `build:` directives. Update it to use `image:` directives to match the actual deployment approach: ```yaml backend: image: commander-forge-backend:latest frontend: image: commander-forge-frontend:latest nginx: image: commander-forge-nginx:latest ```