#!/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