2026-10-03 19:31:38 +02:00
|
|
|
#!/usr/bin/env bash
|
2026-11-01 12:07:04 +01:00
|
|
|
# Production startup (gunicorn). Pick the service and port:
|
2026-10-03 19:31:38 +02:00
|
|
|
#
|
2026-11-01 12:07:04 +01:00
|
|
|
# ./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):
|
2026-10-03 20:24:37 +02:00
|
|
|
# 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.
|
2026-10-03 19:31:38 +02:00
|
|
|
#
|
2026-11-01 12:07:04 +01:00
|
|
|
# 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).
|
2026-10-03 19:31:38 +02:00
|
|
|
set -euo pipefail
|
|
|
|
|
cd "$(dirname "$0")"
|
|
|
|
|
|
2026-11-01 12:07:04 +01:00
|
|
|
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}"
|
2026-10-03 19:31:38 +02:00
|
|
|
|
2026-11-01 20:04:33 +01:00
|
|
|
# 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
|
|
|
|
|
|
2026-11-01 12:07:04 +01:00
|
|
|
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
|
2026-10-03 19:31:38 +02:00
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
2026-11-01 12:07:04 +01:00
|
|
|
# Each service runs migrations for its own database.
|
2026-10-03 19:31:38 +02:00
|
|
|
if [ -x ".venv/bin/alembic" ]; then
|
2026-11-01 12:07:04 +01:00
|
|
|
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
|
2026-10-03 19:31:38 +02:00
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if [ -x ".venv/bin/gunicorn" ]; then
|
|
|
|
|
GUNICORN=".venv/bin/gunicorn"
|
|
|
|
|
else
|
|
|
|
|
GUNICORN="gunicorn"
|
|
|
|
|
fi
|
|
|
|
|
|
2026-11-01 12:07:04 +01:00
|
|
|
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()"
|