#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" ENV_FILE="${1:-$ROOT_DIR/.env.production}" BACKUP_ROOT="${2:-$ROOT_DIR/backups}" KEEP_COUNT="${3:-14}" LOCK_FILE="$BACKUP_ROOT/.backup.lock" OPS_NOTIFY_ON_SUCCESS="${OPS_NOTIFY_ON_SUCCESS:-0}" OPS_ALERT_WEBHOOK_URL="${OPS_ALERT_WEBHOOK_URL:-}" OPS_ALERT_TIMEOUT_SECONDS="${OPS_ALERT_TIMEOUT_SECONDS:-10}" OPS_ALERT_WEBHOOK_BEARER_TOKEN="${OPS_ALERT_WEBHOOK_BEARER_TOKEN:-}" started_at_utc="$(date -u +%Y-%m-%dT%H:%M:%SZ)" latest_backup="" if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then cat <<'USAGE' Usage: scripts/ops/backup-job.sh [env-file] [backup-root] [keep-count] Examples: scripts/ops/backup-job.sh scripts/ops/backup-job.sh .env.production /var/backups/productier 30 Behavior: - Acquires a non-blocking lock to avoid overlapping runs. - Executes production backup. - Verifies resulting backup integrity. - Prunes old backups using keep-count retention. - Sends webhook alerts when OPS_ALERT_WEBHOOK_URL is configured. USAGE exit 0 fi if ! [[ "$KEEP_COUNT" =~ ^[0-9]+$ ]] || [[ "$KEEP_COUNT" -lt 1 ]]; then echo "[error] keep-count must be a positive integer" >&2 exit 1 fi json_escape() { local value="$1" value="${value//\\/\\\\}" value="${value//\"/\\\"}" value="${value//$'\n'/\\n}" value="${value//$'\r'/\\r}" printf '%s' "$value" } send_alert() { local status="$1" local message="$2" if [[ -z "$OPS_ALERT_WEBHOOK_URL" ]]; then return fi if ! command -v curl >/dev/null 2>&1; then echo "[warn] OPS_ALERT_WEBHOOK_URL is set but curl is unavailable; skipping alert" return fi local payload payload="$(cat </dev/null; then echo "[warn] failed to deliver backup alert webhook" fi } on_error() { local exit_code="$1" send_alert "failure" "backup job failed with exit code ${exit_code}" } trap 'on_error $?' ERR mkdir -p "$BACKUP_ROOT" if command -v flock >/dev/null 2>&1; then exec 9>"$LOCK_FILE" if ! flock -n 9; then echo "[error] backup job already running (lock file: $LOCK_FILE)" >&2 exit 1 fi else echo "[warn] flock not found; running without lock protection" fi echo "[info] starting backup job (env=$ENV_FILE root=$BACKUP_ROOT keep=$KEEP_COUNT)" bash "$ROOT_DIR/scripts/ops/backup-prod.sh" "$ENV_FILE" "$BACKUP_ROOT" latest_backup="$(find "$BACKUP_ROOT" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' \ | grep -E '^[0-9]{8}T[0-9]{6}Z$' \ | sort \ | tail -n 1)" if [[ -z "$latest_backup" ]]; then echo "[error] no backup directory found after backup execution" >&2 exit 1 fi bash "$ROOT_DIR/scripts/ops/verify-backup.sh" "$BACKUP_ROOT/$latest_backup" bash "$ROOT_DIR/scripts/ops/prune-backups.sh" "$BACKUP_ROOT" "$KEEP_COUNT" echo "[ok] backup job completed" if [[ "$OPS_NOTIFY_ON_SUCCESS" == "1" || "$OPS_NOTIFY_ON_SUCCESS" == "true" ]]; then send_alert "success" "backup job completed successfully" fi