#!/usr/bin/env bash set -Eeuo pipefail DEV_MODE="${DEV_MODE:-false}" MAIN_HOST="${HOST:-0.0.0.0}" MAIN_PORT="${PORT:-7860}" MODEL_HOST="${MODEL_HOST:-0.0.0.0}" MODEL_PORT="${MODEL_PORT:-8001}" TAILWIND_BIN="/app/bin/tailwindcss" TAILWIND_IN="/app/public/styles/input.css" TAILWIND_OUT="/app/public/styles/output.css" log() { printf "%s %s\n" "$(date +'%Y-%m-%dT%H:%M:%S%z')" "$*"; } ensure_tailwind() { if [[ -x "${TAILWIND_BIN}" ]]; then log "Tailwind CSS found at ${TAILWIND_BIN}" return fi log "Tailwind CSS not found, downloading binary..." mkdir -p /app/bin local os arch os="$(uname -s | tr '[:upper:]' '[:lower:]')" arch="$(uname -m)" case "${os}" in darwin) os="macos" ;; linux) os="linux" ;; *) log "Unsupported OS: ${os}"; exit 1 ;; esac case "${arch}" in x86_64|amd64) arch="x64" ;; arm64|aarch64) arch="arm64" ;; *) log "Unsupported arch: ${arch}"; exit 1 ;; esac curl -sSLo "${TAILWIND_BIN}" \ "https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-${os}-${arch}" chmod +x "${TAILWIND_BIN}" } build_tailwind() { if [[ "${DEV_MODE}" == "true" ]]; then log "Building Tailwind (initial)..." "${TAILWIND_BIN}" -i "${TAILWIND_IN}" -o "${TAILWIND_OUT}" log "Starting Tailwind in watch mode (dev)..." "${TAILWIND_BIN}" -i "${TAILWIND_IN}" -o "${TAILWIND_OUT}" --watch=always --poll & export TAILWIND_PID=$! else log "Building Tailwind (prod)..." "${TAILWIND_BIN}" -i "${TAILWIND_IN}" -o "${TAILWIND_OUT}" --minify fi } download_dataset() { if ! PYTHONPATH=/app poetry run python /app/scripts/download_dataset.py; then log "WARN: dataset download failed; continuing without cached dataset." fi } start_model_server() { if [[ "${DEV_MODE}" == "true" ]]; then log "DEV mode: NOT starting model server. Open VS Code and press F5 (Run and Debug) to launch it." return 0 fi if [[ -n "${MODEL__ITEM_MODEL_PATH:-}" || -n "${MODEL__SCALE_MODEL_PATH:-}" ]]; then log "Starting model server on ${MODEL_HOST}:${MODEL_PORT}..." poetry run uvicorn src.servers.model_server:app \ --host "${MODEL_HOST}" \ --port "${MODEL_PORT}" \ --workers "${MODEL_WORKERS:-1}" & export MODEL_PID=$! else log "Model paths not configured — NOT starting model server." fi } start_main_app() { if [[ "${DEV_MODE}" == "true" ]]; then log "DEV mode: NOT starting main FastAPI app. Open VS Code and press F5 (Run and Debug) to launch it." return 0 fi log "PROD mode: starting Gunicorn (Uvicorn worker) on ${MAIN_HOST}:${MAIN_PORT}..." exec poetry run gunicorn src.main:app \ -k uvicorn.workers.UvicornWorker \ -w "${WEB_CONCURRENCY:-2}" \ -b "${MAIN_HOST}:${MAIN_PORT}" } cleanup() { log "Shutting down..." if [[ -n "${TAILWIND_PID:-}" ]]; then log "Stopping Tailwind (PID ${TAILWIND_PID})" kill "${TAILWIND_PID}" 2>/dev/null || true fi if [[ -n "${MODEL_PID:-}" ]]; then log "Stopping model server (PID ${MODEL_PID})" kill "${MODEL_PID}" 2>/dev/null || true fi } trap cleanup SIGINT SIGTERM if [[ "${DEV_MODE}" == "true" ]]; then log "Starting services in DEVELOPMENT mode..." else log "Starting services in PRODUCTION mode..." fi ensure_tailwind download_dataset build_tailwind start_model_server start_main_app if [[ "${DEV_MODE}" == "true" ]]; then log "Dev services running. Press Ctrl+C to stop." wait fi