A Django app for a homebrew Starfinder TTRPG campaign.
CataclysmApp is a campaign catalog for a customized Starfinder setting.
It documents the world timeline, custom rules, factions, people, worlds, events, ships, vehicles, gear, and other unique lore/items used in this homebrew game.
Set environment variables first (recommended via a local .env file that is not committed):
cat > .env <<'EOF'
DEFAULT_USERNAME=your_username
DEFAULT_PASSWORD=***REPLACE-WITH-SECURE-PASSWORD***
ALLOWED_HOSTS=localhost,127.0.0.1
EOFOr export them in your shell before running compose.
Never commit real credentials. For production, prefer Docker/Portainer secrets or another secure secret-management system.
docker compose up --buildThe app will be available at http://localhost:${PORT:-8000}.
Build:
docker build -t cataclysmapp:latest .Run:
docker run --rm -p 8080:8000 \
-e DEFAULT_USERNAME=admin \
-e DEFAULT_PASSWORD='YourSecurePasswordHere' \
cataclysmapp:latestPORT: Host port published by Docker Compose (default:8000). The container always listens on port8000.GUNICORN_WORKERS: Optional Gunicorn worker count (default:3)GUNICORN_TIMEOUT: Optional Gunicorn timeout seconds (default:120)DEFAULT_USERNAME: Optional default admin username created on startupDEFAULT_PASSWORD: Optional default admin password used when creating the default admin (must satisfy Django password validators: minimum length, non-common, non-numeric, etc.)DEFAULT_PASSWORD_UPDATE: Optional (true/false, defaultfalse); when exactlytrue, updates password for an existingDEFAULT_USERNAMEonly if that user is already staff + superuserALLOWED_HOSTS: Comma-separated Django allowed hosts list (default:localhost,127.0.0.1,[::1]; for examplelocalhost,127.0.0.1,app.example.com)
If you add more published ports later, use separate purpose-based variables (for example APP_HTTP_PORT, APP_METRICS_PORT) instead of reusing one value.
Use Docker/Portainer secrets or a secure environment-variable source for credentials in production.
The container intentionally binds Gunicorn to 0.0.0.0:8000; place it behind a reverse proxy/TLS termination in production.
Treat DEFAULT_PASSWORD_UPDATE=true as a privileged operation and restrict who can modify runtime environment variables.
Use the management command below to pull image URLs from the public crew spreadsheet tabs (Main Crew and Other Crew) and save them to matching Person.image and Species.image records. The command also stores the source URL in Person.image_source_url / Species.image_source_url and supports common Google Drive share-link formats:
cd cataclysm
python manage.py download_sheet_images --spreadsheet-id "https://docs.google.com/spreadsheets/d/YOUR_SPREADSHEET_ID/edit?usp=sharing"download_sheet_images reads sheet cells via the Google Sheets v4 grid-data endpoint so hyperlink URLs embedded in cells are preserved. Set GOOGLE_API_KEY if your environment requires an API key for sheets.googleapis.com.
Useful options:
--tabs "Main Crew" "Other Crew"(override tabs)--person-url-col <index>and--species-url-col <index>(use explicit URL columns)--dry-run(report without writing files)--overwrite(replace existing images)--timeout <seconds>(HTTP timeout per image download, default10)--max-bytes <bytes>(max bytes per image download, default10485760)
Without --overwrite, images are skipped only when the stored image_source_url already matches the current sheet URL; if the sheet URL changed, the image is refreshed.
For security, downloads only support direct http/https image URLs and intentionally do not follow HTTP redirects to reduce SSRF-style redirect abuse.