#!/bin/bash set -e # UnitForge Web Server Startup Script # This script starts the FastAPI development server for UnitForge # Load centralized color utility SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "${SCRIPT_DIR}/scripts/colors.sh" # Configuration DEFAULT_HOST="0.0.0.0" DEFAULT_PORT="8000" BACKEND_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/backend" # Parse command line arguments HOST=${DEFAULT_HOST} PORT=${DEFAULT_PORT} RELOAD=true LOG_LEVEL="info" USE_UV=true usage() { echo "Usage: $0 [OPTIONS]" echo "" echo "Options:" echo " -h, --host HOST Host to bind to (default: ${DEFAULT_HOST})" echo " -p, --port PORT Port to bind to (default: ${DEFAULT_PORT})" echo " --no-reload Disable auto-reload" echo " --log-level LEVEL Log level (debug, info, warning, error, critical)" echo " --help Show this help message" echo "" echo "Examples:" echo " $0 # Start with default settings" echo " $0 -h 127.0.0.1 -p 3000 # Custom host and port" echo " $0 --no-reload # Start without auto-reload" } while [[ $# -gt 0 ]]; do case $1 in -h|--host) HOST="$2" shift 2 ;; -p|--port) PORT="$2" shift 2 ;; --no-reload) RELOAD=false shift ;; --log-level) LOG_LEVEL="$2" shift 2 ;; --help) usage exit 0 ;; *) echo -e "${RED}Error: Unknown option $1${NC}" usage exit 1 ;; esac done # Function to check if command exists command_exists() { command -v "$1" >/dev/null 2>&1 } # Function to check if port is available check_port() { if command_exists lsof; then if lsof -Pi :$1 -sTCP:LISTEN -t >/dev/null 2>&1; then return 1 fi elif command_exists netstat; then if netstat -tuln 2>/dev/null | grep -q ":$1 "; then return 1 fi fi return 0 } # Print banner echo -e "${BLUE}" echo "╔══════════════════════════════════════════════╗" echo "║ UnitForge Server ║" echo "║ Systemd Unit File Creator & Manager ║" echo "╚══════════════════════════════════════════════╝" echo -e "${NC}" # Check if we're in the right directory if [[ ! -d "${BACKEND_DIR}" ]]; then echo -e "${RED}Error: Backend directory not found at ${BACKEND_DIR}${NC}" echo "Please run this script from the unitforge project root directory." exit 1 fi # Check if Python is available if ! command_exists python3; then echo -e "${RED}Error: Python 3 is not installed or not in PATH${NC}" exit 1 fi # Check Python version PYTHON_VERSION=$(python3 -c 'import sys; print(".".join(map(str, sys.version_info[:2])))') REQUIRED_VERSION="3.8" if ! python3 -c "import sys; exit(0 if sys.version_info >= (3, 8) else 1)"; then echo -e "${RED}Error: Python ${REQUIRED_VERSION}+ is required, but ${PYTHON_VERSION} is installed${NC}" exit 1 fi echo -e "${GREEN}✓ Python ${PYTHON_VERSION} found${NC}" # Check for uv if ! command_exists uv; then echo -e "${RED}Error: uv package manager is required${NC}" echo "Install it with: curl -LsSf https://astral.sh/uv/install.sh | sh" exit 1 fi echo -e "${GREEN}✓ uv package manager found${NC}" # Change to backend directory cd "${BACKEND_DIR}" # Check if virtual environment is activated if [[ -z "${VIRTUAL_ENV}" ]]; then # Check if .venv exists in parent directory if [[ -d "../.venv" ]]; then echo -e "${YELLOW}Activating virtual environment...${NC}" source ../.venv/bin/activate success "Virtual environment activated" else echo -e "${YELLOW}ℹ No virtual environment detected${NC}" echo -e "${YELLOW}Run 'make setup-dev' to create environment with uv${NC}" fi fi # Check if requirements are installed info "Checking dependencies..." MISSING_DEPS=() REQUIRED_PACKAGES=("fastapi" "uvicorn" "click" "pydantic" "jinja2") for package in "${REQUIRED_PACKAGES[@]}"; do if ! python3 -c "import ${package}" 2>/dev/null; then MISSING_DEPS+=("${package}") fi done if [[ ${#MISSING_DEPS[@]} -gt 0 ]]; then echo -e "${YELLOW}Missing dependencies: ${MISSING_DEPS[*]}${NC}" echo -e "${YELLOW}Installing dependencies with uv...${NC}" if ! uv pip install -r requirements.txt; then echo -e "${RED}Error: Failed to install dependencies with uv${NC}" echo "You may need to:" echo "1. Create a virtual environment: uv venv" echo "2. Activate it: source .venv/bin/activate" echo "3. Install dependencies: uv pip install -r requirements.txt" echo "4. Or run 'make setup-dev' for complete setup" exit 1 fi success "Dependencies installed" else success "Dependencies already installed" fi # Check if port is available if ! check_port "${PORT}"; then echo -e "${YELLOW}Warning: Port ${PORT} appears to be in use${NC}" echo "The server may fail to start if another process is using this port." read -p "Continue anyway? (y/N): " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then echo "Aborted." exit 1 fi fi # Build uvicorn command UVICORN_CMD="python3 -m uvicorn app.main:app --host ${HOST} --port ${PORT} --log-level ${LOG_LEVEL}" if [[ "${RELOAD}" == "true" ]]; then UVICORN_CMD="${UVICORN_CMD} --reload" fi # Print server info echo "" echo -e "${GREEN}Starting UnitForge server...${NC}" echo -e "${BLUE}Host:${NC} ${HOST}" echo -e "${BLUE}Port:${NC} ${PORT}" echo -e "${BLUE}Auto-reload:${NC} ${RELOAD}" echo -e "${BLUE}Log level:${NC} ${LOG_LEVEL}" echo -e "${BLUE}Package manager:${NC} uv" echo "" echo -e "${GREEN}Server will be available at:${NC}" if [[ "${HOST}" == "0.0.0.0" ]]; then echo -e " ${BLUE}http://localhost:${PORT}${NC}" echo -e " ${BLUE}http://127.0.0.1:${PORT}${NC}" else echo -e " ${BLUE}http://${HOST}:${PORT}${NC}" fi echo "" echo -e "${GREEN}API Documentation:${NC}" if [[ "${HOST}" == "0.0.0.0" ]]; then echo -e " ${BLUE}http://localhost:${PORT}/api/docs${NC}" else echo -e " ${BLUE}http://${HOST}:${PORT}/api/docs${NC}" fi echo "" echo -e "${YELLOW}Press Ctrl+C to stop the server${NC}" echo "" # Set up signal handling for graceful shutdown trap 'echo -e "\n${YELLOW}Shutting down UnitForge server...${NC}"; exit 0' INT TERM # Start the server exec ${UVICORN_CMD}