#!/usr/bin/env bash # Production startup (gunicorn). Pick the service and port: # # ./run-prod.sh # main app on 8000 # ./run-prod.sh main 8000 # main app on 8000 # ./run-prod.sh sensors 8001 # sensors app on 8001 (localhost only) # # SECRET_KEY resolution (main app sessions): # 1. SECRET_KEY environment variable (takes precedence), # 2. .secret_key file next to this script (created once on first start), # 3. otherwise a random key is generated and saved to .secret_key. # # The sensors API is open to every user on the local machine. To expose it # on the network explicitly set HOST=0.0.0.0 (or BIND). set -euo pipefail cd "$(dirname "$0")" APP="${1:-${APP:-main}}" PORT="${2:-${PORT:-}}" case "$APP" in main) MODULE="app:create_app" DEFAULT_PORT=8000 DEFAULT_HOST="0.0.0.0" ;; sensors) MODULE="sensors_app:create_app" DEFAULT_PORT=8001 DEFAULT_HOST="127.0.0.1" ;; *) echo "Usage: $0 [main|sensors] [port]" >&2 exit 2 ;; esac PORT="${PORT:-$DEFAULT_PORT}" HOST="${HOST:-$DEFAULT_HOST}" # Prevent the main app from proxying to itself. Without an explicit # SENSORS_API_URL the client targets localhost:8001, so main must not listen # there as well. if [ "$APP" = "main" ] && [ -z "${SENSORS_API_URL:-}" ] && [ "$PORT" = "8001" ]; then echo "Refusing to start main on port 8001: SENSORS_API_URL would default to" >&2 echo "http://localhost:8001 and the app would call itself recursively." >&2 echo "Use another port (for example 8000) or set SENSORS_API_URL explicitly." >&2 exit 2 fi if [ "$APP" = "main" ]; then SECRET_FILE=".secret_key" if [ -z "${SECRET_KEY:-}" ]; then if [ -f "$SECRET_FILE" ]; then SECRET_KEY="$(cat "$SECRET_FILE")" else SECRET_KEY="$(python3 -c 'import secrets; print(secrets.token_hex(32))')" printf '%s' "$SECRET_KEY" > "$SECRET_FILE" chmod 600 "$SECRET_FILE" echo "Generated a new SECRET_KEY, saved to $SECRET_FILE" >&2 fi export SECRET_KEY fi fi # Each service runs migrations for its own database. if [ -x ".venv/bin/alembic" ]; then ALEMBIC=".venv/bin/alembic" else ALEMBIC="alembic" fi if [ "$APP" = "main" ]; then "$ALEMBIC" -c alembic_users.ini upgrade head else "$ALEMBIC" -c alembic.ini upgrade head fi if [ -x ".venv/bin/gunicorn" ]; then GUNICORN=".venv/bin/gunicorn" else GUNICORN="gunicorn" fi export BIND="${BIND:-$HOST:$PORT}" echo "Starting $APP app on http://$BIND" >&2 # Gunicorn needs the factory call syntax ("module:create_app()"), otherwise it # treats create_app itself as the WSGI callable. exec "$GUNICORN" -c gunicorn.conf.py "$MODULE()"