- FastAPI backend with SQLModel, Alembic migrations, AgentScope agents - Next.js 15 frontend with React 19, Tailwind, Zustand, React Flow - Multi-provider AI system (DashScope, Kling, MiniMax, Volcengine, OpenAI, etc.) - All HTTP clients migrated from sync requests to async httpx - Admin-managed API keys via environment variables - SSRF vulnerability fixed in ensure_url()
344 lines
9.0 KiB
Bash
344 lines
9.0 KiB
Bash
#!/bin/bash
|
||
|
||
# Colors for output
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m' # No Color
|
||
|
||
# Configuration
|
||
BACKEND_PORT=8000
|
||
FRONTEND_PORT=3000
|
||
BACKEND_DIR="backend"
|
||
FRONTEND_DIR="frontend"
|
||
LOG_DIR="logs"
|
||
|
||
# Create logs directory if it doesn't exist
|
||
mkdir -p "$LOG_DIR"
|
||
|
||
# Log files
|
||
BACKEND_LOG="$LOG_DIR/backend.log"
|
||
FRONTEND_LOG="$LOG_DIR/frontend.log"
|
||
|
||
# Function to print colored messages
|
||
print_info() {
|
||
echo -e "${BLUE}ℹ${NC} $1"
|
||
}
|
||
|
||
print_success() {
|
||
echo -e "${GREEN}✓${NC} $1"
|
||
}
|
||
|
||
print_warning() {
|
||
echo -e "${YELLOW}⚠${NC} $1"
|
||
}
|
||
|
||
print_error() {
|
||
echo -e "${RED}✗${NC} $1"
|
||
}
|
||
|
||
# Function to kill background processes on exit
|
||
cleanup() {
|
||
echo ""
|
||
print_info "Stopping all services..."
|
||
|
||
# Kill all background jobs
|
||
jobs -p | xargs -r kill 2>/dev/null
|
||
|
||
# Wait a moment for graceful shutdown
|
||
sleep 1
|
||
|
||
# Force kill if still running
|
||
jobs -p | xargs -r kill -9 2>/dev/null
|
||
|
||
print_success "All services stopped"
|
||
exit 0
|
||
}
|
||
|
||
# Function to check if command exists
|
||
command_exists() {
|
||
command -v "$1" >/dev/null 2>&1
|
||
}
|
||
|
||
# Function to check prerequisites
|
||
check_prerequisites() {
|
||
print_info "Checking prerequisites..."
|
||
|
||
local missing_deps=()
|
||
|
||
# Check for required commands
|
||
if ! command_exists "uv"; then
|
||
missing_deps+=("uv (Python package manager)")
|
||
fi
|
||
|
||
if ! command_exists "node"; then
|
||
missing_deps+=("node (Node.js)")
|
||
fi
|
||
|
||
if ! command_exists "pnpm"; then
|
||
missing_deps+=("pnpm (Node package manager)")
|
||
fi
|
||
|
||
if ! command_exists "curl"; then
|
||
missing_deps+=("curl")
|
||
fi
|
||
|
||
if ! command_exists "lsof"; then
|
||
print_warning "lsof not found, port checking will be limited"
|
||
fi
|
||
|
||
if [ ${#missing_deps[@]} -gt 0 ]; then
|
||
print_error "Missing required dependencies:"
|
||
for dep in "${missing_deps[@]}"; do
|
||
echo " - $dep"
|
||
done
|
||
echo ""
|
||
echo "Please install missing dependencies and try again."
|
||
exit 1
|
||
fi
|
||
|
||
print_success "All prerequisites satisfied"
|
||
}
|
||
|
||
# Function to check and free port
|
||
check_and_free_port() {
|
||
local port=$1
|
||
local name=$2
|
||
|
||
if ! command_exists "lsof"; then
|
||
return 0
|
||
fi
|
||
|
||
# Check if port is in use
|
||
if lsof -i :$port >/dev/null 2>&1; then
|
||
print_warning "Port $port ($name) is in use"
|
||
|
||
# Get PIDs using the port
|
||
local pids=$(lsof -ti :$port)
|
||
|
||
# Ask user for confirmation
|
||
read -p "Kill processes on port $port? (y/N): " -n 1 -r
|
||
echo
|
||
|
||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||
for pid in $pids; do
|
||
print_info "Killing process $pid on port $port..."
|
||
kill -9 $pid 2>/dev/null
|
||
done
|
||
sleep 1
|
||
|
||
# Verify if port is still in use
|
||
if lsof -i :$port >/dev/null 2>&1; then
|
||
print_error "Failed to free port $port. Please free it manually."
|
||
exit 1
|
||
else
|
||
print_success "Port $port freed successfully"
|
||
fi
|
||
else
|
||
print_error "Cannot start service on port $port. Exiting."
|
||
exit 1
|
||
fi
|
||
fi
|
||
}
|
||
|
||
# Function to check if directory exists
|
||
check_directory() {
|
||
local dir=$1
|
||
local name=$2
|
||
|
||
if [ ! -d "$dir" ]; then
|
||
print_error "$name directory not found: $dir"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# Function to start backend
|
||
start_backend() {
|
||
print_info "Starting Backend on port $BACKEND_PORT..."
|
||
|
||
cd "$BACKEND_DIR" || exit 1
|
||
|
||
# Check if .env file exists
|
||
if [ ! -f ".env" ]; then
|
||
print_warning "Backend .env file not found. Using default configuration."
|
||
fi
|
||
|
||
# Set PYTHONPATH
|
||
export PYTHONPATH=$PYTHONPATH:$(pwd)
|
||
|
||
# Start backend with logging
|
||
uv run uvicorn src.main:app --reload --port $BACKEND_PORT > "../$BACKEND_LOG" 2>&1 &
|
||
BACKEND_PID=$!
|
||
|
||
cd ..
|
||
|
||
print_info "Backend PID: $BACKEND_PID (logs: $BACKEND_LOG)"
|
||
}
|
||
|
||
# Function to wait for backend to be ready
|
||
wait_for_backend() {
|
||
print_info "Waiting for backend to be ready..."
|
||
|
||
local max_retries=30
|
||
local retry_count=0
|
||
local health_url="http://localhost:$BACKEND_PORT/health"
|
||
|
||
while [ $retry_count -lt $max_retries ]; do
|
||
if curl -s "$health_url" > /dev/null 2>&1; then
|
||
print_success "Backend is ready!"
|
||
return 0
|
||
fi
|
||
|
||
# Check if backend process is still running
|
||
if ! kill -0 $BACKEND_PID 2>/dev/null; then
|
||
print_error "Backend process died. Check logs: $BACKEND_LOG"
|
||
tail -n 20 "$BACKEND_LOG"
|
||
return 1
|
||
fi
|
||
|
||
retry_count=$((retry_count + 1))
|
||
|
||
# Show progress
|
||
printf "\r Attempt %d/%d..." $retry_count $max_retries
|
||
|
||
sleep 1
|
||
done
|
||
|
||
echo ""
|
||
print_error "Backend failed to start after $max_retries seconds"
|
||
print_info "Last 20 lines of backend log:"
|
||
tail -n 20 "$BACKEND_LOG"
|
||
return 1
|
||
}
|
||
|
||
# Function to start frontend
|
||
start_frontend() {
|
||
print_info "Starting Frontend on port $FRONTEND_PORT..."
|
||
|
||
cd "$FRONTEND_DIR" || exit 1
|
||
|
||
# Check if node_modules exists
|
||
if [ ! -d "node_modules" ]; then
|
||
print_warning "node_modules not found. Running pnpm install..."
|
||
pnpm install
|
||
fi
|
||
|
||
# Start frontend with logging
|
||
pnpm dev > "../$FRONTEND_LOG" 2>&1 &
|
||
FRONTEND_PID=$!
|
||
|
||
cd ..
|
||
|
||
print_info "Frontend PID: $FRONTEND_PID (logs: $FRONTEND_LOG)"
|
||
}
|
||
|
||
# Function to wait for frontend to be ready
|
||
wait_for_frontend() {
|
||
print_info "Waiting for frontend to be ready..."
|
||
|
||
local max_retries=30
|
||
local retry_count=0
|
||
|
||
while [ $retry_count -lt $max_retries ]; do
|
||
if curl -s "http://localhost:$FRONTEND_PORT" > /dev/null 2>&1; then
|
||
print_success "Frontend is ready!"
|
||
return 0
|
||
fi
|
||
|
||
# Check if frontend process is still running
|
||
if ! kill -0 $FRONTEND_PID 2>/dev/null; then
|
||
print_error "Frontend process died. Check logs: $FRONTEND_LOG"
|
||
tail -n 20 "$FRONTEND_LOG"
|
||
return 1
|
||
fi
|
||
|
||
retry_count=$((retry_count + 1))
|
||
|
||
# Show progress
|
||
printf "\r Attempt %d/%d..." $retry_count $max_retries
|
||
|
||
sleep 1
|
||
done
|
||
|
||
echo ""
|
||
print_error "Frontend failed to start after $max_retries seconds"
|
||
print_info "Last 20 lines of frontend log:"
|
||
tail -n 20 "$FRONTEND_LOG"
|
||
return 1
|
||
}
|
||
|
||
# Function to display service information
|
||
display_info() {
|
||
echo ""
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
print_success "All services started successfully!"
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
echo ""
|
||
echo " 📡 Backend API: http://localhost:$BACKEND_PORT"
|
||
echo " 📚 API Docs: http://localhost:$BACKEND_PORT/docs"
|
||
echo " 📖 ReDoc: http://localhost:$BACKEND_PORT/redoc"
|
||
echo " 🏥 Health Check: http://localhost:$BACKEND_PORT/health"
|
||
echo " 📊 Metrics: http://localhost:$BACKEND_PORT/metrics"
|
||
echo ""
|
||
echo " 🎨 Frontend: http://localhost:$FRONTEND_PORT"
|
||
echo ""
|
||
echo " 📝 Backend Logs: $BACKEND_LOG"
|
||
echo " 📝 Frontend Logs: $FRONTEND_LOG"
|
||
echo ""
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
echo ""
|
||
print_info "Press Ctrl+C to stop all services"
|
||
echo ""
|
||
}
|
||
|
||
# Main execution
|
||
main() {
|
||
echo ""
|
||
echo "╔═══════════════════════════════════════════════════════╗"
|
||
echo "║ Pixel Development Environment Startup ║"
|
||
echo "╚═══════════════════════════════════════════════════════╝"
|
||
echo ""
|
||
|
||
# Trap SIGINT (Ctrl+C) and call cleanup
|
||
trap cleanup SIGINT SIGTERM
|
||
|
||
# Check prerequisites
|
||
check_prerequisites
|
||
|
||
# Check directories
|
||
check_directory "$BACKEND_DIR" "Backend"
|
||
check_directory "$FRONTEND_DIR" "Frontend"
|
||
|
||
# Check and free ports
|
||
check_and_free_port $BACKEND_PORT "Backend"
|
||
check_and_free_port $FRONTEND_PORT "Frontend"
|
||
|
||
# Start backend
|
||
start_backend
|
||
|
||
# Wait for backend to be ready
|
||
if ! wait_for_backend; then
|
||
cleanup
|
||
exit 1
|
||
fi
|
||
|
||
# Start frontend
|
||
start_frontend
|
||
|
||
# Wait for frontend to be ready
|
||
if ! wait_for_frontend; then
|
||
cleanup
|
||
exit 1
|
||
fi
|
||
|
||
# Display service information
|
||
display_info
|
||
|
||
# Wait for processes
|
||
wait
|
||
}
|
||
|
||
# Run main function
|
||
main
|