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
This commit is contained in:
282
scripts/deploy.sh
Normal file
282
scripts/deploy.sh
Normal file
@@ -0,0 +1,282 @@
|
||||
#!/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 "$@"
|
||||
Reference in New Issue
Block a user