#!/usr/bin/env bash set -Eeuo pipefail APP_DIR="${APP_DIR:-/opt/proxpanel}" ENV_FILE="${ENV_FILE:-$APP_DIR/.env.production}" SECRET_FILE="${SECRET_FILE:-$APP_DIR/.backup.env}" BACKUP_ROOT="${BACKUP_ROOT:-/opt/proxpanel-backups/daily}" RETENTION_DAYS="${RETENTION_DAYS:-14}" log() { printf '[%s] %s\n' "$(date -u +'%Y-%m-%d %H:%M:%S UTC')" "$*" } die() { printf '[ERROR] %s\n' "$*" >&2 exit 1 } require_file() { [[ -f "$1" ]] || die "Missing required file: $1" } require_command() { command -v "$1" >/dev/null 2>&1 || die "Missing required command: $1" } main() { require_command docker require_command openssl require_command sha256sum require_file "$ENV_FILE" require_file "$SECRET_FILE" # shellcheck disable=SC1090 source "$ENV_FILE" # shellcheck disable=SC1090 source "$SECRET_FILE" [[ -n "${BACKUP_ENCRYPTION_KEY:-}" ]] || die "BACKUP_ENCRYPTION_KEY is empty in $SECRET_FILE" export BACKUP_ENCRYPTION_KEY [[ -n "${POSTGRES_USER:-}" ]] || die "POSTGRES_USER missing in $ENV_FILE" [[ -n "${POSTGRES_DB:-}" ]] || die "POSTGRES_DB missing in $ENV_FILE" local ts backup_dir plain_sql encrypted_sql ts="$(date -u +%Y%m%d-%H%M%S)" backup_dir="${BACKUP_ROOT}/${ts}" plain_sql="${backup_dir}/proxpanel.sql" encrypted_sql="${plain_sql}.enc" mkdir -p "$backup_dir" chmod 700 "$backup_dir" log "Creating plaintext dump" docker exec proxpanel-postgres pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" --clean --if-exists >"$plain_sql" log "Encrypting dump" openssl enc -aes-256-cbc -salt -pbkdf2 -iter 200000 \ -in "$plain_sql" \ -out "$encrypted_sql" \ -pass env:BACKUP_ENCRYPTION_KEY log "Writing checksum" sha256sum "$encrypted_sql" >"${encrypted_sql}.sha256" rm -f "$plain_sql" chmod 600 "$encrypted_sql" "${encrypted_sql}.sha256" log "Applying retention policy (${RETENTION_DAYS} days)" find "$BACKUP_ROOT" -mindepth 1 -maxdepth 1 -type d -mtime +"$RETENTION_DAYS" -exec rm -rf {} + log "Encrypted backup created: $encrypted_sql" } main "$@"