- Install dependencies:
python -m pip install -r requirements.txt
- Run the application:
Note: The application runs on port 5000 by default.
python src/app.py
The included Dockerfile builds a production-ready image using Gunicorn.
- Build the image:
docker build -t racemanager . - Run the container:
# Example running with local credentials mounted and environment variables set docker run -d -p 8080:8080 \ --env FLASK_SECRET="your-secret" \ --env USE_FIRESTORE=1 \ --env GOOGLE_CLOUD_PROJECT="racemanager-482418" \ --env FB_API_KEY="your-api-key" \ --env FB_APP_ID="your-app-id" \ --env GOOGLE_APPLICATION_CREDENTIALS="/app/config/service-account.json" \ --env FIREBASE_CREDENTIALS="/app/config/service-account.json" \ --volume /path/to/local/config:/app/config \ --volume /path/to/local/qr:/app/src/static/qr \ racemanager
For production environments not using Docker, use a WSGI server.
Gunicorn is included in requirements.txt.
export PORT=8080
export WEB_CONCURRENCY=3
gunicorn -w ${WEB_CONCURRENCY} -b 0.0.0.0:${PORT} --timeout 120 --access-logfile - --error-logfile - "src.app:app"Gunicorn does not run on Windows. Use waitress instead.
- Install Waitress:
pip install waitress
- Run the application:
# PowerShell $Env:PORT=8080 waitress-serve --port=$Env:PORT "src.app:app" # Command Prompt set PORT=8080 waitress-serve --port=%PORT% "src.app:app"
To access the administrative interface for the first time:
- Ensure
auth_config.jsonis present in yourCONFIG_DIRand contains anadmin_token. - Navigate to
/login?token=<your-admin-token>(e.g.,http://localhost:5000/login?token=secret-admin-token). - Once logged in as Admin, you can access the dashboard at
/admin/uito manage races, users, and settings.
The application is configured via the following environment variables:
| Variable | Default | Description |
|---|---|---|
FLASK_SECRET or APP_SECRET |
Random string | Secret key for Flask sessions. Must be set in production. |
SESSION_COOKIE_SECURE |
0 |
Set to 1, true, or True to require HTTPS for session cookies. |
SESSION_COOKIE_SAMESITE |
Lax |
SameSite attribute for session cookies. |
LOG_LEVEL |
INFO |
Logging level (e.g., DEBUG, INFO, WARNING, ERROR). |
DEFAULT_RACE |
demo |
The default race ID to load if none is specified. |
CONFIG_DIR |
config |
Directory path for local JSON configuration and data files. |
USE_FIRESTORE |
0 |
Set to 1, true, or yes to use Google Cloud Firestore for storage. |
NUM_LANES |
4 |
Number of lanes on the track. |
CACHE_TTL |
30 |
In-memory cache duration in seconds for race data. |
FIREBASE_CREDENTIALS |
None | Path to Firebase Admin SDK service account JSON (required for local dev; Cloud Run uses ADC). |
FIREBASE_API_CREDENTIALS |
None | Path to a JSON file containing Firebase Web SDK config (apiKey, appId, etc.). |
FB_API_KEY |
DEFAULT_API_KEY |
Fallback Firebase Web API Key if credentials file is missing. |
FB_APP_ID |
DEFAULT_APP_ID |
Fallback Firebase Web App ID if credentials file is missing. |
FLASK_DEBUG or DEBUG |
0 |
Set to 1, true, or yes to enable Flask debug mode. |
The application looks for the following JSON files in the CONFIG_DIR (default: config/):
patrol_config.json: Defines the mapping of patrol IDs to names.{ "1": "Foxes", "2": "Hawks", ... }auth_config.json: Contains legacy authentication tokens and optional Firebase configuration.{ "owner_token": "secret-owner-token", "judge_token": "secret-judge-token", "admin_token": "secret-admin-token", "firebase": { "apiKey": "...", ... } }race_data_{race_id}.json: Stores the complete state for a specific race (participants, heats, times) whenUSE_FIRESTOREis false.race_members_{race_id}.json: Stores user roles (VIEWER, OWNER, JUDGE, ADMIN) for a specific race whenUSE_FIRESTOREis false.race_archive_{race_id}.json: Stores archived race data whenUSE_FIRESTOREis false.
This application leverages the following GCP services:
- Cloud Run: Serverless container execution.
- Artifact Registry: Storage for Docker container images.
- Firestore (Native Mode): NoSQL database for race data and user roles.
- Secret Manager: Secure storage for application secrets.
- GCP Project: Ensure you have a project (e.g.,
racemanager-482418). - Storage Buckets: Create two buckets: one for private config and one for public QR codes.
racemanager-config(Private)racemanager-qr(Public/Static assets)
- Service Account: Create a service account (e.g.,
RaceManager-svc-acct) with the following IAM roles:Cloud Run Invoker: To allow access to the service.Cloud Datastore User: To read/write to Firestore.Storage Object Viewer: To pull images from Artifact Registry (if private).Secret Manager Secret Accessor: If using Secret Manager.
# Enable services
gcloud services enable run.googleapis.com artifactregistry.googleapis.com firestore.googleapis.com
# Create repository (one-time)
gcloud artifacts repositories create racemanager-images --repository-format=docker --location=us-east4
# Configure auth
gcloud auth configure-docker us-east4-docker.pkg.dev
# Build and Push
export PROJECT_ID="racemanager-482418"
docker build -t us-east4-docker.pkg.dev/${PROJECT_ID}/racemanager-images/racemanager:latest .
docker push us-east4-docker.pkg.dev/${PROJECT_ID}/racemanager-images/racemanager:latestgcloud run deploy race-manager \
--image us-east4-docker.pkg.dev/${PROJECT_ID}/racemanager-images/racemanager:latest \
--platform managed \
--region us-east4 \
--allow-unauthenticated \
--service-account RaceManager-svc-acct@${PROJECT_ID}.iam.gserviceaccount.com \
--set-env-vars "USE_FIRESTORE=1" \
--set-secrets "FLASK_SECRET=flask-secret:latest,FB_API_KEY=firebase-api-key:latest,FB_APP_ID=firebase-app-id:latest" \
--add-volume name=config-storage,type=cloud-storage,bucket=racemanager-config \
--add-volume name=qr-storage,type=cloud-storage,bucket=racemanager-qr \
--add-volume-mount volume=config-storage,mount-path=/app/config \
--add-volume-mount volume=qr-storage,mount-path=/app/src/static/qr- Project Link: Link your GCP project to Firebase in the Firebase Console.
- Authentication: Enable Authentication providers (Google, Email/Password).
- Firestore: Ensure the database is created in Native mode.
- Note: The application stores data at
races/{raceId}.
- Note: The application stores data at
- Security Rules: Deploy
firestore.rulesto secure your database.firebase deploy --only firestore:rules
- Client Config: Update
config/auth_config.jsonor setFB_API_KEYandFB_APP_IDenvironment variables so the frontend can initialize the Firebase SDK.
For production, avoid passing secrets as plain environment variables.
- Create a secret in Secret Manager (e.g.,
flask-secret). - Grant the Cloud Run service account access to the secret.
- Mount the secret as an environment variable in Cloud Run:
gcloud run deploy race-manager ... --update-secrets=FLASK_SECRET=flask-secret:latest