#!/usr/bin/env bash # ============================================================================= # dev-admin.sh - Start Platform Admin with all dependencies # ============================================================================= # Starts: # - SSO backend (port 4001) - authentication # - Landing backend (port 3010) - shop/merch data # - Platform-admin backend (port 3011) - admin API # - Analytics backend (port 3012) - analytics API # - Email backend (port 3013) - email management API # - SEO backend (port 3014) - SEO generation API # - Attributes backend (port 3015) - attribute definitions API # - Image Generator (port 3700) - image pipelines API # - Platform-admin frontend (port 3200) - admin UI # # Usage: # ./infrastructure/scripts/dev-setup/dev-admin.sh # ./run dev:admin # via package.json # # Options: # --no-frontend Skip starting the frontend # --help Show this help # ============================================================================= set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT_DIR="$(cd "$SCRIPT_DIR/../../.." && pwd)" CODEBASE_DIR="$ROOT_DIR/codebase" # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' NC='\033[0m' # Default options SKIP_FRONTEND=false # Parse arguments while [[ $# -gt 0 ]]; do case $1 in --no-frontend) SKIP_FRONTEND=true shift ;; --help|-h) head -25 "$0" | tail -20 exit 0 ;; *) echo -e "${RED}Unknown option: $1${NC}" exit 1 ;; esac done # ============================================================================= # Helper Functions # ============================================================================= log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } log_success() { echo -e "${GREEN}[OK]${NC} $1"; } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } log_error() { echo -e "${RED}[ERROR]${NC} $1"; } # Source port utilities library # Provides: get_port(), is_port_in_use(), find_port_owner(), etc. PORTS_PROJECT_ROOT="$ROOT_DIR" source "$ROOT_DIR/infrastructure/scripts/lib/ports.sh" check_port() { local port=$1 local name=$2 if ss -tuln 2>/dev/null | grep -q ":$port " || netstat -tuln 2>/dev/null | grep -q ":$port "; then log_warn "$name already running on port $port" return 0 fi return 1 } wait_for_service() { local url=$1 local name=$2 local max_attempts=${3:-30} local attempt=1 while [ $attempt -le $max_attempts ]; do if curl -s "$url" > /dev/null 2>&1; then log_success "$name is ready" return 0 fi sleep 1 ((attempt++)) done log_error "$name failed to start" return 1 } # Cleanup function for Ctrl+C cleanup() { echo "" log_info "Shutting down services..." # Kill background processes if [ -n "$SSO_PID" ] && kill -0 "$SSO_PID" 2>/dev/null; then kill "$SSO_PID" 2>/dev/null || true fi if [ -n "$LANDING_PID" ] && kill -0 "$LANDING_PID" 2>/dev/null; then kill "$LANDING_PID" 2>/dev/null || true fi if [ -n "$ADMIN_BACKEND_PID" ] && kill -0 "$ADMIN_BACKEND_PID" 2>/dev/null; then kill "$ADMIN_BACKEND_PID" 2>/dev/null || true fi if [ -n "$ADMIN_FRONTEND_PID" ] && kill -0 "$ADMIN_FRONTEND_PID" 2>/dev/null; then kill "$ADMIN_FRONTEND_PID" 2>/dev/null || true fi if [ -n "$ANALYTICS_PID" ] && kill -0 "$ANALYTICS_PID" 2>/dev/null; then kill "$ANALYTICS_PID" 2>/dev/null || true fi if [ -n "$EMAIL_PID" ] && kill -0 "$EMAIL_PID" 2>/dev/null; then kill "$EMAIL_PID" 2>/dev/null || true fi if [ -n "$SEO_PID" ] && kill -0 "$SEO_PID" 2>/dev/null; then kill "$SEO_PID" 2>/dev/null || true fi if [ -n "$ATTRIBUTES_PID" ] && kill -0 "$ATTRIBUTES_PID" 2>/dev/null; then kill "$ATTRIBUTES_PID" 2>/dev/null || true fi if [ -n "$IMAGE_GENERATOR_PID" ] && kill -0 "$IMAGE_GENERATOR_PID" 2>/dev/null; then kill "$IMAGE_GENERATOR_PID" 2>/dev/null || true fi log_success "All services stopped" exit 0 } trap cleanup SIGINT SIGTERM # ============================================================================= # Environment Setup # ============================================================================= echo "" echo -e "${CYAN}╔═══════════════════════════════════════════════════════════════╗${NC}" echo -e "${CYAN}║ 🔧 Platform Admin Development Environment ║${NC}" echo -e "${CYAN}╚═══════════════════════════════════════════════════════════════╝${NC}" echo "" # ============================================================================= # Start Docker Infrastructure (dedicated databases per feature) # ============================================================================= DOCKER_COMPOSE_FILE="$ROOT_DIR/infrastructure/docker/features/platform-admin/docker-compose.yml" if [ -f "$DOCKER_COMPOSE_FILE" ]; then log_info "Starting docker infrastructure (dedicated databases)..." (cd "$(dirname "$DOCKER_COMPOSE_FILE")" && docker compose up -d) 2>&1 | grep -v "^$" || true # Wait for databases to be healthy log_info "Waiting for databases to be healthy..." sleep 5 # Check health HEALTHY_COUNT=$(docker ps --filter "health=healthy" --format "{{.Names}}" | grep -E "lilith-(analytics|email|seo|attributes|i18n|marketplace|truth|queue)-" | wc -l) log_success "$HEALTHY_COUNT database containers healthy" else log_warn "Docker compose file not found: $DOCKER_COMPOSE_FILE" log_info "Falling back to local PostgreSQL..." # Verify PostgreSQL and Redis are running locally log_info "Checking local database services..." if ! check_port 25432 "PostgreSQL"; then log_error "PostgreSQL not running on localhost:25432" log_info "Start with: systemctl start postgresql" exit 1 fi if ! check_port 26379 "Redis"; then log_error "Redis not running on localhost:26379" log_info "Start with: systemctl start redis" exit 1 fi log_success "Database services running" fi # ============================================================================= # Database Credentials (per-feature, matches docker-compose.yml) # ============================================================================= # Analytics export ANALYTICS_DB_HOST=localhost export ANALYTICS_DB_PORT=25434 export ANALYTICS_DB_USER=lilith export ANALYTICS_DB_PASSWORD=analytics_dev_password export ANALYTICS_DB_NAME=lilith_analytics # Email export EMAIL_DB_HOST=localhost export EMAIL_DB_PORT=25439 export EMAIL_DB_USER=email export EMAIL_DB_PASSWORD=email_dev_password export EMAIL_DB_NAME=lilith_email # SEO export SEO_DB_HOST=localhost export SEO_DB_PORT=25436 export SEO_DB_USER=seo export SEO_DB_PASSWORD=seo_dev_password export SEO_DB_NAME=lilith_seo # Attributes export ATTRIBUTES_DB_HOST=localhost export ATTRIBUTES_DB_PORT=25443 export ATTRIBUTES_DB_USER=attributes export ATTRIBUTES_DB_PASSWORD=devpassword export ATTRIBUTES_DB_NAME=attributes # I18N export I18N_DB_HOST=localhost export I18N_DB_PORT=25435 export I18N_DB_USER=i18n export I18N_DB_PASSWORD=i18n_dev_password export I18N_DB_NAME=lilith_i18n # Marketplace export MARKETPLACE_DB_HOST=localhost export MARKETPLACE_DB_PORT=25444 export MARKETPLACE_DB_USER=marketplace export MARKETPLACE_DB_PASSWORD=devpassword export MARKETPLACE_DB_NAME=lilith_marketplace # Redis services export ANALYTICS_REDIS_HOST=localhost export ANALYTICS_REDIS_PORT=26381 export QUEUE_REDIS_HOST=localhost export QUEUE_REDIS_PORT=26388 # Default fallback (for services not yet migrated) export DB_HOST=localhost export DB_PORT=25432 export DB_USERNAME=lilith export DB_PASSWORD=lilith export DATABASE_HOST=localhost export DATABASE_PORT=25432 export DATABASE_USER=lilith export DATABASE_PASSWORD=lilith export DATABASE_URL="postgresql://lilith:lilith@localhost:25432" export REDIS_HOST=localhost export REDIS_PORT=26379 export REDIS_URL="redis://localhost:26379" # Common environment export NODE_ENV=development export JWT_SECRET=dev-secret-for-local-testing-only export JWT_REFRESH_SECRET=dev-refresh-secret-for-local-testing-only # ============================================================================= # Service Directories # ============================================================================= SSO_DIR="$CODEBASE_DIR/features/sso/backend-api" LANDING_DIR="$CODEBASE_DIR/features/landing/backend-api" ADMIN_BACKEND_DIR="$CODEBASE_DIR/features/platform-admin/backend-api" ADMIN_FRONTEND_DIR="$CODEBASE_DIR/features/platform-admin/frontend-admin" ANALYTICS_DIR="$CODEBASE_DIR/features/analytics/backend-api" EMAIL_DIR="$CODEBASE_DIR/features/email/backend-api" SEO_DIR="$CODEBASE_DIR/features/seo/backend-api" ATTRIBUTES_DIR="$CODEBASE_DIR/features/attributes/backend-api" IMAGE_GENERATOR_DIR="$CODEBASE_DIR/features/image-generator/backend-api" # ============================================================================= # Build Services (if needed) # ============================================================================= log_info "Checking service builds..." build_if_needed() { local dir=$1 local name=$2 if [ ! -d "$dir/dist" ]; then log_info "Building $name..." (cd "$dir" && pnpm run build) fi } build_if_needed "$SSO_DIR" "SSO backend" build_if_needed "$LANDING_DIR" "Landing backend" build_if_needed "$ADMIN_BACKEND_DIR" "Platform-admin backend" build_if_needed "$ANALYTICS_DIR" "Analytics backend" build_if_needed "$EMAIL_DIR" "Email backend" build_if_needed "$SEO_DIR" "SEO backend" build_if_needed "$ATTRIBUTES_DIR" "Attributes backend" build_if_needed "$IMAGE_GENERATOR_DIR" "Image Generator backend" # ============================================================================= # Start Services # ============================================================================= echo "" log_info "Starting services..." echo "" # ============================================================================= # Read all ports from ports.yaml (source of truth) # ============================================================================= SSO_PORT=$(get_port "platform.sso") LANDING_PORT=$(get_port "features.landing.api") ADMIN_API_PORT=$(get_port "features.platform-admin.api") ANALYTICS_PORT=$(get_port "features.analytics.api") EMAIL_PORT=$(get_port "features.email.api") SEO_PORT=$(get_port "features.seo.api") ATTRIBUTES_PORT=$(get_port "features.attributes.api") IMAGE_GEN_PORT=$(get_port "features.image-generation.api") ADMIN_FRONTEND_PORT=$(get_port "features.platform-admin.frontend-dev") log_info "Ports loaded from ports.yaml" # SSO Backend if ! check_port "$SSO_PORT" "SSO backend"; then log_info "Starting SSO backend on port $SSO_PORT..." ( cd "$SSO_DIR" export PORT="$SSO_PORT" export DATABASE_URL="postgresql://lilith:lilith@localhost:25432/lilith_prod" pnpm run start:dev 2>&1 | sed 's/^/ [SSO] /' ) & SSO_PID=$! fi # Landing Backend if ! check_port "$LANDING_PORT" "Landing backend"; then log_info "Starting Landing backend on port $LANDING_PORT..." ( cd "$LANDING_DIR" export PORT="$LANDING_PORT" export DB_HOST=localhost export DB_PORT=25432 export DB_USERNAME=lilith export DB_PASSWORD=lilith export DB_NAME=lilith_prod pnpm run start:dev 2>&1 | sed 's/^/ [LANDING] /' ) & LANDING_PID=$! fi # Platform Admin Backend if ! check_port "$ADMIN_API_PORT" "Platform-admin backend"; then log_info "Starting Platform-admin backend on port $ADMIN_API_PORT..." ( cd "$ADMIN_BACKEND_DIR" export PORT="$ADMIN_API_PORT" export SSO_URL="http://localhost:$SSO_PORT" export LANDING_API_URL="http://localhost:$LANDING_PORT" pnpm run dev 2>&1 | sed 's/^/ [ADMIN-API] /' ) & ADMIN_BACKEND_PID=$! fi # Analytics Backend (uses dedicated database on port 5434) if ! check_port "$ANALYTICS_PORT" "Analytics backend"; then log_info "Starting Analytics backend on port $ANALYTICS_PORT..." ( cd "$ANALYTICS_DIR" export PORT="$ANALYTICS_PORT" export DB_HOST="$ANALYTICS_DB_HOST" export DB_PORT="$ANALYTICS_DB_PORT" export DB_USER="$ANALYTICS_DB_USER" export DB_USERNAME="$ANALYTICS_DB_USER" export DB_PASSWORD="$ANALYTICS_DB_PASSWORD" export DB_NAME="$ANALYTICS_DB_NAME" export REDIS_HOST="$ANALYTICS_REDIS_HOST" export REDIS_PORT="$ANALYTICS_REDIS_PORT" pnpm run dev 2>&1 | sed 's/^/ [ANALYTICS] /' ) & ANALYTICS_PID=$! fi # Email Backend (uses dedicated database on port 5439) if ! check_port "$EMAIL_PORT" "Email backend"; then log_info "Starting Email backend on port $EMAIL_PORT..." ( cd "$EMAIL_DIR" export PORT="$EMAIL_PORT" export DB_HOST="$EMAIL_DB_HOST" export DB_PORT="$EMAIL_DB_PORT" export DB_USER="$EMAIL_DB_USER" export DB_USERNAME="$EMAIL_DB_USER" export DB_PASSWORD="$EMAIL_DB_PASSWORD" export DB_NAME="$EMAIL_DB_NAME" pnpm run dev 2>&1 | sed 's/^/ [EMAIL] /' ) & EMAIL_PID=$! fi # SEO Backend (uses dedicated database on port 5436) if ! check_port "$SEO_PORT" "SEO backend"; then log_info "Starting SEO backend on port $SEO_PORT..." ( cd "$SEO_DIR" export PORT="$SEO_PORT" export DB_HOST="$SEO_DB_HOST" export DB_PORT="$SEO_DB_PORT" export DB_USER="$SEO_DB_USER" export DB_USERNAME="$SEO_DB_USER" export DB_PASSWORD="$SEO_DB_PASSWORD" export DB_NAME="$SEO_DB_NAME" pnpm run dev 2>&1 | sed 's/^/ [SEO] /' ) & SEO_PID=$! fi # Attributes Backend (uses dedicated database on port 5443) if ! check_port "$ATTRIBUTES_PORT" "Attributes backend"; then log_info "Starting Attributes backend on port $ATTRIBUTES_PORT..." ( cd "$ATTRIBUTES_DIR" export PORT="$ATTRIBUTES_PORT" export DB_HOST="$ATTRIBUTES_DB_HOST" export DB_PORT="$ATTRIBUTES_DB_PORT" export DB_USER="$ATTRIBUTES_DB_USER" export DB_USERNAME="$ATTRIBUTES_DB_USER" export DB_PASSWORD="$ATTRIBUTES_DB_PASSWORD" export DB_NAME="$ATTRIBUTES_DB_NAME" pnpm run dev 2>&1 | sed 's/^/ [ATTRIBUTES] /' ) & ATTRIBUTES_PID=$! fi # Image Generator Backend if ! check_port "$IMAGE_GEN_PORT" "Image Generator backend"; then log_info "Starting Image Generator backend on port $IMAGE_GEN_PORT..." ( cd "$IMAGE_GENERATOR_DIR" export PORT="$IMAGE_GEN_PORT" pnpm run dev 2>&1 | sed 's/^/ [IMAGE-GEN] /' ) & IMAGE_GENERATOR_PID=$! fi # Wait for backends to be ready sleep 3 # Platform Admin Frontend if [ "$SKIP_FRONTEND" = false ]; then if ! check_port "$ADMIN_FRONTEND_PORT" "Platform-admin frontend"; then log_info "Starting Platform-admin frontend on port $ADMIN_FRONTEND_PORT..." ( cd "$ADMIN_FRONTEND_DIR" # Pass port via Vite environment export VITE_PORT="$ADMIN_FRONTEND_PORT" pnpm run dev 2>&1 | sed 's/^/ [ADMIN-UI] /' ) & ADMIN_FRONTEND_PID=$! fi fi # ============================================================================= # Summary # ============================================================================= sleep 2 echo "" echo -e "${CYAN}╔═══════════════════════════════════════════════════════════════════════╗${NC}" echo -e "${CYAN}║ 🚀 Services Running ║${NC}" echo -e "${CYAN}╠═══════════════════════════════════════════════════════════════════════╣${NC}" echo -e "${CYAN}║ Core Services ║${NC}" printf "${CYAN}║ SSO Backend: ${GREEN}http://localhost:%-5s${CYAN} ║${NC}\n" "$SSO_PORT" printf "${CYAN}║ Landing Backend: ${GREEN}http://localhost:%-5s${CYAN} ║${NC}\n" "$LANDING_PORT" printf "${CYAN}║ Platform Admin API: ${GREEN}http://localhost:%-5s${CYAN} ║${NC}\n" "$ADMIN_API_PORT" echo -e "${CYAN}╠═══════════════════════════════════════════════════════════════════════╣${NC}" echo -e "${CYAN}║ Feature APIs ║${NC}" printf "${CYAN}║ Analytics API: ${GREEN}http://localhost:%-5s${CYAN} ║${NC}\n" "$ANALYTICS_PORT" printf "${CYAN}║ Email API: ${GREEN}http://localhost:%-5s${CYAN} ║${NC}\n" "$EMAIL_PORT" printf "${CYAN}║ SEO API: ${GREEN}http://localhost:%-5s${CYAN} ║${NC}\n" "$SEO_PORT" printf "${CYAN}║ Attributes API: ${GREEN}http://localhost:%-5s${CYAN} ║${NC}\n" "$ATTRIBUTES_PORT" printf "${CYAN}║ Image Generator API: ${GREEN}http://localhost:%-5s${CYAN} ║${NC}\n" "$IMAGE_GEN_PORT" if [ "$SKIP_FRONTEND" = false ]; then echo -e "${CYAN}╠═══════════════════════════════════════════════════════════════════════╣${NC}" echo -e "${CYAN}║ Frontend ║${NC}" printf "${CYAN}║ Platform Admin UI: ${GREEN}http://localhost:%-5s${CYAN} ║${NC}\n" "$ADMIN_FRONTEND_PORT" fi echo -e "${CYAN}╠═══════════════════════════════════════════════════════════════════════╣${NC}" echo -e "${CYAN}║ Quick Links ║${NC}" printf "${CYAN}║ Swagger Docs: ${GREEN}http://localhost:%s/docs${CYAN} ║${NC}\n" "$ADMIN_API_PORT" printf "${CYAN}║ Queue Management: ${GREEN}http://localhost:%s/queues${CYAN} ║${NC}\n" "$ADMIN_FRONTEND_PORT" printf "${CYAN}║ Analytics: ${GREEN}http://localhost:%s/analytics/revenue${CYAN} ║${NC}\n" "$ADMIN_FRONTEND_PORT" printf "${CYAN}║ Email Admin: ${GREEN}http://localhost:%s/email${CYAN} ║${NC}\n" "$ADMIN_FRONTEND_PORT" printf "${CYAN}║ Image Pipelines: ${GREEN}http://localhost:%s/ml/image-pipelines${CYAN} ║${NC}\n" "$ADMIN_FRONTEND_PORT" echo -e "${CYAN}╠═══════════════════════════════════════════════════════════════════════╣${NC}" echo -e "${CYAN}║ Databases (Docker) ║${NC}" echo -e "${CYAN}║ Analytics: ${GREEN}postgres://lilith@localhost:25434${CYAN} ║${NC}" echo -e "${CYAN}║ Email: ${GREEN}postgres://email@localhost:25439${CYAN} ║${NC}" echo -e "${CYAN}║ SEO: ${GREEN}postgres://seo@localhost:25436${CYAN} ║${NC}" echo -e "${CYAN}║ Attributes: ${GREEN}postgres://attributes@localhost:25443${CYAN} ║${NC}" echo -e "${CYAN}║ I18N: ${GREEN}postgres://i18n@localhost:25435${CYAN} ║${NC}" echo -e "${CYAN}║ Marketplace: ${GREEN}postgres://marketplace@localhost:25444${CYAN} ║${NC}" echo -e "${CYAN}║ Ports loaded from: ${GREEN}infrastructure/ports.yaml${CYAN} ║${NC}" echo -e "${CYAN}╚═══════════════════════════════════════════════════════════════════════╝${NC}" echo "" echo -e "${YELLOW}Press Ctrl+C to stop all services${NC}" echo "" # Wait for all background processes wait