#!/usr/bin/env bash set -euo pipefail RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' info() { echo -e "${GREEN}[+]${NC} $*"; } die() { echo -e "${RED}[x] FATAL:${NC} $*" >&2; exit 1; } ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" BACKEND_DIR="$ROOT_DIR/backend" FRONTEND_DIR="$ROOT_DIR/frontend" PLAYGROUND_DIR="$ROOT_DIR/playground" ENV_FILE="$BACKEND_DIR/.env" require_command() { command -v "$1" >/dev/null 2>&1 || die "Missing required command: $1" } require_env() { [[ -f "$ENV_FILE" ]] || die "Missing $ENV_FILE. Run ./setup.sh first." } env_value() { local key="$1" local default_value="${2:-}" local value value=$(grep -E "^${key}=" "$ENV_FILE" | cut -d '=' -f2- || true) if [[ -n "$value" ]]; then printf '%s\n' "$value" return fi printf '%s\n' "$default_value" } compose() { docker compose --env-file "$ENV_FILE" -f "$ROOT_DIR/docker-compose.yml" "$@" } service_compose_path() { local name="$1" local path="$PLAYGROUND_DIR/$name/docker-compose.yml" [[ -f "$path" ]] || die "Unknown service group: $name" printf '%s\n' "$path" } service_names() { find "$PLAYGROUND_DIR" -mindepth 2 -maxdepth 2 -name docker-compose.yml -printf '%h\n' \ | xargs -r -n1 basename \ | sort } run_backend() { require_command uv require_env local api_port api_port=$(env_value API_PORT 3671) info "Starting backend dev server on http://0.0.0.0:$api_port" ( cd "$BACKEND_DIR" uv run fastapi dev app/main.py --host 0.0.0.0 --port "$api_port" ) } run_frontend() { require_command npm require_env info "Starting frontend Vite dev server" ( cd "$FRONTEND_DIR" npm run dev ) } run_mongo() { require_command docker require_env local action="${1:-status}" case "$action" in up) compose up -d mongodb ;; down) compose stop mongodb ;; status) compose ps mongodb ;; logs) compose logs -f mongodb ;; *) die "Unknown mongo action: $action" ;; esac } run_services() { require_command docker local action="${1:-list}" shift || true case "$action" in list) service_names ;; up|down|status) local target="${1:-all}" if [[ "$target" == "all" ]]; then while IFS= read -r name; do [[ -n "$name" ]] || continue local compose_file compose_file=$(service_compose_path "$name") case "$action" in up) docker compose -f "$compose_file" up -d ;; down) docker compose -f "$compose_file" down ;; status) docker compose -f "$compose_file" ps ;; esac done < <(service_names) return fi local compose_file compose_file=$(service_compose_path "$target") case "$action" in up) docker compose -f "$compose_file" up -d ;; down) docker compose -f "$compose_file" down ;; status) docker compose -f "$compose_file" ps ;; esac ;; *) die "Unknown services action: $action" ;; esac } show_help() { cat <<'EOF' Usage: ./dev.sh backend ./dev.sh frontend ./dev.sh mongo [up|down|status|logs] ./dev.sh services [list|up|down|status] [name|all] Examples: ./dev.sh mongo up ./dev.sh backend ./dev.sh frontend ./dev.sh services list ./dev.sh services up GabibbiTowers EOF } main() { local command="${1:-help}" shift || true case "$command" in backend) run_backend "$@" ;; frontend) run_frontend "$@" ;; mongo) run_mongo "$@" ;; services) run_services "$@" ;; help|-h|--help) show_help ;; *) die "Unknown command: $command" ;; esac } main "$@"