5.8 KiB
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-publicnetwork) - Traefik routes by hostname label using
traefik.docker.network=traefik-public - All other services (backend, db, cache, frontend) on
commander-forge_internalnetwork - Cloudflared runs on host network — must use
localhostnot container hostnames
Image Management
All three custom images are built manually on the server and Portainer uses pre-built images:
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
passlibreplaced withbcrypt==4.1.3in requirements.txt and security.pynpm cireplaced withnpm installin frontend Dockerfile (Windows-generated lockfile missing Linux binaries)package-lock.jsonadded to repo- nginx baked into its own image via
nginx/Dockerfile(Portainer pre-creates volume mount paths as directories) docker-compose.ymluses Traefik labels +traefik.docker.network=traefik-publicDATABASE_URLandREDIS_URLpassed explicitly as env varsUserRoleenum members renamed to lowercase (pending/approved/admin) to match database valuesadmin_bootstrap.pyanddeps.pyupdated to use lowercaseUserRole.admin,UserRole.pending, etc.- Portainer git source caches aggressively — always use
--no-cache --pulland 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
localhostnot 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:
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:
backend:
image: commander-forge-backend:latest
frontend:
image: commander-forge-frontend:latest
nginx:
image: commander-forge-nginx:latest