Files
roa2web-service-auto/scripts/deploy.sh
Marius Mutu 6b13ffa183 Initial commit: ROA2WEB - FastAPI + Vue.js + Telegram Bot
Modern ERP Reports Application with microservices architecture

Tech Stack:
- Backend: FastAPI + python-oracledb (Oracle DB integration)
- Frontend: Vue.js 3 + PrimeVue + Vite
- Telegram Bot: python-telegram-bot + SQLite
- Infrastructure: Shared database pool, JWT authentication, SSH tunnel

Features:
- FastAPI backend with async Oracle connection pool
- Vue.js 3 responsive frontend with PrimeVue components
- Telegram bot alternative interface
- Microservices architecture with shared components
- Complete deployment support (Linux Docker + Windows IIS)
- Comprehensive testing (Playwright E2E + pytest)

Repository Structure:
- reports-app/ - Main application (backend, frontend, telegram-bot)
- shared/ - Shared components (database pool, auth, utils)
- deployment/ - Deployment scripts (Linux & Windows)
- docs/ - Project documentation
- security/ - Security scanning and git hooks
2025-10-25 14:55:08 +03:00

282 lines
7.5 KiB
Bash

#!/bin/bash
# ROA2WEB Production Deployment Script
# Zero-downtime deployment with health checks and rollback capability
set -e
# Configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
BACKUP_DIR="$PROJECT_DIR/backups"
LOG_FILE="$PROJECT_DIR/deploy.log"
MAX_RETRIES=3
HEALTH_CHECK_TIMEOUT=60
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Logging function
log() {
local level=$1
shift
local message="$*"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo -e "[$timestamp] [$level] $message" | tee -a "$LOG_FILE"
}
# Error handling
error_exit() {
log "ERROR" "$1"
exit 1
}
# Success message
success() {
log "SUCCESS" "$1"
}
# Warning message
warning() {
log "WARNING" "$1"
}
# Info message
info() {
log "INFO" "$1"
}
# Check if running as root
check_root() {
if [[ $EUID -eq 0 ]]; then
error_exit "This script should not be run as root for security reasons"
fi
}
# Check prerequisites
check_prerequisites() {
info "Checking prerequisites..."
# Check if Docker is installed and running
if ! command -v docker &> /dev/null; then
error_exit "Docker is not installed"
fi
if ! docker info &> /dev/null; then
error_exit "Docker daemon is not running"
fi
# Check if Docker Compose is installed
if ! command -v docker-compose &> /dev/null; then
error_exit "Docker Compose is not installed"
fi
# Check if required files exist
if [[ ! -f "$PROJECT_DIR/.env.production" ]]; then
error_exit "Production environment file (.env.production) not found"
fi
if [[ ! -f "$PROJECT_DIR/docker-compose.yml" ]]; then
error_exit "Docker Compose file not found"
fi
if [[ ! -f "$PROJECT_DIR/docker-compose.production.yml" ]]; then
error_exit "Production Docker Compose file not found"
fi
success "Prerequisites check passed"
}
# Create backup directory
create_backup_dir() {
if [[ ! -d "$BACKUP_DIR" ]]; then
mkdir -p "$BACKUP_DIR"
info "Created backup directory: $BACKUP_DIR"
fi
}
# Create backup of current deployment
create_backup() {
info "Creating backup of current deployment..."
local backup_name="backup_$(date +%Y%m%d_%H%M%S)"
local backup_path="$BACKUP_DIR/$backup_name"
# Create backup directory
mkdir -p "$backup_path"
# Backup Docker images
info "Backing up Docker images..."
docker save -o "$backup_path/images.tar" \
roa2web/backend:latest \
roa2web/frontend:latest \
roa2web/nginx-gateway:latest 2>/dev/null || warning "Some images may not exist yet"
# Backup configuration files
cp -r "$PROJECT_DIR"/.env* "$backup_path/" 2>/dev/null || true
cp -r "$PROJECT_DIR"/docker-compose*.yml "$backup_path/"
cp -r "$PROJECT_DIR"/nginx/conf "$backup_path/" 2>/dev/null || true
# Backup volumes data
info "Backing up volume data..."
docker run --rm -v roa2web_nginx-logs:/data -v "$backup_path":/backup alpine tar czf /backup/nginx-logs.tar.gz -C /data . 2>/dev/null || warning "Nginx logs backup failed"
docker run --rm -v roa2web_ssl-certs:/data -v "$backup_path":/backup alpine tar czf /backup/ssl-certs.tar.gz -C /data . 2>/dev/null || warning "SSL certs backup failed"
docker run --rm -v roa2web_redis-data:/data -v "$backup_path":/backup alpine tar czf /backup/redis-data.tar.gz -C /data . 2>/dev/null || warning "Redis data backup failed"
echo "$backup_name" > "$PROJECT_DIR/.last_backup"
success "Backup created: $backup_name"
}
# Health check function
health_check() {
local service=$1
local url=$2
local timeout=${3:-30}
info "Performing health check for $service..."
local count=0
while [[ $count -lt $timeout ]]; do
if curl -f -s "$url" > /dev/null 2>&1; then
success "$service health check passed"
return 0
fi
sleep 1
((count++))
done
error_exit "$service health check failed after ${timeout}s"
}
# Wait for services to be healthy
wait_for_services() {
info "Waiting for services to be healthy..."
# Wait for containers to be running
sleep 10
# Check backend health
health_check "Backend API" "http://localhost/api/health" $HEALTH_CHECK_TIMEOUT
# Check frontend health
health_check "Frontend" "http://localhost/health" $HEALTH_CHECK_TIMEOUT
# Check gateway health
health_check "Gateway" "http://localhost/health" $HEALTH_CHECK_TIMEOUT
success "All services are healthy"
}
# Deploy function
deploy() {
info "Starting deployment..."
cd "$PROJECT_DIR"
# Load production environment
set -a
source .env.production
set +a
# Build images
info "Building Docker images..."
docker-compose -f docker-compose.yml -f docker-compose.production.yml build --no-cache
# Pull any updated base images
info "Pulling base images..."
docker-compose -f docker-compose.yml -f docker-compose.production.yml pull
# Deploy with zero downtime
info "Deploying services..."
docker-compose -f docker-compose.yml -f docker-compose.production.yml up -d --force-recreate
# Wait for services to be ready
wait_for_services
# Clean up old images
info "Cleaning up old images..."
docker image prune -f
success "Deployment completed successfully!"
}
# Rollback function
rollback() {
local backup_name=$1
if [[ -z "$backup_name" ]]; then
if [[ -f "$PROJECT_DIR/.last_backup" ]]; then
backup_name=$(cat "$PROJECT_DIR/.last_backup")
else
error_exit "No backup specified and no last backup found"
fi
fi
local backup_path="$BACKUP_DIR/$backup_name"
if [[ ! -d "$backup_path" ]]; then
error_exit "Backup not found: $backup_name"
fi
warning "Rolling back to backup: $backup_name"
# Stop current services
docker-compose -f docker-compose.yml -f docker-compose.production.yml down
# Restore images
if [[ -f "$backup_path/images.tar" ]]; then
docker load -i "$backup_path/images.tar"
fi
# Restore configuration
cp "$backup_path"/.env* "$PROJECT_DIR/"
# Start services
docker-compose -f docker-compose.yml -f docker-compose.production.yml up -d
# Wait for services
wait_for_services
success "Rollback completed successfully!"
}
# Main deployment workflow
main() {
local action=${1:-deploy}
case $action in
"deploy")
info "=== ROA2WEB Production Deployment ==="
check_root
check_prerequisites
create_backup_dir
create_backup
deploy
;;
"rollback")
info "=== ROA2WEB Rollback ==="
check_root
rollback "$2"
;;
"health-check")
info "=== ROA2WEB Health Check ==="
wait_for_services
;;
*)
echo "Usage: $0 {deploy|rollback [backup_name]|health-check}"
echo ""
echo "Commands:"
echo " deploy - Deploy the application to production"
echo " rollback - Rollback to the last backup or specified backup"
echo " health-check - Perform health check on running services"
exit 1
;;
esac
}
# Run main function with all arguments
main "$@"