152 lines
3.3 KiB
Bash
Executable File
152 lines
3.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
RUN_DIR="$ROOT_DIR/.run"
|
|
LOG_DIR="$ROOT_DIR/logs"
|
|
|
|
BACKEND_PID_FILE="$RUN_DIR/backend.pid"
|
|
FRONTEND_PID_FILE="$RUN_DIR/frontend.pid"
|
|
BACKEND_LOG_FILE="$LOG_DIR/backend.log"
|
|
FRONTEND_LOG_FILE="$LOG_DIR/frontend.log"
|
|
|
|
BACKEND_HOST="${BACKEND_HOST:-0.0.0.0}"
|
|
BACKEND_PORT="${BACKEND_PORT:-8000}"
|
|
FRONTEND_HOST="${FRONTEND_HOST:-0.0.0.0}"
|
|
FRONTEND_PORT="${FRONTEND_PORT:-8451}"
|
|
CORS_ORIGINS="${CORS_ORIGINS:-http://localhost:${FRONTEND_PORT},http://127.0.0.1:${FRONTEND_PORT}}"
|
|
VITE_PROXY_TARGET="${VITE_PROXY_TARGET:-http://127.0.0.1:${BACKEND_PORT}}"
|
|
|
|
mkdir -p "$RUN_DIR" "$LOG_DIR"
|
|
|
|
require_command() {
|
|
if ! command -v "$1" >/dev/null 2>&1; then
|
|
echo "Missing command: $1" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
cleanup_stale_pid() {
|
|
local pid_file="$1"
|
|
if [[ -f "$pid_file" ]]; then
|
|
local pid
|
|
pid="$(cat "$pid_file")"
|
|
if [[ -n "$pid" ]] && kill -0 "$pid" >/dev/null 2>&1; then
|
|
return 0
|
|
fi
|
|
rm -f "$pid_file"
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
wait_for_port() {
|
|
local host="$1"
|
|
local port="$2"
|
|
local name="$3"
|
|
local retries=30
|
|
|
|
while (( retries > 0 )); do
|
|
if python3 - "$host" "$port" <<'PY'
|
|
import socket
|
|
import sys
|
|
|
|
host = sys.argv[1]
|
|
port = int(sys.argv[2])
|
|
sock = socket.socket()
|
|
sock.settimeout(0.5)
|
|
try:
|
|
sock.connect((host, port))
|
|
except OSError:
|
|
sys.exit(1)
|
|
finally:
|
|
sock.close()
|
|
sys.exit(0)
|
|
PY
|
|
then
|
|
echo "$name is ready on ${host}:${port}"
|
|
return 0
|
|
fi
|
|
sleep 1
|
|
retries=$((retries - 1))
|
|
done
|
|
|
|
echo "$name failed to start on ${host}:${port}. Check logs in $LOG_DIR" >&2
|
|
return 1
|
|
}
|
|
|
|
require_command python3
|
|
require_command node
|
|
require_command npm
|
|
require_command ffmpeg
|
|
|
|
if ! python3 - <<'PY' >/dev/null 2>&1
|
|
import fastapi
|
|
import uvicorn
|
|
import multipart
|
|
PY
|
|
then
|
|
echo "Missing Python packages. Run: cd backend && python3 -m pip install --user -r requirements.txt" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! -d "$ROOT_DIR/frontend/node_modules" ]]; then
|
|
echo "Installing frontend dependencies..."
|
|
(
|
|
cd "$ROOT_DIR/frontend"
|
|
npm install
|
|
)
|
|
fi
|
|
|
|
if cleanup_stale_pid "$BACKEND_PID_FILE"; then
|
|
echo "Backend already running with PID $(cat "$BACKEND_PID_FILE")"
|
|
else
|
|
echo "Starting backend..."
|
|
(
|
|
cd "$ROOT_DIR/backend"
|
|
nohup env \
|
|
CORS_ORIGINS="$CORS_ORIGINS" \
|
|
python3 -m uvicorn app.main:app \
|
|
--host "$BACKEND_HOST" \
|
|
--port "$BACKEND_PORT" \
|
|
>"$BACKEND_LOG_FILE" 2>&1 &
|
|
echo $! > "$BACKEND_PID_FILE"
|
|
)
|
|
fi
|
|
|
|
wait_for_port "127.0.0.1" "$BACKEND_PORT" "backend"
|
|
|
|
if cleanup_stale_pid "$FRONTEND_PID_FILE"; then
|
|
echo "Frontend already running with PID $(cat "$FRONTEND_PID_FILE")"
|
|
else
|
|
echo "Starting frontend..."
|
|
(
|
|
cd "$ROOT_DIR/frontend"
|
|
nohup env \
|
|
VITE_PROXY_TARGET="$VITE_PROXY_TARGET" \
|
|
npm run dev -- --host "$FRONTEND_HOST" --port "$FRONTEND_PORT" \
|
|
>"$FRONTEND_LOG_FILE" 2>&1 &
|
|
echo $! > "$FRONTEND_PID_FILE"
|
|
)
|
|
fi
|
|
|
|
wait_for_port "127.0.0.1" "$FRONTEND_PORT" "frontend"
|
|
|
|
DISPLAY_HOST="${PUBLIC_HOST:-}"
|
|
if [[ -z "$DISPLAY_HOST" ]]; then
|
|
DISPLAY_HOST="$(hostname -I 2>/dev/null | awk '{print $1}')"
|
|
fi
|
|
if [[ -z "$DISPLAY_HOST" ]]; then
|
|
DISPLAY_HOST="127.0.0.1"
|
|
fi
|
|
|
|
cat <<EOF
|
|
VPlatform started in background.
|
|
Frontend: http://${DISPLAY_HOST}:${FRONTEND_PORT}
|
|
Backend: http://${DISPLAY_HOST}:${BACKEND_PORT}
|
|
|
|
Logs:
|
|
$BACKEND_LOG_FILE
|
|
$FRONTEND_LOG_FILE
|
|
EOF
|