#!/bin/sh set -eu backup_dir=${BACKUP_DIR:-/backups} ssh_key_dir=/root/.ssh success_marker="$backup_dir/.last-success" ssh_host=${BACKUP_SSH_HOST:-} ssh_user=${BACKUP_SSH_USER:-backup} ssh_port=${BACKUP_SSH_PORT:-22} ssh_dir=${BACKUP_SSH_DIR:-} ssh_opts="-o BatchMode=yes -o PasswordAuthentication=no -o StrictHostKeyChecking=yes" copy_remote() { target=$1 [ -n "$ssh_host" ] || return 0 [ -d "$ssh_key_dir" ] || { printf 'SSH key directory %s not found\n' "$ssh_key_dir" >&2 return 1 } case "$ssh_port" in ''|*[!0-9]*) printf 'BACKUP_SSH_PORT must be numeric\n' >&2; return 1 ;; esac case "$ssh_user" in ''|*[!A-Za-z0-9_.-]*) printf 'BACKUP_SSH_USER contains unsupported characters\n' >&2; return 1 ;; esac case "$ssh_dir" in /|''|*[!A-Za-z0-9_./-]*) printf 'BACKUP_SSH_DIR must be a non-root absolute path without spaces\n' >&2 return 1 ;; /*) ;; *) printf 'BACKUP_SSH_DIR must be an absolute path\n' >&2 return 1 ;; esac filename=$(basename "$target") remote="$ssh_user@$ssh_host" remote_target="$ssh_dir/$filename" remote_temporary="$ssh_dir/.$filename.tmp" ssh $ssh_opts -p "$ssh_port" "$remote" "mkdir -p '$ssh_dir'" >/dev/null scp $ssh_opts -P "$ssh_port" "$target" "$remote:$remote_temporary" ssh $ssh_opts -p "$ssh_port" "$remote" \ "mv '$remote_temporary' '$remote_target' && find '$ssh_dir' -maxdepth 1 -type f -name 'cents-*.dump' -mtime +29 -delete" printf 'Backup copied to %s:%s\n' "$remote" "$remote_target" } run_backup() { umask 077 mkdir -p "$backup_dir/db" timestamp=$(date +%Y%m%d-%H%M%S) target="$backup_dir/db/cents-$timestamp.dump" temporary="$target.tmp" trap 'rm -f "$temporary"' EXIT INT TERM pg_dump --format=custom --file="$temporary" pg_restore --list "$temporary" >/dev/null mv "$temporary" "$target" trap - EXIT INT TERM printf 'Backup created: %s\n' "$target" copy_remote "$target" find "$backup_dir/db" -maxdepth 1 -type f -name 'cents-*.dump' -mtime +29 -delete touch "$success_marker" printf 'Backup completed successfully\n' } check_health() { pgrep crond >/dev/null || return 1 [ -f "$success_marker" ] || return 1 now=$(date +%s) last_success=$(stat -c %Y "$success_marker") age=$((now - last_success)) [ "$age" -ge 0 ] && [ "$age" -le 108000 ] } case "${1:-cron}" in run) run_backup ;; cron) printf '%s\n' \ '0 2 * * * /usr/local/bin/cents-backup run >> /proc/1/fd/1 2>> /proc/1/fd/2' \ > /etc/crontabs/root printf 'Backup scheduler started: daily at 02:00 %s, retaining 30 days\n' "${TZ:-local time}" exec crond -f -l 2 ;; health) check_health ;; *) printf 'Unknown command: %s\n' "$1" >&2 exit 2 ;; esac