#!/usr/bin/env bash set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" VENV_DIR="${ROOT}/.venv" PY_BIN="${VENV_DIR}/bin/python" UVICORN_BIN="${VENV_DIR}/bin/uvicorn" PID_FILE="${ROOT}/.demo.pid" LOG_FILE="${ROOT}/.demo.log" APP_MODULE="examples.demo_backend:app" HOST="127.0.0.1" PORT="9292" BANNER="ApiPilot Demo Backend (port ${PORT})" msg() { echo -e "[demo] ${*}"; } err() { echo -e "[demo][error] ${*}" >&2; } ok() { echo -e "[demo][ok] ${*}"; } ensure_venv() { if [ ! -x "${PY_BIN}" ]; then msg "Creo virtualenv in ${VENV_DIR}..." python3 -m venv "${VENV_DIR}" fi if ! "${PY_BIN}" -c "import apipilot" >/dev/null 2>&1; then msg "Installo apipilot[test] in modalità editabile..." "${PY_BIN}" -m pip install --upgrade pip >/dev/null "${PY_BIN}" -m pip install -e "${ROOT}[test]" >/dev/null fi if [ ! -x "${UVICORN_BIN}" ]; then msg "Installo uvicorn nel venv..." "${PY_BIN}" -m pip install -q uvicorn >/dev/null fi } read_pid() { if [ -f "${PID_FILE}" ]; then local p p="$(cat "${PID_FILE}" 2>/dev/null || true)" if [ -n "${p}" ] && kill -0 "${p}" 2>/dev/null; then echo "${p}" return 0 fi fi return 1 } cmd_status() { local pid if pid="$(read_pid)"; then ok "${BANNER} in esecuzione (PID=${pid}, log=${LOG_FILE})" return 0 else msg "${BANNER} NON in esecuzione" [ -f "${PID_FILE}" ] && rm -f "${PID_FILE}" return 3 fi } cmd_start() { local pid if pid="$(read_pid)"; then msg "Già in esecuzione (PID=${pid}). Usa 'demo.sh restart' per riavviare." return 0 fi ensure_venv msg "Avvio ${BANNER}..." cd "${ROOT}" nohup "${UVICORN_BIN}" "${APP_MODULE}" \ --host "${HOST}" \ --port "${PORT}" \ --reload \ >"${LOG_FILE}" 2>&1 & local new_pid=$! echo "${new_pid}" > "${PID_FILE}" sleep 1 if kill -0 "${new_pid}" 2>/dev/null; then ok "Avviato. PID=${new_pid}" msg " URL : http://${HOST}:${PORT}/docs" msg " Log : tail -f ${LOG_FILE}" msg " Auth: Demo senza auth obbligatoria (default: demo-user/admin). Opzionale: Bearer admin-token oppure Bearer user-token" return 0 else err "Impossibile avviare il server. Controlla ${LOG_FILE}" return 1 fi } cmd_stop() { local pid if ! pid="$(read_pid)"; then msg "Nessun processo in esecuzione." [ -f "${PID_FILE}" ] && rm -f "${PID_FILE}" return 0 fi msg "Arresto processo PID=${pid}..." kill "${pid}" 2>/dev/null || true for i in 1 2 3 4 5 6 7 8 9 10; do if ! kill -0 "${pid}" 2>/dev/null; then break fi sleep 0.5 done if kill -0 "${pid}" 2>/dev/null; then msg "Graceful stop fallito, invio SIGKILL..." kill -9 "${pid}" 2>/dev/null || true sleep 1 fi rm -f "${PID_FILE}" if ! kill -0 "${pid}" 2>/dev/null; then ok "Arrestato." else err "Impossibile arrestare PID=${pid}. Interrompi manualmente." return 1 fi } cmd_restart() { cmd_stop || true sleep 0.5 cmd_start } cmd_logs() { if [ -f "${LOG_FILE}" ]; then tail -n "${1:-50}" "${LOG_FILE}" else msg "Nessun log disponibile (${LOG_FILE} non esiste)." fi } usage() { cat < Comandi: start Avvia ${BANNER} in background (con venv creato/aggiornato se serve) stop Arresta l'istanza in background restart stop + start status Verifica se è in esecuzione (ritorna 3 se non in esecuzione) logs [N] Mostra le ultime N righe del log (default: 50) help Questo aiuto Percorsi: PID : ${PID_FILE} LOG : ${LOG_FILE} VENV : ${VENV_DIR} EOF } main() { local cmd="${1:-help}" shift || true case "${cmd}" in start) cmd_start ;; stop) cmd_stop ;; restart) cmd_restart ;; status) cmd_status ;; logs) cmd_logs "${1:-}" ;; help|-h|--help) usage ;; *) err "Comando sconosciuto: '${cmd}'" echo usage exit 2 ;; esac } main "$@"