| #!/bin/bash |
|
|
| |
| |
|
|
| set -e |
|
|
| |
| RED='\033[0;31m' |
| GREEN='\033[0;32m' |
| YELLOW='\033[1;33m' |
| BLUE='\033[0;34m' |
| PURPLE='\033[0;35m' |
| NC='\033[0m' |
|
|
| |
| PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" |
| BACKEND_DIR="$PROJECT_ROOT/backend" |
| FRONTEND_DIR="$PROJECT_ROOT/frontend" |
| DATA_DIR="$PROJECT_ROOT/data" |
| LOGS_DIR="$PROJECT_ROOT/logs" |
|
|
| |
| MODE="local" |
| SKIP_DEPS=false |
| SKIP_DB_INIT=false |
| SKIP_FRONTEND=false |
| VERBOSE=false |
| DETACHED=false |
|
|
| |
| print_header() { |
| echo -e "${PURPLE}================================${NC}" |
| echo -e "${PURPLE}$1${NC}" |
| echo -e "${PURPLE}================================${NC}" |
| } |
|
|
| print_status() { |
| echo -e "${BLUE}[INFO]${NC} $1" |
| } |
|
|
| print_success() { |
| echo -e "${GREEN}[SUCCESS]${NC} $1" |
| } |
|
|
| print_warning() { |
| echo -e "${YELLOW}[WARNING]${NC} $1" |
| } |
|
|
| print_error() { |
| echo -e "${RED}[ERROR]${NC} $1" |
| } |
|
|
| |
| show_usage() { |
| echo "Usage: $0 [OPTIONS]" |
| echo "" |
| echo "Options:" |
| echo " -h, --help Show this help message" |
| echo " -m, --mode MODE Set deployment mode (local|cloud) [default: local]" |
| echo " -s, --skip-deps Skip dependency installation" |
| echo " -d, --skip-db Skip database initialization" |
| echo " -f, --skip-frontend Skip frontend serving" |
| echo " -v, --verbose Enable verbose output" |
| echo " -D, --detached Run services in detached mode" |
| echo " --stop Stop all running services" |
| echo " --status Show status of running services" |
| echo " --logs Show logs from all services" |
| echo "" |
| echo "Examples:" |
| echo " $0 Start all services in local mode" |
| echo " $0 --mode cloud Start with cloud configuration" |
| echo " $0 --skip-deps Start without installing dependencies" |
| echo " $0 --detached Start services in background" |
| echo " $0 --stop Stop all running services" |
| } |
|
|
| |
| parse_args() { |
| while [[ $# -gt 0 ]]; do |
| case $1 in |
| -h|--help) |
| show_usage |
| exit 0 |
| ;; |
| -m|--mode) |
| MODE="$2" |
| shift 2 |
| ;; |
| -s|--skip-deps) |
| SKIP_DEPS=true |
| shift |
| ;; |
| -d|--skip-db) |
| SKIP_DB_INIT=true |
| shift |
| ;; |
| -f|--skip-frontend) |
| SKIP_FRONTEND=true |
| shift |
| ;; |
| -v|--verbose) |
| VERBOSE=true |
| shift |
| ;; |
| -D|--detached) |
| DETACHED=true |
| shift |
| ;; |
| --stop) |
| stop_services |
| exit 0 |
| ;; |
| --status) |
| show_status |
| exit 0 |
| ;; |
| --logs) |
| show_logs |
| exit 0 |
| ;; |
| *) |
| print_error "Unknown option: $1" |
| show_usage |
| exit 1 |
| ;; |
| esac |
| done |
| } |
|
|
| |
| check_prerequisites() { |
| print_status "Checking prerequisites..." |
| |
| |
| if ! command -v python3 &> /dev/null; then |
| print_error "Python 3 is not installed. Please install Python 3.8 or higher." |
| exit 1 |
| fi |
| |
| local python_version=$(python3 --version | cut -d' ' -f2) |
| print_status "Python version: $python_version" |
| |
| |
| if ! command -v pip3 &> /dev/null; then |
| print_error "pip3 is not installed. Please install pip3." |
| exit 1 |
| fi |
| |
| |
| if [[ "$SKIP_FRONTEND" == false ]]; then |
| if ! command -v node &> /dev/null; then |
| print_warning "Node.js not found. Frontend serving will be disabled." |
| SKIP_FRONTEND=true |
| else |
| local node_version=$(node --version) |
| print_status "Node.js version: $node_version" |
| fi |
| fi |
| |
| |
| for dir in "$BACKEND_DIR" "$FRONTEND_DIR" "$DATA_DIR"; do |
| if [[ ! -d "$dir" ]]; then |
| print_error "Required directory not found: $dir" |
| exit 1 |
| fi |
| done |
| |
| print_success "Prerequisites check completed" |
| } |
|
|
| |
| create_directories() { |
| print_status "Creating necessary directories..." |
| |
| mkdir -p "$LOGS_DIR" |
| mkdir -p "$DATA_DIR" |
| mkdir -p "$PROJECT_ROOT/.env" |
| |
| print_success "Directories created" |
| } |
|
|
| |
| install_dependencies() { |
| if [[ "$SKIP_DEPS" == true ]]; then |
| print_warning "Skipping dependency installation" |
| return |
| fi |
| |
| print_status "Installing Python dependencies..." |
| |
| cd "$BACKEND_DIR" |
| |
| |
| if [[ ! -d "venv" ]]; then |
| print_status "Creating Python virtual environment..." |
| python3 -m venv venv |
| fi |
| |
| |
| source venv/bin/activate |
| |
| |
| pip install --upgrade pip |
| |
| |
| if [[ -f "requirements.txt" ]]; then |
| pip install -r requirements.txt |
| else |
| print_error "requirements.txt not found in backend directory" |
| exit 1 |
| fi |
| |
| print_success "Python dependencies installed" |
| |
| |
| if [[ "$SKIP_FRONTEND" == false && -f "$FRONTEND_DIR/package.json" ]]; then |
| print_status "Installing frontend dependencies..." |
| cd "$FRONTEND_DIR" |
| npm install |
| print_success "Frontend dependencies installed" |
| fi |
| } |
|
|
| |
| initialize_database() { |
| if [[ "$SKIP_DB_INIT" == true ]]; then |
| print_warning "Skipping database initialization" |
| return |
| fi |
| |
| print_status "Initializing database..." |
| |
| cd "$BACKEND_DIR" |
| source venv/bin/activate |
| |
| |
| if [[ -f "init_db.py" ]]; then |
| python init_db.py --mode "$MODE" |
| print_success "Database initialized" |
| else |
| print_warning "Database initialization script not found, skipping..." |
| fi |
| } |
|
|
| |
| setup_environment() { |
| print_status "Setting up environment variables..." |
| |
| |
| local env_file="$PROJECT_ROOT/.env" |
| if [[ ! -f "$env_file" ]]; then |
| print_status "Creating .env file..." |
| cat > "$env_file" << EOF |
| # Misinformation Heatmap Configuration |
| MODE=$MODE |
| DEBUG=true |
| LOG_LEVEL=INFO |
| |
| # Database Configuration |
| DATABASE_URL=sqlite:///./data/heatmap.db |
| |
| # API Configuration |
| API_HOST=0.0.0.0 |
| API_PORT=8000 |
| CORS_ORIGINS=["http://localhost:3000", "http://localhost:8000"] |
| |
| # NLP Configuration |
| HUGGINGFACE_TOKEN= |
| MODEL_CACHE_DIR=./models |
| |
| # Pub/Sub Configuration (Local) |
| PUBSUB_EMULATOR_HOST=localhost:8085 |
| PUBSUB_PROJECT_ID=misinformation-heatmap-local |
| |
| # Google Cloud Configuration (for cloud mode) |
| GOOGLE_CLOUD_PROJECT= |
| GOOGLE_APPLICATION_CREDENTIALS= |
| |
| # IBM Watson Configuration (for cloud mode) |
| WATSON_DISCOVERY_API_KEY= |
| WATSON_DISCOVERY_URL= |
| |
| # Monitoring Configuration |
| ENABLE_METRICS=true |
| METRICS_PORT=9090 |
| EOF |
| print_success ".env file created" |
| else |
| print_status ".env file already exists" |
| fi |
| } |
|
|
| |
| start_pubsub_emulator() { |
| if [[ "$MODE" != "local" ]]; then |
| return |
| fi |
| |
| print_status "Starting Pub/Sub emulator..." |
| |
| |
| if ! command -v gcloud &> /dev/null; then |
| print_warning "gcloud CLI not found. Installing Pub/Sub emulator..." |
| |
| pip install google-cloud-pubsub |
| fi |
| |
| |
| local pubsub_log="$LOGS_DIR/pubsub-emulator.log" |
| |
| if [[ "$DETACHED" == true ]]; then |
| nohup gcloud beta emulators pubsub start --host-port=localhost:8085 > "$pubsub_log" 2>&1 & |
| local pubsub_pid=$! |
| echo $pubsub_pid > "$LOGS_DIR/pubsub.pid" |
| print_success "Pub/Sub emulator started (PID: $pubsub_pid)" |
| else |
| print_status "Starting Pub/Sub emulator (press Ctrl+C to stop all services)" |
| gcloud beta emulators pubsub start --host-port=localhost:8085 & |
| local pubsub_pid=$! |
| echo $pubsub_pid > "$LOGS_DIR/pubsub.pid" |
| fi |
| |
| |
| sleep 3 |
| |
| |
| export PUBSUB_EMULATOR_HOST=localhost:8085 |
| python3 -c " |
| import os |
| from google.cloud import pubsub_v1 |
| |
| os.environ['PUBSUB_EMULATOR_HOST'] = 'localhost:8085' |
| project_id = 'misinformation-heatmap-local' |
| |
| publisher = pubsub_v1.PublisherClient() |
| subscriber = pubsub_v1.SubscriberClient() |
| |
| # Create topics |
| topics = ['events-raw', 'events-processed', 'events-validated'] |
| for topic_name in topics: |
| topic_path = publisher.topic_path(project_id, topic_name) |
| try: |
| publisher.create_topic(request={'name': topic_path}) |
| print(f'Created topic: {topic_name}') |
| except Exception as e: |
| print(f'Topic {topic_name} may already exist: {e}') |
| |
| # Create subscriptions |
| subscriptions = [ |
| ('events-raw', 'events-raw-sub'), |
| ('events-processed', 'events-processed-sub'), |
| ('events-validated', 'events-validated-sub') |
| ] |
| |
| for topic_name, sub_name in subscriptions: |
| topic_path = publisher.topic_path(project_id, topic_name) |
| subscription_path = subscriber.subscription_path(project_id, sub_name) |
| try: |
| subscriber.create_subscription( |
| request={'name': subscription_path, 'topic': topic_path} |
| ) |
| print(f'Created subscription: {sub_name}') |
| except Exception as e: |
| print(f'Subscription {sub_name} may already exist: {e}') |
| " |
| } |
|
|
| |
| start_backend() { |
| print_status "Starting backend API server..." |
| |
| cd "$BACKEND_DIR" |
| source venv/bin/activate |
| |
| local api_log="$LOGS_DIR/api.log" |
| |
| if [[ "$DETACHED" == true ]]; then |
| nohup python api.py > "$api_log" 2>&1 & |
| local api_pid=$! |
| echo $api_pid > "$LOGS_DIR/api.pid" |
| print_success "Backend API started (PID: $api_pid)" |
| else |
| print_status "Starting backend API (press Ctrl+C to stop all services)" |
| python api.py & |
| local api_pid=$! |
| echo $api_pid > "$LOGS_DIR/api.pid" |
| fi |
| } |
|
|
| |
| start_frontend() { |
| if [[ "$SKIP_FRONTEND" == true ]]; then |
| print_warning "Skipping frontend server" |
| return |
| fi |
| |
| print_status "Starting frontend server..." |
| |
| cd "$FRONTEND_DIR" |
| |
| local frontend_log="$LOGS_DIR/frontend.log" |
| |
| |
| if [[ "$DETACHED" == true ]]; then |
| nohup python3 -m http.server 3000 > "$frontend_log" 2>&1 & |
| local frontend_pid=$! |
| echo $frontend_pid > "$LOGS_DIR/frontend.pid" |
| print_success "Frontend server started (PID: $frontend_pid)" |
| else |
| print_status "Starting frontend server (press Ctrl+C to stop all services)" |
| python3 -m http.server 3000 & |
| local frontend_pid=$! |
| echo $frontend_pid > "$LOGS_DIR/frontend.pid" |
| fi |
| } |
|
|
| |
| perform_health_checks() { |
| print_status "Performing health checks..." |
| |
| |
| sleep 5 |
| |
| |
| if curl -s http://localhost:8000/health > /dev/null; then |
| print_success "Backend API is healthy" |
| else |
| print_warning "Backend API health check failed" |
| fi |
| |
| |
| if [[ "$SKIP_FRONTEND" == false ]]; then |
| if curl -s http://localhost:3000 > /dev/null; then |
| print_success "Frontend server is healthy" |
| else |
| print_warning "Frontend server health check failed" |
| fi |
| fi |
| |
| |
| if [[ "$MODE" == "local" ]]; then |
| if curl -s http://localhost:8085 > /dev/null; then |
| print_success "Pub/Sub emulator is healthy" |
| else |
| print_warning "Pub/Sub emulator health check failed" |
| fi |
| fi |
| } |
|
|
| |
| show_status() { |
| print_header "Service Status" |
| |
| |
| if [[ -f "$LOGS_DIR/api.pid" ]]; then |
| local api_pid=$(cat "$LOGS_DIR/api.pid") |
| if ps -p $api_pid > /dev/null; then |
| print_success "Backend API running (PID: $api_pid)" |
| else |
| print_warning "Backend API not running (stale PID file)" |
| fi |
| else |
| print_warning "Backend API not started" |
| fi |
| |
| |
| if [[ -f "$LOGS_DIR/frontend.pid" ]]; then |
| local frontend_pid=$(cat "$LOGS_DIR/frontend.pid") |
| if ps -p $frontend_pid > /dev/null; then |
| print_success "Frontend server running (PID: $frontend_pid)" |
| else |
| print_warning "Frontend server not running (stale PID file)" |
| fi |
| else |
| print_warning "Frontend server not started" |
| fi |
| |
| |
| if [[ -f "$LOGS_DIR/pubsub.pid" ]]; then |
| local pubsub_pid=$(cat "$LOGS_DIR/pubsub.pid") |
| if ps -p $pubsub_pid > /dev/null; then |
| print_success "Pub/Sub emulator running (PID: $pubsub_pid)" |
| else |
| print_warning "Pub/Sub emulator not running (stale PID file)" |
| fi |
| else |
| print_warning "Pub/Sub emulator not started" |
| fi |
| } |
|
|
| |
| show_logs() { |
| print_header "Service Logs" |
| |
| if [[ -f "$LOGS_DIR/api.log" ]]; then |
| echo -e "${BLUE}=== Backend API Logs ===${NC}" |
| tail -n 20 "$LOGS_DIR/api.log" |
| echo "" |
| fi |
| |
| if [[ -f "$LOGS_DIR/frontend.log" ]]; then |
| echo -e "${BLUE}=== Frontend Server Logs ===${NC}" |
| tail -n 20 "$LOGS_DIR/frontend.log" |
| echo "" |
| fi |
| |
| if [[ -f "$LOGS_DIR/pubsub-emulator.log" ]]; then |
| echo -e "${BLUE}=== Pub/Sub Emulator Logs ===${NC}" |
| tail -n 20 "$LOGS_DIR/pubsub-emulator.log" |
| echo "" |
| fi |
| } |
|
|
| |
| stop_services() { |
| print_header "Stopping Services" |
| |
| |
| if [[ -f "$LOGS_DIR/api.pid" ]]; then |
| local api_pid=$(cat "$LOGS_DIR/api.pid") |
| if ps -p $api_pid > /dev/null; then |
| kill $api_pid |
| print_success "Backend API stopped" |
| fi |
| rm -f "$LOGS_DIR/api.pid" |
| fi |
| |
| |
| if [[ -f "$LOGS_DIR/frontend.pid" ]]; then |
| local frontend_pid=$(cat "$LOGS_DIR/frontend.pid") |
| if ps -p $frontend_pid > /dev/null; then |
| kill $frontend_pid |
| print_success "Frontend server stopped" |
| fi |
| rm -f "$LOGS_DIR/frontend.pid" |
| fi |
| |
| |
| if [[ -f "$LOGS_DIR/pubsub.pid" ]]; then |
| local pubsub_pid=$(cat "$LOGS_DIR/pubsub.pid") |
| if ps -p $pubsub_pid > /dev/null; then |
| kill $pubsub_pid |
| print_success "Pub/Sub emulator stopped" |
| fi |
| rm -f "$LOGS_DIR/pubsub.pid" |
| fi |
| |
| print_success "All services stopped" |
| } |
|
|
| |
| cleanup() { |
| if [[ "$DETACHED" == false ]]; then |
| print_status "Cleaning up..." |
| stop_services |
| fi |
| } |
|
|
| |
| show_service_urls() { |
| print_header "Service URLs" |
| echo -e "${GREEN}Backend API:${NC} http://localhost:8000" |
| echo -e "${GREEN}API Documentation:${NC} http://localhost:8000/docs" |
| echo -e "${GREEN}Health Check:${NC} http://localhost:8000/health" |
| |
| if [[ "$SKIP_FRONTEND" == false ]]; then |
| echo -e "${GREEN}Frontend:${NC} http://localhost:3000" |
| fi |
| |
| if [[ "$MODE" == "local" ]]; then |
| echo -e "${GREEN}Pub/Sub Emulator:${NC} http://localhost:8085" |
| fi |
| |
| echo "" |
| echo -e "${YELLOW}Logs Directory:${NC} $LOGS_DIR" |
| echo -e "${YELLOW}Data Directory:${NC} $DATA_DIR" |
| } |
|
|
| |
| main() { |
| print_header "Misinformation Heatmap - Local Development Setup" |
| |
| parse_args "$@" |
| |
| |
| trap cleanup EXIT INT TERM |
| |
| check_prerequisites |
| create_directories |
| setup_environment |
| install_dependencies |
| initialize_database |
| |
| |
| if [[ "$MODE" == "local" ]]; then |
| start_pubsub_emulator |
| fi |
| |
| start_backend |
| start_frontend |
| |
| |
| perform_health_checks |
| |
| |
| show_service_urls |
| |
| if [[ "$DETACHED" == true ]]; then |
| print_success "All services started in detached mode" |
| print_status "Use '$0 --status' to check service status" |
| print_status "Use '$0 --logs' to view service logs" |
| print_status "Use '$0 --stop' to stop all services" |
| else |
| print_success "All services started successfully!" |
| print_status "Press Ctrl+C to stop all services" |
| |
| |
| while true; do |
| sleep 1 |
| done |
| fi |
| } |
|
|
| |
| main "$@" |