#!/bin/sh
# Start FastAPI backend (:8000) + Next.js frontend (:3000) in one container,
# each under a respawn supervisor.
#
# Why a supervisor: a transient failure of either process — e.g. Postgres
# (:5433) not yet reachable in the seconds after a host reboot — must self-heal
# via capped-backoff restart, instead of leaving the container half-alive
# (Next.js up, FastAPI dead → /api/health returns 503, which is exactly the
# outage that followed the kernel update + reboot on 2026-06-10). Both processes
# log to stdout/stderr so Docker captures everything.

set -u

# Run "$@" forever, restarting it when it exits. Backoff doubles on a fast crash
# (cap 30s) and resets once the process has stayed up for >=30s, so a flapping
# dependency cannot spin the CPU while a genuinely-recovered process restarts
# promptly.
supervise() {
  name="$1"
  shift
  backoff=1
  while true; do
    echo "[start.sh] starting ${name} ..."
    start=$(date +%s)
    "$@"
    code=$?
    if [ $(( $(date +%s) - start )) -ge 30 ]; then
      backoff=1
    fi
    echo "[start.sh] ${name} exited (code=${code}); restarting in ${backoff}s ..."
    sleep "$backoff"
    backoff=$(( backoff * 2 ))
    [ "$backoff" -gt 30 ] && backoff=30
  done
}

supervise "FastAPI (uvicorn :8000)" \
  uvicorn web.app:app --host 127.0.0.1 --port 8000 --workers 1 &
API_PID=$!

supervise "Next.js (node :3000)" \
  node server.js &
WEB_PID=$!

# Clean shutdown on redeploy/stop: forward the signal to both supervisors and
# let Docker tear the container down (children are reaped with the pid namespace).
trap 'echo "[start.sh] terminating ..."; kill "$API_PID" "$WEB_PID" 2>/dev/null; exit 0' TERM INT

# Both supervisors loop forever; returning from wait means one died unexpectedly,
# so exit non-zero and let Docker restart the whole container.
wait "$API_PID" "$WEB_PID"
echo "[start.sh] a supervisor exited unexpectedly; stopping container"
exit 1
