#!/bin/bash # # Paper Dynasty Game Engine - Native Development Mode # # Fast development workflow - no Docker rebuilds, instant restarts, hot-reload # # Usage: # ./dev-native.sh start Start all services natively # ./dev-native.sh stop Stop all services # ./dev-native.sh logs Show logs from all services # ./dev-native.sh restart Restart all services # set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color print_status() { echo -e "${BLUE}[INFO]${NC} $1"; } print_success() { echo -e "${GREEN}[OK]${NC} $1"; } print_warning() { echo -e "${YELLOW}[WARN]${NC} $1"; } print_error() { echo -e "${RED}[ERROR]${NC} $1"; } PID_DIR="$SCRIPT_DIR/.pids" LOG_DIR="$SCRIPT_DIR/.logs" # Create directories mkdir -p "$PID_DIR" "$LOG_DIR" # Check dependencies check_dependencies() { local missing=0 if ! command -v uv &> /dev/null; then print_error "uv not found - install from https://docs.astral.sh/uv/" missing=1 fi if ! command -v node &> /dev/null; then print_error "node not found - install Node.js 22+" missing=1 fi if ! command -v docker &> /dev/null; then print_error "docker not found - needed for Redis" missing=1 fi if [[ $missing -eq 1 ]]; then exit 1 fi print_success "All dependencies found" } # Setup environment files setup_env() { local mode=${1:-local} if [[ "$mode" == "network" ]]; then print_status "Setting up NETWORK environment files..." # Detect network IP local network_ip=$(ip addr show | grep "inet " | grep -v "127.0.0.1" | grep -v "tailscale" | head -1 | awk '{print $2}' | cut -d'/' -f1) if [[ -z "$network_ip" ]]; then print_error "Could not detect network IP" return 1 fi print_status "Detected network IP: $network_ip" # Backend cp backend/.env.network backend/.env sed -i "s|10\\.0\\.0\\.[0-9]*|$network_ip|g" backend/.env # Frontend cp frontend-sba/.env.network frontend-sba/.env sed -i "s|10\\.0\\.0\\.[0-9]*|$network_ip|g" frontend-sba/.env print_success "Network mode configured for $network_ip" print_warning "Add to Discord OAuth: http://$network_ip:8000/api/auth/discord/callback/server" else print_status "Setting up development environment files..." # Backend if [[ ! -f "backend/.env" ]] || ! grep -q "localhost:8000" "backend/.env" 2>/dev/null; then print_status "Copying backend/.env.dev -> backend/.env" cp backend/.env.dev backend/.env fi # Frontend if [[ ! -f "frontend-sba/.env" ]] || ! grep -q "localhost:8000" "frontend-sba/.env" 2>/dev/null; then print_status "Copying frontend-sba/.env.dev -> frontend-sba/.env" cp frontend-sba/.env.dev frontend-sba/.env fi print_success "Environment files ready" fi } # Start Redis (Docker) start_redis() { print_status "Starting Redis (Docker)..." if docker ps | grep -q "strat-gameplay-webapp-redis"; then print_warning "Redis already running" return 0 fi docker compose up -d redis # Wait for Redis to be healthy local max_wait=30 local waited=0 while [[ $waited -lt $max_wait ]]; do if docker compose ps redis 2>/dev/null | grep -q "healthy"; then print_success "Redis is healthy" return 0 fi sleep 1 waited=$((waited + 1)) done print_error "Redis failed to start" return 1 } # Start Backend (native Python with uv) start_backend() { print_status "Starting backend (native uvicorn)..." cd backend # Check if backend is already running if [[ -f "$PID_DIR/backend.pid" ]]; then local pid=$(cat "$PID_DIR/backend.pid") if ps -p "$pid" > /dev/null 2>&1; then print_warning "Backend already running (PID: $pid)" cd .. return 0 fi fi # Start backend in background nohup uv run python -m uvicorn app.main:socket_app \ --host 0.0.0.0 \ --port 8000 \ --reload \ > "$LOG_DIR/backend.log" 2>&1 & local pid=$! echo $pid > "$PID_DIR/backend.pid" cd .. # Wait for backend to respond local max_wait=10 local waited=0 while [[ $waited -lt $max_wait ]]; do if curl -sf http://localhost:8000/api/health > /dev/null 2>&1; then print_success "Backend started (PID: $pid)" return 0 fi sleep 1 waited=$((waited + 1)) done print_error "Backend failed to start - check logs: tail -f $LOG_DIR/backend.log" return 1 } # Start Frontend (native npm) start_frontend() { print_status "Starting frontend (native nuxt dev)..." cd frontend-sba # Check if frontend is already running if [[ -f "$PID_DIR/frontend.pid" ]]; then local pid=$(cat "$PID_DIR/frontend.pid") if ps -p "$pid" > /dev/null 2>&1; then print_warning "Frontend already running (PID: $pid)" cd .. return 0 fi fi # Ensure node_modules exists if [[ ! -d "node_modules" ]]; then print_status "Installing frontend dependencies..." npm ci fi # Start frontend in background nohup npm run dev > "$LOG_DIR/frontend.log" 2>&1 & local pid=$! echo $pid > "$PID_DIR/frontend.pid" cd .. # Wait for frontend to respond local max_wait=30 local waited=0 while [[ $waited -lt $max_wait ]]; do if curl -sf http://localhost:3000 > /dev/null 2>&1; then print_success "Frontend started (PID: $pid)" return 0 fi sleep 1 waited=$((waited + 1)) done print_error "Frontend failed to start - check logs: tail -f $LOG_DIR/frontend.log" return 1 } # Stop all services stop_services() { print_status "Stopping all services..." # Stop backend if [[ -f "$PID_DIR/backend.pid" ]]; then local pid=$(cat "$PID_DIR/backend.pid") if ps -p "$pid" > /dev/null 2>&1; then print_status "Stopping backend (PID: $pid)..." kill $pid rm "$PID_DIR/backend.pid" fi fi # Stop frontend if [[ -f "$PID_DIR/frontend.pid" ]]; then local pid=$(cat "$PID_DIR/frontend.pid") if ps -p "$pid" > /dev/null 2>&1; then print_status "Stopping frontend (PID: $pid)..." kill $pid rm "$PID_DIR/frontend.pid" fi fi # Stop Redis print_status "Stopping Redis..." docker compose stop redis print_success "All services stopped" } # Show logs show_logs() { print_status "Showing logs (Ctrl+C to exit)..." echo "" # Use multitail if available, otherwise tail if command -v multitail &> /dev/null; then multitail -s 2 \ -l "tail -f $LOG_DIR/backend.log" \ -l "tail -f $LOG_DIR/frontend.log" else print_warning "Install 'multitail' for better log viewing" tail -f "$LOG_DIR/backend.log" "$LOG_DIR/frontend.log" fi } # Start all services start_all() { local mode=${1:-local} print_status "Starting native development environment..." echo "" check_dependencies setup_env "$mode" start_redis || exit 1 start_backend || exit 1 start_frontend || exit 1 echo "" print_success "Development environment ready!" echo "" if [[ "$mode" == "network" ]]; then local network_ip=$(ip addr show | grep "inet " | grep -v "127.0.0.1" | grep -v "tailscale" | head -1 | awk '{print $2}' | cut -d'/' -f1) echo -e "${GREEN}========================================${NC}" echo -e "${GREEN} Native Development Mode (NETWORK)${NC}" echo -e "${GREEN}========================================${NC}" echo "" echo " Access from ANY device on your network:" echo " Frontend: http://$network_ip:3000" echo " Backend: http://$network_ip:8000" echo " API Docs: http://$network_ip:8000/docs" echo "" else echo -e "${GREEN}========================================${NC}" echo -e "${GREEN} Native Development Mode${NC}" echo -e "${GREEN}========================================${NC}" echo "" echo " Backend API: http://localhost:8000" echo " API Docs: http://localhost:8000/docs" echo " Frontend: http://localhost:3000" echo "" fi echo " Features:" echo " ✓ Hot-reload enabled (backend + frontend)" echo " ✓ No Docker rebuilds" echo " ✓ Instant restarts" echo "" echo " Logs:" echo " Backend: tail -f $LOG_DIR/backend.log" echo " Frontend: tail -f $LOG_DIR/frontend.log" echo " All: ./dev-native.sh logs" echo "" echo " Commands:" echo " ./dev-native.sh logs View logs" echo " ./dev-native.sh stop Stop services" echo " ./dev-native.sh restart Restart services" echo "" } # Restart all services restart_all() { local mode=${1:-local} stop_services sleep 2 start_all "$mode" } # Show usage show_usage() { local network_ip=$(ip addr show | grep "inet " | grep -v "127.0.0.1" | grep -v "tailscale" | head -1 | awk '{print $2}' | cut -d'/' -f1) echo "Paper Dynasty - Native Development Mode" echo "" echo "Usage: ./dev-native.sh [--network]" echo "" echo "Commands:" echo " start Start services (localhost only)" echo " start --network Start services (accessible from network)" echo " stop Stop all services" echo " logs Tail logs from all services" echo " restart Restart services" echo " restart --network Restart in network mode" echo "" echo "Network Mode:" echo " Your IP: $network_ip" echo " Access: http://$network_ip:3000" echo "" echo "Benefits:" echo " • No Docker rebuilds (saves minutes)" echo " • Instant startup" echo " • Hot-reload on file changes (backend + frontend)" echo " • Native debugging support" echo "" echo "Requirements:" echo " • uv (Python package manager)" echo " • Node.js 22+" echo " • Docker (for Redis only)" echo "" } # Main MODE="local" if [[ "${2:-}" == "--network" ]]; then MODE="network" fi case "${1:-}" in start) start_all "$MODE" ;; stop) stop_services ;; logs) show_logs ;; restart) restart_all "$MODE" ;; *) show_usage exit 1 ;; esac