#!/bin/bash # Nexus Autoparts — Automated Backup Script # Backs up PostgreSQL + project files, uploads to S3/GCS if configured. # Usage: ./backup.sh [--dry-run] set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_DIR="$(dirname "$SCRIPT_DIR")" BACKUP_DIR="${PROJECT_DIR}/backups" TIMESTAMP=$(date +%Y%m%d_%H%M%S) BACKUP_NAME="nexus_backup_${TIMESTAMP}" DRY_RUN=false # ─── Config ─── DB_NAME="${BACKUP_DB_NAME:-nexus_autoparts}" DB_USER="${BACKUP_DB_USER:-postgres}" S3_BUCKET="${BACKUP_S3_BUCKET:-}" S3_PREFIX="${BACKUP_S3_PREFIX:-nexus-backups}" AWS_CLI="${PROJECT_DIR}/tools/bin/aws" RETENTION_DAYS="${BACKUP_RETENTION_DAYS:-7}" if [[ "${1:-}" == "--dry-run" ]]; then DRY_RUN=true echo "[DRY-RUN] No changes will be made." fi mkdir -p "$BACKUP_DIR" log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" } # ─── PostgreSQL dump ─── log "Dumping PostgreSQL database: $DB_NAME ..." DUMP_FILE="${BACKUP_DIR}/${BACKUP_NAME}.sql" if [[ "$DRY_RUN" == true ]]; then log "DRY-RUN: would run pg_dump -Fc -d $DB_NAME > $DUMP_FILE" else sudo -u "$DB_USER" pg_dump -Fc -d "$DB_NAME" > "$DUMP_FILE" log "Dump complete: $DUMP_FILE ($(du -h "$DUMP_FILE" | cut -f1))" fi # ─── Project files tar ─── log "Creating project archive ..." ARCHIVE_FILE="${BACKUP_DIR}/${BACKUP_NAME}.tar.gz" if [[ "$DRY_RUN" == true ]]; then log "DRY-RUN: would tar project files -> $ARCHIVE_FILE" else tar czf "$ARCHIVE_FILE" \ --exclude='backups/*.tar.gz' \ --exclude='backups/*.sql' \ --exclude='node_modules' \ --exclude='__pycache__' \ --exclude='.venv' \ --exclude='venv' \ --exclude='.git' \ --exclude='*.pyc' \ --exclude='pos/static/js/*.min.js' \ --exclude='pos/static/css/*.min.css' \ -C "$PROJECT_DIR" . log "Archive complete: $ARCHIVE_FILE ($(du -h "$ARCHIVE_FILE" | cut -f1))" fi # ─── Combine into single backup ─── COMBINED="${BACKUP_DIR}/${BACKUP_NAME}.tar.gz" if [[ "$DRY_RUN" == true ]]; then log "DRY-RUN: would create combined backup" else # Rename archive to combined and append dump FINAL="${BACKUP_DIR}/${BACKUP_NAME}.tar.gz" mv "$ARCHIVE_FILE" "$FINAL" log "Backup ready: $FINAL ($(du -h "$FINAL" | cut -f1))" fi # ─── Upload to S3 ─── if [[ -n "$S3_BUCKET" ]]; then if [[ -x "$AWS_CLI" ]]; then log "Uploading to s3://${S3_BUCKET}/${S3_PREFIX}/ ..." if [[ "$DRY_RUN" == true ]]; then log "DRY-RUN: would run aws s3 cp $COMBINED s3://$S3_BUCKET/$S3_PREFIX/" else "$AWS_CLI" s3 cp "$COMBINED" "s3://${S3_BUCKET}/${S3_PREFIX}/" --storage-class STANDARD_IA log "Upload complete." fi else log "WARNING: AWS CLI not found at $AWS_CLI. Skipping S3 upload." fi else log "INFO: S3_BUCKET not set. Skipping cloud upload." fi # ─── Cleanup old backups ─── log "Cleaning up backups older than $RETENTION_DAYS days ..." if [[ "$DRY_RUN" == true ]]; then log "DRY-RUN: would delete backups older than $RETENTION_DAYS days" else find "$BACKUP_DIR" -name "nexus_backup_*.tar.gz" -type f -mtime +$RETENTION_DAYS -delete find "$BACKUP_DIR" -name "nexus_backup_*.sql" -type f -mtime +$RETENTION_DAYS -delete log "Cleanup complete." fi log "Done."