#!/bin/bash # ===================================================== # ERP GENERIC - PostgreSQL Full Backup Script # Performs full database backup with multi-schema support # ===================================================== set -euo pipefail # Configuration BACKUP_DIR="/backups/postgres" TIMESTAMP=$(date +"%Y%m%d_%H%M%S") RETENTION_DAYS=7 DB_HOST="${POSTGRES_HOST:-postgres}" DB_PORT="${POSTGRES_PORT:-5432}" DB_NAME="${POSTGRES_DB:-erp_generic}" DB_USER="${POSTGRES_USER:-erp_user}" PGPASSWORD="${POSTGRES_PASSWORD}" export PGPASSWORD # Logging LOG_FILE="/var/log/erp-generic/backup.log" exec > >(tee -a "$LOG_FILE") exec 2>&1 echo "===== PostgreSQL Backup Started at $(date) =====" # Create backup directory if not exists mkdir -p "$BACKUP_DIR" # 1. Full Database Backup echo "1. Creating full database backup..." FULL_BACKUP_FILE="${BACKUP_DIR}/full_${TIMESTAMP}.dump" pg_dump -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -Fc -v -d "$DB_NAME" -f "$FULL_BACKUP_FILE" if [ $? -eq 0 ]; then echo "✓ Full backup created: $FULL_BACKUP_FILE" FILE_SIZE=$(du -h "$FULL_BACKUP_FILE" | cut -f1) echo " Size: $FILE_SIZE" else echo "✗ Full backup failed!" exit 1 fi # 2. Generate MD5 checksum echo "2. Generating checksum..." md5sum "$FULL_BACKUP_FILE" > "${FULL_BACKUP_FILE}.md5" echo "✓ Checksum saved: ${FULL_BACKUP_FILE}.md5" # 3. Per-Schema Backups (Multi-Tenant Isolation) echo "3. Creating per-schema backups..." SCHEMAS=("auth" "core" "financial" "inventory" "purchase" "sales" "analytics" "projects" "system") for schema in "${SCHEMAS[@]}"; do SCHEMA_BACKUP_FILE="${BACKUP_DIR}/${schema}_${TIMESTAMP}.dump" echo " Backing up schema: $schema" pg_dump -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -Fc -n "$schema" -d "$DB_NAME" -f "$SCHEMA_BACKUP_FILE" if [ $? -eq 0 ]; then SCHEMA_SIZE=$(du -h "$SCHEMA_BACKUP_FILE" | cut -f1) echo " ✓ $schema backup created ($SCHEMA_SIZE)" else echo " ✗ $schema backup failed!" fi done # 4. Backup Database Roles and Permissions echo "4. Backing up database roles..." ROLES_BACKUP_FILE="${BACKUP_DIR}/roles_${TIMESTAMP}.sql" pg_dumpall -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" --roles-only -f "$ROLES_BACKUP_FILE" echo "✓ Roles backup created: $ROLES_BACKUP_FILE" # 5. Backup PostgreSQL Configuration echo "5. Backing up PostgreSQL configuration..." CONFIG_BACKUP_DIR="${BACKUP_DIR}/config_${TIMESTAMP}" mkdir -p "$CONFIG_BACKUP_DIR" # Copy config files (if accessible) if [ -f /etc/postgresql/16/main/postgresql.conf ]; then cp /etc/postgresql/16/main/postgresql.conf "$CONFIG_BACKUP_DIR/" cp /etc/postgresql/16/main/pg_hba.conf "$CONFIG_BACKUP_DIR/" echo "✓ Config files backed up" fi # 6. Backup WAL Archive Status echo "6. Recording WAL archive status..." psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -tAc "SELECT * FROM pg_stat_archiver;" > "${BACKUP_DIR}/wal_status_${TIMESTAMP}.txt" # 7. Upload to Cloud Storage (S3) if [ -n "${AWS_S3_BUCKET:-}" ]; then echo "7. Uploading to S3..." aws s3 cp "$FULL_BACKUP_FILE" "s3://${AWS_S3_BUCKET}/postgres/${TIMESTAMP}/" --storage-class STANDARD_IA aws s3 cp "${FULL_BACKUP_FILE}.md5" "s3://${AWS_S3_BUCKET}/postgres/${TIMESTAMP}/" # Upload per-schema backups for schema in "${SCHEMAS[@]}"; do SCHEMA_BACKUP_FILE="${BACKUP_DIR}/${schema}_${TIMESTAMP}.dump" if [ -f "$SCHEMA_BACKUP_FILE" ]; then aws s3 cp "$SCHEMA_BACKUP_FILE" "s3://${AWS_S3_BUCKET}/postgres/${TIMESTAMP}/schemas/" --storage-class STANDARD_IA fi done echo "✓ Backup uploaded to S3" fi # 8. Cleanup Old Backups (Local) echo "8. Cleaning up old backups (older than $RETENTION_DAYS days)..." find "$BACKUP_DIR" -type f -name "*.dump" -mtime +$RETENTION_DAYS -delete find "$BACKUP_DIR" -type f -name "*.sql" -mtime +$RETENTION_DAYS -delete find "$BACKUP_DIR" -type f -name "*.md5" -mtime +$RETENTION_DAYS -delete find "$BACKUP_DIR" -type d -name "config_*" -mtime +$RETENTION_DAYS -exec rm -rf {} + 2>/dev/null || true echo "✓ Old backups cleaned up" # 9. Verify Backup Integrity echo "9. Verifying backup integrity..." md5sum -c "${FULL_BACKUP_FILE}.md5" if [ $? -eq 0 ]; then echo "✓ Backup integrity verified" else echo "✗ Backup integrity check failed!" exit 1 fi # 10. Send Notification echo "10. Sending backup notification..." BACKUP_SIZE=$(du -sh "$BACKUP_DIR" | cut -f1) # Slack notification (optional) if [ -n "${SLACK_WEBHOOK_URL:-}" ]; then curl -X POST "$SLACK_WEBHOOK_URL" \ -H 'Content-Type: application/json' \ -d "{\"text\": \"✅ PostgreSQL backup completed successfully\n• Database: $DB_NAME\n• Size: $FILE_SIZE\n• Timestamp: $TIMESTAMP\n• Total backup dir size: $BACKUP_SIZE\"}" fi echo "===== PostgreSQL Backup Completed at $(date) =====" echo "Total backup size: $BACKUP_SIZE" echo "Backup location: $BACKUP_DIR" # Exit successfully exit 0