#!/bin/bash ################################################################################ # BAIC Dashboard Deployment Script # # This script automates the deployment of the BAIC Dashboard application # on an Ubuntu server with Apache and systemd. # # Prerequisites: # - Repository cloned to /opt/baic_dashboard # - .env file configured in repository root # - Run this script from /opt/baic_dashboard directory # - Execute with sudo/root privileges # # Usage: sudo bash deploy.sh ################################################################################ set -e # Exit on any error # Color codes for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Expected deployment directory EXPECTED_DIR="/opt/baic_dashboard" SERVICE_NAME="baic-dashboard" SERVICE_USER="www-data" ################################################################################ # Helper Functions ################################################################################ print_header() { echo -e "\n${BLUE}========================================${NC}" echo -e "${BLUE}$1${NC}" echo -e "${BLUE}========================================${NC}\n" } print_success() { echo -e "${GREEN}✓ $1${NC}" } print_error() { echo -e "${RED}✗ ERROR: $1${NC}" } print_warning() { echo -e "${YELLOW}⚠ WARNING: $1${NC}" } print_info() { echo -e "${BLUE}ℹ $1${NC}" } exit_with_error() { print_error "$1" exit 1 } ################################################################################ # Pre-flight Checks ################################################################################ print_header "Pre-flight Checks" # Check if running as root if [[ $EUID -ne 0 ]]; then exit_with_error "This script must be run as root (use sudo)" fi print_success "Running as root" # Check if we're in the correct directory CURRENT_DIR=$(pwd) if [[ "$CURRENT_DIR" != "$EXPECTED_DIR" ]]; then exit_with_error "Script must be run from $EXPECTED_DIR (currently in $CURRENT_DIR)" fi print_success "Running from correct directory: $EXPECTED_DIR" # Check for required commands REQUIRED_COMMANDS=("python3" "npm" "systemctl") for cmd in "${REQUIRED_COMMANDS[@]}"; do if ! command -v $cmd &> /dev/null; then exit_with_error "$cmd is not installed. Please install it first." fi print_success "$cmd is installed" done # Check if .env file exists if [[ ! -f ".env" ]]; then exit_with_error ".env file not found in current directory. Please create it before running this script." fi print_success ".env file found" # Check if apache2 is installed (warn if not, but don't fail) if ! command -v apache2 &> /dev/null; then print_warning "apache2 is not installed. You'll need to install and configure it manually." else print_success "apache2 is installed" fi ################################################################################ # Backend Setup ################################################################################ print_header "Backend Setup" # Create Python virtual environment print_info "Creating Python virtual environment..." if [[ -d "venv" ]]; then print_warning "Virtual environment already exists. Removing and recreating..." rm -rf venv fi python3 -m venv venv if [[ $? -ne 0 ]]; then exit_with_error "Failed to create virtual environment" fi print_success "Virtual environment created" # Activate virtual environment and install dependencies print_info "Installing Python dependencies from requirements.txt..." source venv/bin/activate if [[ ! -f "backend/requirements.txt" ]]; then exit_with_error "backend/requirements.txt not found" fi pip install --upgrade pip > /dev/null 2>&1 pip install -r backend/requirements.txt if [[ $? -ne 0 ]]; then exit_with_error "Failed to install Python dependencies" fi print_success "Python dependencies installed" # Test that Flask can be imported python3 -c "import flask" 2>/dev/null if [[ $? -ne 0 ]]; then exit_with_error "Flask installation verification failed" fi print_success "Flask installation verified" deactivate # Set ownership to www-data print_info "Setting ownership to $SERVICE_USER..." chown -R $SERVICE_USER:$SERVICE_USER venv chown -R $SERVICE_USER:$SERVICE_USER backend print_success "Ownership set to $SERVICE_USER" ################################################################################ # Frontend Build ################################################################################ print_header "Frontend Build" # Check if frontend directory exists if [[ ! -d "frontend" ]]; then exit_with_error "frontend directory not found" fi cd frontend # Install npm dependencies print_info "Installing npm dependencies..." npm install if [[ $? -ne 0 ]]; then exit_with_error "Failed to install npm dependencies" fi print_success "npm dependencies installed" # Copy .env file to frontend directory for Vite to read during build print_info "Copying .env to frontend directory for build..." cp ../.env ./.env if [[ $? -ne 0 ]]; then exit_with_error "Failed to copy .env file" fi print_success ".env file copied" # Build frontend print_info "Building frontend for production (this may take a minute)..." npm run build if [[ $? -ne 0 ]]; then exit_with_error "Frontend build failed" fi # Remove .env from frontend directory (cleanup) print_info "Cleaning up temporary .env file..." rm -f ./.env print_success "Cleanup complete" # Verify dist directory was created if [[ ! -d "dist" ]]; then exit_with_error "Build completed but dist/ directory not found" fi print_success "Frontend built successfully" cd .. ################################################################################ # Frontend Deployment ################################################################################ print_header "Frontend Deployment" # Prompt user for frontend deployment path echo -e "${BLUE}Please enter the full path where the frontend files should be deployed${NC}" echo -e "${BLUE}(e.g., /var/www/html/dashboard or /var/www/mysite.com/dashboard)${NC}" read -p "Deployment path: " FRONTEND_DEPLOY_PATH # Validate input if [[ -z "$FRONTEND_DEPLOY_PATH" ]]; then exit_with_error "No deployment path provided" fi # Create directory if it doesn't exist if [[ ! -d "$FRONTEND_DEPLOY_PATH" ]]; then print_info "Creating directory: $FRONTEND_DEPLOY_PATH" mkdir -p "$FRONTEND_DEPLOY_PATH" if [[ $? -ne 0 ]]; then exit_with_error "Failed to create deployment directory" fi fi # Copy frontend files print_info "Copying frontend files to $FRONTEND_DEPLOY_PATH..." cp -r frontend/dist/* "$FRONTEND_DEPLOY_PATH/" if [[ $? -ne 0 ]]; then exit_with_error "Failed to copy frontend files" fi print_success "Frontend files copied" # Set ownership and permissions print_info "Setting ownership and permissions..." chown -R $SERVICE_USER:$SERVICE_USER "$FRONTEND_DEPLOY_PATH" find "$FRONTEND_DEPLOY_PATH" -type d -exec chmod 755 {} \; find "$FRONTEND_DEPLOY_PATH" -type f -exec chmod 644 {} \; print_success "Ownership and permissions set" ################################################################################ # Systemd Service Creation ################################################################################ print_header "Systemd Service Setup" # Read BACKEND_PORT from .env or use default BACKEND_PORT=$(grep "^BACKEND_PORT=" .env | cut -d'=' -f2 | tr -d ' ') if [[ -z "$BACKEND_PORT" ]]; then BACKEND_PORT="5001" print_warning "BACKEND_PORT not found in .env, using default: 5001" else print_info "Using BACKEND_PORT from .env: $BACKEND_PORT" fi SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service" print_info "Creating systemd service file: $SERVICE_FILE" cat > "$SERVICE_FILE" << EOF [Unit] Description=BAIC Dashboard Backend Service After=network.target [Service] Type=simple User=$SERVICE_USER Group=$SERVICE_USER WorkingDirectory=$EXPECTED_DIR/backend EnvironmentFile=$EXPECTED_DIR/.env ExecStart=$EXPECTED_DIR/venv/bin/python app.py # Restart policy Restart=always RestartSec=10 # Security settings NoNewPrivileges=true PrivateTmp=true # Logging StandardOutput=journal StandardError=journal SyslogIdentifier=$SERVICE_NAME [Install] WantedBy=multi-user.target EOF if [[ $? -ne 0 ]]; then exit_with_error "Failed to create systemd service file" fi print_success "Systemd service file created" ################################################################################ # Service Management ################################################################################ print_header "Service Management" # Reload systemd daemon print_info "Reloading systemd daemon..." systemctl daemon-reload if [[ $? -ne 0 ]]; then exit_with_error "Failed to reload systemd daemon" fi print_success "Systemd daemon reloaded" # Enable service print_info "Enabling service to start on boot..." systemctl enable $SERVICE_NAME if [[ $? -ne 0 ]]; then exit_with_error "Failed to enable service" fi print_success "Service enabled" # Restart service (to ensure code changes are picked up) print_info "Restarting service..." systemctl restart $SERVICE_NAME if [[ $? -ne 0 ]]; then print_error "Failed to restart service. Check logs with: journalctl -u $SERVICE_NAME -n 50" exit 1 fi print_success "Service restarted" # Wait a moment for service to initialize sleep 2 # Check service status print_info "Checking service status..." if systemctl is-active --quiet $SERVICE_NAME; then print_success "Service is running" echo "" systemctl status $SERVICE_NAME --no-pager -l else print_error "Service failed to start properly" print_info "Showing recent logs:" journalctl -u $SERVICE_NAME -n 20 --no-pager exit 1 fi ################################################################################ # Apache Configuration Generation ################################################################################ print_header "Apache Configuration" # Get domain and base path from .env DOMAIN=$(grep "^DOMAIN=" .env | cut -d'=' -f2 | tr -d ' ') BASE_PATH=$(grep "^BASE_PATH=" .env | cut -d'=' -f2 | tr -d ' ') if [[ -z "$DOMAIN" ]]; then DOMAIN="yourdomain.com" print_warning "DOMAIN not found in .env, using placeholder in config" fi if [[ -z "$BASE_PATH" ]]; then BASE_PATH="/dashboard/" print_warning "BASE_PATH not found in .env, using default: /dashboard/" fi # Remove trailing slash from BASE_PATH for cleaner config BASE_PATH_NO_SLASH=${BASE_PATH%/} APACHE_CONFIG_FILE="$EXPECTED_DIR/apache-config.conf" print_info "Generating Apache configuration: $APACHE_CONFIG_FILE" cat > "$APACHE_CONFIG_FILE" << EOF # Apache Configuration for BAIC Dashboard # # Add this configuration to your Apache virtual host file # (e.g., /etc/apache2/sites-available/000-default.conf or your custom site config) # # Required Apache modules (enable with a2enmod): # - proxy # - proxy_http # - headers # - rewrite # # Enable modules: # sudo a2enmod proxy proxy_http headers rewrite # sudo systemctl restart apache2 ServerName $DOMAIN # SSL Configuration (adjust paths to your certificates) # SSLEngine on # SSLCertificateFile /etc/letsencrypt/live/$DOMAIN/fullchain.pem # SSLCertificateKeyFile /etc/letsencrypt/live/$DOMAIN/privkey.pem # Frontend static files Alias $BASE_PATH_NO_SLASH $FRONTEND_DEPLOY_PATH Options -Indexes +FollowSymLinks AllowOverride All Require all granted # Handle client-side routing (React Router) RewriteEngine On RewriteBase $BASE_PATH_NO_SLASH/ RewriteRule ^index\.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . $BASE_PATH_NO_SLASH/index.html [L] # Backend API reverse proxy ProxyPreserveHost On ProxyTimeout 300 ProxyPass http://localhost:$BACKEND_PORT/api ProxyPassReverse http://localhost:$BACKEND_PORT/api # Add headers for proper proxying RequestHeader set X-Forwarded-Proto "https" RequestHeader set X-Forwarded-Port "443" # Error and Access logs ErrorLog \${APACHE_LOG_DIR}/${SERVICE_NAME}-error.log CustomLog \${APACHE_LOG_DIR}/${SERVICE_NAME}-access.log combined # Optional: Redirect HTTP to HTTPS ServerName $DOMAIN Redirect permanent / https://$DOMAIN/ EOF if [[ $? -ne 0 ]]; then print_error "Failed to create Apache configuration file" else print_success "Apache configuration file created" fi ################################################################################ # Deployment Complete ################################################################################ print_header "Deployment Complete!" echo -e "${GREEN}✓ Backend virtual environment created and dependencies installed${NC}" echo -e "${GREEN}✓ Frontend built and deployed to: $FRONTEND_DEPLOY_PATH${NC}" echo -e "${GREEN}✓ Systemd service created and restarted: $SERVICE_NAME${NC}" echo -e "${GREEN}✓ Apache configuration generated: $APACHE_CONFIG_FILE${NC}" echo "" print_header "Next Steps" echo -e "${YELLOW}1. Review the Apache configuration:${NC}" echo -e " cat $APACHE_CONFIG_FILE" echo "" echo -e "${YELLOW}2. Enable required Apache modules:${NC}" echo -e " sudo a2enmod proxy proxy_http headers rewrite" echo "" echo -e "${YELLOW}3. Add the configuration to your Apache virtual host:${NC}" echo -e " - Edit: /etc/apache2/sites-available/your-site.conf" echo -e " - Or copy: sudo cp $APACHE_CONFIG_FILE /etc/apache2/sites-available/${SERVICE_NAME}.conf" echo -e " - Enable: sudo a2ensite ${SERVICE_NAME}" echo "" echo -e "${YELLOW}4. Test Apache configuration:${NC}" echo -e " sudo apache2ctl configtest" echo "" echo -e "${YELLOW}5. Reload Apache:${NC}" echo -e " sudo systemctl reload apache2" echo "" echo -e "${YELLOW}6. Configure SSL/TLS certificates (if not already done):${NC}" echo -e " sudo apt install certbot python3-certbot-apache" echo -e " sudo certbot --apache -d $DOMAIN" echo "" print_header "Useful Commands" echo -e "${BLUE}Service Management:${NC}" echo -e " sudo systemctl status $SERVICE_NAME # Check service status" echo -e " sudo systemctl restart $SERVICE_NAME # Restart service" echo -e " sudo systemctl stop $SERVICE_NAME # Stop service" echo -e " sudo journalctl -u $SERVICE_NAME -f # View live logs" echo -e " sudo journalctl -u $SERVICE_NAME -n 100 # View last 100 log lines" echo "" print_success "Deployment script completed successfully!" exit 0