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:
390
scripts/backup.sh
Normal file
390
scripts/backup.sh
Normal file
@@ -0,0 +1,390 @@
|
||||
#!/bin/bash
|
||||
# ROA2WEB Database and Application Backup Script
|
||||
# Creates backups of Oracle database, Docker volumes, and configuration
|
||||
|
||||
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/backup.log"
|
||||
RETENTION_DAYS=30
|
||||
MAX_BACKUPS=10
|
||||
|
||||
# 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"
|
||||
}
|
||||
|
||||
# Info message
|
||||
info() {
|
||||
log "INFO" "$1"
|
||||
}
|
||||
|
||||
# Warning message
|
||||
warning() {
|
||||
log "WARNING" "$1"
|
||||
}
|
||||
|
||||
# Load environment variables
|
||||
load_env() {
|
||||
if [[ -f "$PROJECT_DIR/.env" ]]; then
|
||||
set -a
|
||||
source "$PROJECT_DIR/.env"
|
||||
set +a
|
||||
elif [[ -f "$PROJECT_DIR/.env.production" ]]; then
|
||||
set -a
|
||||
source "$PROJECT_DIR/.env.production"
|
||||
set +a
|
||||
else
|
||||
error_exit "No environment file found"
|
||||
fi
|
||||
}
|
||||
|
||||
# Create backup directory structure
|
||||
create_backup_structure() {
|
||||
local backup_name=$1
|
||||
local backup_path="$BACKUP_DIR/$backup_name"
|
||||
|
||||
mkdir -p "$backup_path"/{database,volumes,config,logs}
|
||||
echo "$backup_path"
|
||||
}
|
||||
|
||||
# Backup Oracle database
|
||||
backup_database() {
|
||||
local backup_path=$1
|
||||
|
||||
info "Starting Oracle database backup..."
|
||||
|
||||
# Check if SSH tunnel is required
|
||||
if [[ "$ORACLE_HOST" == "localhost" && -f "$PROJECT_DIR/ssh_tunnel.sh" ]]; then
|
||||
info "Ensuring SSH tunnel is running..."
|
||||
"$PROJECT_DIR/ssh_tunnel.sh" status || "$PROJECT_DIR/ssh_tunnel.sh" start
|
||||
fi
|
||||
|
||||
# Create database backup using Oracle export
|
||||
local db_backup_file="$backup_path/database/roa_backup_$(date +%Y%m%d_%H%M%S).dmp"
|
||||
local log_file="$backup_path/database/export.log"
|
||||
|
||||
# Export specific schemas (adjust as needed)
|
||||
if command -v expdp &> /dev/null; then
|
||||
info "Using Oracle Data Pump for backup..."
|
||||
expdp \
|
||||
userid="$ORACLE_USER/$ORACLE_PASSWORD@$ORACLE_HOST:$ORACLE_PORT/$ORACLE_SID" \
|
||||
schemas="$ORACLE_USER" \
|
||||
dumpfile="$(basename "$db_backup_file")" \
|
||||
logfile="$(basename "$log_file")" \
|
||||
directory=DATA_PUMP_DIR \
|
||||
compression=ALL &> /dev/null || warning "Data Pump backup failed, trying alternative method"
|
||||
fi
|
||||
|
||||
# Alternative: SQL backup for essential data
|
||||
if [[ ! -f "$db_backup_file" ]]; then
|
||||
info "Creating SQL backup of essential tables..."
|
||||
cat > "$backup_path/database/backup.sql" << EOF
|
||||
-- ROA2WEB Database Backup $(date)
|
||||
-- Essential tables backup
|
||||
|
||||
-- Users table
|
||||
CREATE TABLE users_backup AS SELECT * FROM users;
|
||||
|
||||
-- Companies table
|
||||
CREATE TABLE companies_backup AS SELECT * FROM companies;
|
||||
|
||||
-- Invoices table
|
||||
CREATE TABLE invoices_backup AS SELECT * FROM invoices;
|
||||
|
||||
-- Payments table
|
||||
CREATE TABLE payments_backup AS SELECT * FROM payments;
|
||||
|
||||
-- Authentication tokens (structure only for security)
|
||||
CREATE TABLE auth_tokens_backup AS SELECT user_id, created_at, expires_at FROM auth_tokens WHERE 1=0;
|
||||
|
||||
COMMIT;
|
||||
EOF
|
||||
|
||||
# Execute SQL backup if sqlplus is available
|
||||
if command -v sqlplus &> /dev/null; then
|
||||
sqlplus -s "$ORACLE_USER/$ORACLE_PASSWORD@$ORACLE_HOST:$ORACLE_PORT/$ORACLE_SID" @"$backup_path/database/backup.sql" > "$backup_path/database/backup_output.log" 2>&1 || warning "SQL backup failed"
|
||||
fi
|
||||
fi
|
||||
|
||||
success "Database backup completed"
|
||||
}
|
||||
|
||||
# Backup Docker volumes
|
||||
backup_volumes() {
|
||||
local backup_path=$1
|
||||
|
||||
info "Backing up Docker volumes..."
|
||||
|
||||
# Backup nginx logs
|
||||
if docker volume ls | grep -q "roa2web_nginx-logs"; then
|
||||
info "Backing up nginx logs..."
|
||||
docker run --rm \
|
||||
-v roa2web_nginx-logs:/data:ro \
|
||||
-v "$backup_path/volumes":/backup \
|
||||
alpine tar czf /backup/nginx-logs.tar.gz -C /data . 2>/dev/null || warning "Nginx logs backup failed"
|
||||
fi
|
||||
|
||||
# Backup SSL certificates
|
||||
if docker volume ls | grep -q "roa2web_ssl-certs"; then
|
||||
info "Backing up SSL certificates..."
|
||||
docker run --rm \
|
||||
-v roa2web_ssl-certs:/data:ro \
|
||||
-v "$backup_path/volumes":/backup \
|
||||
alpine tar czf /backup/ssl-certs.tar.gz -C /data . 2>/dev/null || warning "SSL certs backup failed"
|
||||
fi
|
||||
|
||||
# Backup Redis data
|
||||
if docker volume ls | grep -q "roa2web_redis-data"; then
|
||||
info "Backing up Redis data..."
|
||||
docker run --rm \
|
||||
-v roa2web_redis-data:/data:ro \
|
||||
-v "$backup_path/volumes":/backup \
|
||||
alpine tar czf /backup/redis-data.tar.gz -C /data . 2>/dev/null || warning "Redis data backup failed"
|
||||
fi
|
||||
|
||||
# Backup backend logs
|
||||
if docker volume ls | grep -q "roa2web_backend-logs"; then
|
||||
info "Backing up backend logs..."
|
||||
docker run --rm \
|
||||
-v roa2web_backend-logs:/data:ro \
|
||||
-v "$backup_path/volumes":/backup \
|
||||
alpine tar czf /backup/backend-logs.tar.gz -C /data . 2>/dev/null || warning "Backend logs backup failed"
|
||||
fi
|
||||
|
||||
success "Docker volumes backup completed"
|
||||
}
|
||||
|
||||
# Backup configuration files
|
||||
backup_config() {
|
||||
local backup_path=$1
|
||||
|
||||
info "Backing up configuration files..."
|
||||
|
||||
# Copy environment files
|
||||
cp -r "$PROJECT_DIR"/.env* "$backup_path/config/" 2>/dev/null || true
|
||||
|
||||
# Copy Docker Compose files
|
||||
cp "$PROJECT_DIR"/docker-compose*.yml "$backup_path/config/"
|
||||
|
||||
# Copy nginx configuration
|
||||
if [[ -d "$PROJECT_DIR/nginx/conf" ]]; then
|
||||
cp -r "$PROJECT_DIR/nginx/conf" "$backup_path/config/"
|
||||
fi
|
||||
|
||||
# Copy SSL configuration (if exists)
|
||||
if [[ -d "$PROJECT_DIR/nginx/ssl" ]]; then
|
||||
cp -r "$PROJECT_DIR/nginx/ssl" "$backup_path/config/" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Copy deployment scripts
|
||||
if [[ -d "$PROJECT_DIR/scripts" ]]; then
|
||||
cp -r "$PROJECT_DIR/scripts" "$backup_path/config/"
|
||||
fi
|
||||
|
||||
# Create backup metadata
|
||||
cat > "$backup_path/backup_info.txt" << EOF
|
||||
ROA2WEB Backup Information
|
||||
=========================
|
||||
Backup Date: $(date)
|
||||
Backup Type: Full Backup
|
||||
Environment: ${ENVIRONMENT:-unknown}
|
||||
Oracle Host: ${ORACLE_HOST:-unknown}
|
||||
Oracle User: ${ORACLE_USER:-unknown}
|
||||
Git Commit: $(git rev-parse HEAD 2>/dev/null || echo "unknown")
|
||||
Git Branch: $(git branch --show-current 2>/dev/null || echo "unknown")
|
||||
Docker Images:
|
||||
$(docker images --format "table {{.Repository}}:{{.Tag}}\t{{.CreatedAt}}\t{{.Size}}" | grep roa2web || echo "No ROA2WEB images found")
|
||||
EOF
|
||||
|
||||
success "Configuration backup completed"
|
||||
}
|
||||
|
||||
# Clean old backups
|
||||
cleanup_old_backups() {
|
||||
info "Cleaning up old backups..."
|
||||
|
||||
# Remove backups older than retention period
|
||||
find "$BACKUP_DIR" -name "backup_*" -type d -mtime +$RETENTION_DAYS -exec rm -rf {} \; 2>/dev/null || true
|
||||
|
||||
# Keep only the latest MAX_BACKUPS
|
||||
local backup_count=$(find "$BACKUP_DIR" -name "backup_*" -type d | wc -l)
|
||||
if [[ $backup_count -gt $MAX_BACKUPS ]]; then
|
||||
local excess=$((backup_count - MAX_BACKUPS))
|
||||
find "$BACKUP_DIR" -name "backup_*" -type d -printf '%T+ %p\n' | sort | head -n $excess | cut -d' ' -f2- | xargs rm -rf
|
||||
info "Removed $excess old backups"
|
||||
fi
|
||||
|
||||
success "Backup cleanup completed"
|
||||
}
|
||||
|
||||
# List available backups
|
||||
list_backups() {
|
||||
info "Available backups:"
|
||||
echo ""
|
||||
|
||||
if [[ ! -d "$BACKUP_DIR" ]] || [[ -z "$(ls -A "$BACKUP_DIR" 2>/dev/null)" ]]; then
|
||||
warning "No backups found"
|
||||
return
|
||||
fi
|
||||
|
||||
find "$BACKUP_DIR" -name "backup_*" -type d -printf '%T+ %p\n' | sort -r | while read -r line; do
|
||||
local date_part=$(echo "$line" | cut -d' ' -f1 | cut -d+ -f1)
|
||||
local backup_name=$(basename "$(echo "$line" | cut -d' ' -f2)")
|
||||
local backup_path="$(echo "$line" | cut -d' ' -f2)"
|
||||
|
||||
echo "📦 $backup_name ($date_part)"
|
||||
|
||||
if [[ -f "$backup_path/backup_info.txt" ]]; then
|
||||
grep -E "(Backup Date|Environment|Git)" "$backup_path/backup_info.txt" | sed 's/^/ /'
|
||||
fi
|
||||
echo ""
|
||||
done
|
||||
}
|
||||
|
||||
# Restore from backup
|
||||
restore_backup() {
|
||||
local backup_name=$1
|
||||
|
||||
if [[ -z "$backup_name" ]]; then
|
||||
error_exit "Backup name is required for restore operation"
|
||||
fi
|
||||
|
||||
local backup_path="$BACKUP_DIR/$backup_name"
|
||||
|
||||
if [[ ! -d "$backup_path" ]]; then
|
||||
error_exit "Backup not found: $backup_name"
|
||||
fi
|
||||
|
||||
warning "Restoring from backup: $backup_name"
|
||||
warning "This will overwrite current data. Press Ctrl+C to cancel or wait 10 seconds to continue..."
|
||||
sleep 10
|
||||
|
||||
info "Starting restore process..."
|
||||
|
||||
# Stop services
|
||||
docker-compose -f "$PROJECT_DIR/docker-compose.yml" -f "$PROJECT_DIR/docker-compose.production.yml" down 2>/dev/null || true
|
||||
|
||||
# Restore configuration
|
||||
if [[ -d "$backup_path/config" ]]; then
|
||||
info "Restoring configuration files..."
|
||||
cp -r "$backup_path/config"/.env* "$PROJECT_DIR/" 2>/dev/null || true
|
||||
cp -r "$backup_path/config"/docker-compose*.yml "$PROJECT_DIR/"
|
||||
|
||||
if [[ -d "$backup_path/config/conf" ]]; then
|
||||
mkdir -p "$PROJECT_DIR/nginx"
|
||||
cp -r "$backup_path/config/conf" "$PROJECT_DIR/nginx/"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Restore volumes
|
||||
if [[ -d "$backup_path/volumes" ]]; then
|
||||
info "Restoring Docker volumes..."
|
||||
|
||||
for volume_backup in "$backup_path/volumes"/*.tar.gz; do
|
||||
if [[ -f "$volume_backup" ]]; then
|
||||
local volume_name=$(basename "$volume_backup" .tar.gz)
|
||||
docker volume create "roa2web_$volume_name" 2>/dev/null || true
|
||||
docker run --rm \
|
||||
-v "roa2web_$volume_name":/data \
|
||||
-v "$backup_path/volumes":/backup \
|
||||
alpine tar xzf "/backup/$(basename "$volume_backup")" -C /data
|
||||
info "Restored volume: $volume_name"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
success "Restore completed. Please restart services manually."
|
||||
}
|
||||
|
||||
# Main function
|
||||
main() {
|
||||
local action=${1:-backup}
|
||||
|
||||
case $action in
|
||||
"backup"|"full")
|
||||
info "=== ROA2WEB Full Backup ==="
|
||||
load_env
|
||||
|
||||
local backup_name="backup_$(date +%Y%m%d_%H%M%S)"
|
||||
local backup_path=$(create_backup_structure "$backup_name")
|
||||
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
backup_database "$backup_path"
|
||||
backup_volumes "$backup_path"
|
||||
backup_config "$backup_path"
|
||||
cleanup_old_backups
|
||||
|
||||
success "Full backup completed: $backup_name"
|
||||
;;
|
||||
"database"|"db")
|
||||
info "=== ROA2WEB Database Backup ==="
|
||||
load_env
|
||||
|
||||
local backup_name="db_backup_$(date +%Y%m%d_%H%M%S)"
|
||||
local backup_path=$(create_backup_structure "$backup_name")
|
||||
|
||||
backup_database "$backup_path"
|
||||
success "Database backup completed: $backup_name"
|
||||
;;
|
||||
"volumes")
|
||||
info "=== ROA2WEB Volumes Backup ==="
|
||||
|
||||
local backup_name="volumes_backup_$(date +%Y%m%d_%H%M%S)"
|
||||
local backup_path=$(create_backup_structure "$backup_name")
|
||||
|
||||
backup_volumes "$backup_path"
|
||||
success "Volumes backup completed: $backup_name"
|
||||
;;
|
||||
"list")
|
||||
list_backups
|
||||
;;
|
||||
"restore")
|
||||
restore_backup "$2"
|
||||
;;
|
||||
"cleanup")
|
||||
cleanup_old_backups
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {backup|database|volumes|list|restore <backup_name>|cleanup}"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " backup|full - Create full backup (database + volumes + config)"
|
||||
echo " database|db - Backup only Oracle database"
|
||||
echo " volumes - Backup only Docker volumes"
|
||||
echo " list - List available backups"
|
||||
echo " restore - Restore from backup"
|
||||
echo " cleanup - Clean up old backups"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user