#!/bin/bash # ============================================ # ASF Traceability Matrix Deployment Script # ============================================ # This script deploys the Traceability Matrix web app # using Docker Compose with Caddy reverse proxy # # Domain: Traceability.nabd-co.com # Features: # - Persistent data storage via Docker volumes # - Automatic data backup before updates # - Multi-service architecture (web, data, email) # ============================================ set -e # 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 APP_NAME="traceability" APP_DIR="/opt/traceability" CADDY_DIR="/root/caddy" BACKUP_DIR="/opt/traceability-backups" VOLUME_NAME="traceability_traceability_data" echo -e "${BLUE}============================================${NC}" echo -e "${BLUE} ASF Traceability Matrix Deployment${NC}" echo -e "${BLUE}============================================${NC}" # Function to print status print_status() { echo -e "${GREEN}[✓]${NC} $1" } print_warning() { echo -e "${YELLOW}[!]${NC} $1" } print_error() { echo -e "${RED}[✗]${NC} $1" } # Check if running as root if [ "$EUID" -ne 0 ]; then print_error "Please run as root (sudo ./deploy.sh)" exit 1 fi # Parse command line arguments BACKUP_ONLY=false RESTORE_BACKUP="" SKIP_BACKUP=false while [[ $# -gt 0 ]]; do case $1 in --backup) BACKUP_ONLY=true shift ;; --restore) RESTORE_BACKUP="$2" shift 2 ;; --skip-backup) SKIP_BACKUP=true shift ;; --help) echo "Usage: $0 [OPTIONS]" echo "" echo "Options:" echo " --backup Create a backup only (no deployment)" echo " --restore FILE Restore from a specific backup file" echo " --skip-backup Skip backup before deployment" echo " --help Show this help message" exit 0 ;; *) print_error "Unknown option: $1" exit 1 ;; esac done # Function to backup data backup_data() { echo "" echo -e "${BLUE}Creating data backup...${NC}" mkdir -p $BACKUP_DIR TIMESTAMP=$(date +%Y%m%d_%H%M%S) BACKUP_FILE="$BACKUP_DIR/backup_$TIMESTAMP.tar.gz" # Check if volume exists and has data if docker volume ls | grep -q "$VOLUME_NAME"; then # Create a temporary container to access the volume docker run --rm -v $VOLUME_NAME:/data -v $BACKUP_DIR:/backup alpine \ tar -czf /backup/backup_$TIMESTAMP.tar.gz -C /data . 2>/dev/null || true if [ -f "$BACKUP_FILE" ]; then BACKUP_SIZE=$(du -h "$BACKUP_FILE" | cut -f1) print_status "Backup created: $BACKUP_FILE ($BACKUP_SIZE)" # Keep only last 10 backups ls -t $BACKUP_DIR/backup_*.tar.gz 2>/dev/null | tail -n +11 | xargs -r rm -- print_status "Cleaned old backups (keeping last 10)" else print_warning "No existing data to backup" fi else print_warning "No existing data volume found - skipping backup" fi } # Function to restore data restore_data() { local backup_file=$1 if [ ! -f "$backup_file" ]; then print_error "Backup file not found: $backup_file" exit 1 fi echo "" echo -e "${BLUE}Restoring data from backup...${NC}" # Ensure volume exists docker volume create $VOLUME_NAME 2>/dev/null || true # Restore data docker run --rm -v $VOLUME_NAME:/data -v $(dirname $backup_file):/backup alpine \ sh -c "rm -rf /data/* && tar -xzf /backup/$(basename $backup_file) -C /data" print_status "Data restored from: $backup_file" } # Handle backup only mode if [ "$BACKUP_ONLY" = true ]; then backup_data echo "" echo -e "${GREEN}Backup complete!${NC}" exit 0 fi # Handle restore mode if [ -n "$RESTORE_BACKUP" ]; then restore_data "$RESTORE_BACKUP" echo "" echo -e "${GREEN}Restore complete! Run deploy.sh again to start services.${NC}" exit 0 fi # Step 1: Create application directory echo "" echo -e "${BLUE}Step 1: Setting up application directory...${NC}" mkdir -p $APP_DIR print_status "Created directory: $APP_DIR" # Step 2: Copy files to application directory (if running from repo) echo "" echo -e "${BLUE}Step 2: Copying application files...${NC}" if [ -f "docker-compose.yml" ]; then cp -r . $APP_DIR/ print_status "Copied application files to $APP_DIR" else print_warning "No local files found. Please ensure files are in $APP_DIR" fi cd $APP_DIR # Step 3: Backup existing data (unless skipped) if [ "$SKIP_BACKUP" = false ]; then backup_data fi # Step 4: Ensure Caddy network exists echo "" echo -e "${BLUE}Step 4: Checking Docker network...${NC}" if ! docker network ls | grep -q "caddy_network"; then print_warning "Caddy network not found. Creating..." docker network create caddy_network print_status "Created caddy_network network" else print_status "Caddy network exists" fi # Step 5: Show Caddy configuration to add echo "" echo -e "${BLUE}Step 5: Caddy configuration...${NC}" # Check if Traceability entry already exists in Caddyfile if grep -q "traceability.nabd-co.com" "$CADDY_DIR/Caddyfile" 2>/dev/null; then print_status "Caddy configuration already exists in Caddyfile" else print_warning "Add these entries to your Caddyfile at $CADDY_DIR/Caddyfile:" echo "" echo -e "${YELLOW}# -------------------------" echo "# Traceability Matrix Proxy" echo "# -------------------------" echo "traceability.nabd-co.com {" echo " reverse_proxy traceability_web:8088" echo " encode gzip" echo "}" echo "" echo "# Traceability Data API" echo "traceability-api.nabd-co.com {" echo " reverse_proxy data_service:3002" echo " encode gzip" echo -e "}${NC}" echo "" fi # Step 6: Build and start the application echo "" echo -e "${BLUE}Step 6: Building and starting application...${NC}" docker compose down --remove-orphans 2>/dev/null || true docker compose build --no-cache docker compose up -d print_status "Application started" # Step 7: Wait for services to be ready echo "" echo -e "${BLUE}Step 7: Waiting for services to initialize...${NC}" sleep 5 # Check data service health for i in {1..10}; do if docker exec data_service wget -q -O - http://localhost:3002/health >/dev/null 2>&1; then print_status "Data service is healthy" break fi if [ $i -eq 10 ]; then print_warning "Data service health check timed out (may still be starting)" fi sleep 2 done # Step 8: Reload Caddy echo "" echo -e "${BLUE}Step 8: Reloading Caddy...${NC}" cd $CADDY_DIR docker compose exec -T caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null || { print_warning "Could not reload Caddy automatically. Restarting container..." docker compose restart caddy 2>/dev/null || docker restart caddy 2>/dev/null || true } print_status "Caddy reloaded" # Step 9: Health check echo "" echo -e "${BLUE}Step 9: Running health check...${NC}" SERVICES=("traceability_web" "data_service" "email_service") ALL_RUNNING=true for service in "${SERVICES[@]}"; do if docker ps | grep -q "$service"; then print_status "$service is running" else print_error "$service failed to start" ALL_RUNNING=false fi done if [ "$ALL_RUNNING" = false ]; then print_error "Some services failed to start. Check logs with: docker logs " exit 1 fi # Final output echo "" echo -e "${GREEN}============================================${NC}" echo -e "${GREEN} Deployment Complete!${NC}" echo -e "${GREEN}============================================${NC}" echo "" echo -e "Application URL: ${BLUE}https://traceability.nabd-co.com${NC}" echo -e "API URL: ${BLUE}https://traceability-api.nabd-co.com${NC}" echo "" echo -e "${YELLOW}Data Persistence:${NC}" echo -e " Data Volume: ${BLUE}$VOLUME_NAME${NC}" echo -e " Backup Dir: ${BLUE}$BACKUP_DIR${NC}" echo "" echo -e "${YELLOW}Useful commands:${NC}" echo -e " View logs: ${BLUE}docker logs -f traceability_web${NC}" echo -e " Data logs: ${BLUE}docker logs -f data_service${NC}" echo -e " Backup data: ${BLUE}$APP_DIR/deploy.sh --backup${NC}" echo -e " Restore data: ${BLUE}$APP_DIR/deploy.sh --restore ${NC}" echo -e " Restart: ${BLUE}docker compose -f $APP_DIR/docker-compose.yml restart${NC}" echo -e " Stop: ${BLUE}docker compose -f $APP_DIR/docker-compose.yml down${NC}" echo -e " Rebuild: ${BLUE}docker compose -f $APP_DIR/docker-compose.yml up -d --build${NC}" echo "" echo -e "${GREEN}Note: Your data is stored in a Docker volume and will persist${NC}" echo -e "${GREEN}across container restarts and redeployments.${NC}" echo ""