#!/usr/bin/env bash # پشتیبان‌گیری روزانه — دیتابیس PostgreSQL (همه محتوای سایت) و تصاویر MinIO. # # اجرا: bash scripts/backup.sh # زمان‌بندی: cron (لینوکس) یا Task Scheduler (ویندوز)، مثلا هر شب ساعت ۳: # 0 3 * * * cd /path/to/aramesh-backend && bash scripts/backup.sh >> backups/backup.log 2>&1 # یا سرویس db-backup در پروفایل ops (پشتیبان ساعتی DB بدون cron میزبان). # # نگهداری: فایل‌های قدیمی‌تر از RETENTION_DAYS (پیش‌فرض ۱۴) حذف می‌شوند. # انتشار خارج از سرور: اگر OFFSITE_RCLONE_REMOTE تنظیم شده باشد (در .env، مثل # OFFSITE_RCLONE_REMOTE=storage:aramesh-backups)، بعد از هر پشتیبان rclone copy انجام می‌شود. # تازگی پشتیبان: backups/backup_freshness.prom برای هشدار «پشتیبان کهنه» در Grafana نوشته می‌شود. set -euo pipefail cd "$(dirname "$0")/.." RETENTION_DAYS="${RETENTION_DAYS:-14}" STAMP="$(date +%Y%m%d-%H%M%S)" mkdir -p backups # متغیرهای .env (برای MINIO_ROOT_* و OFFSITE_RCLONE_REMOTE) در دسترس قرار می‌گیرند if [ -f .env ]; then set -a # shellcheck disable=SC1091 . ./.env set +a fi echo "[backup] $STAMP" # ۱) دیتابیس — همه متن‌ها، مقاله‌ها و تنظیمات سایت DB_FILE="backups/db-${STAMP}.sql.gz" # --exit-on-error: اگر pg_dump وسط کار خطا بگیرد، پشتیبان ناقص تولید نمی‌شود docker compose exec -T db pg_dump --exit-on-error -U aramesh arameshplaza | gzip > "$DB_FILE" # پشتیبان خالی/کوچک یعنی خرابی — زودتر بفهمیم تا «روز نیاز» MIN_DB_BYTES=1024 DB_BYTES=$(wc -c < "$DB_FILE" | tr -d ' ') if [ "$DB_BYTES" -lt "$MIN_DB_BYTES" ]; then echo "[backup] ✗ پشتیبان دیتابیس شکسته است (${DB_BYTES} بایت)" >&2 rm -f "$DB_FILE" exit 1 fi sha256sum "$DB_FILE" > "${DB_FILE}.sha256" echo "[backup] db: $DB_FILE ($(du -h "$DB_FILE" | cut -f1))" # ۲) تصاویر MinIO — باکت aramesh-media docker compose run --rm -v "$(pwd)/backups:/backups" --entrypoint sh mc -c " mc alias set local http://minio:9000 \"\$MINIO_ROOT_USER\" \"\$MINIO_ROOT_PASSWORD\" >/dev/null && mc mirror --overwrite local/aramesh-media /backups/images-${STAMP} " echo "[backup] images: backups/images-${STAMP}" # ۳) انتشار خارج از سرور (اختیاری) — هر شب کپی به حافظهٔ دیگری if [ -n "${OFFSITE_RCLONE_REMOTE:-}" ]; then if command -v rclone >/dev/null 2>&1; then rclone copy backups "${OFFSITE_RCLONE_REMOTE}" --max-age 48h echo "[backup] offsite: ${OFFSITE_RCLONE_REMOTE}" else echo "[backup] هشدار: OFFSITE_RCLONE_REMOTE تنظیم است اما rclone نصب نیست" >&2 fi fi # ۴) پاک‌سازی نسخه‌های قدیمی‌تر find backups -name 'db-*.sql.gz' -mtime "+${RETENTION_DAYS}" -delete find backups -name 'db-*.sql.gz.sha256' -mtime "+${RETENTION_DAYS}" -delete find backups -maxdepth 1 -type d -name 'images-*' -mtime "+${RETENTION_DAYS}" -exec rm -rf {} + echo "[backup] پاک‌سازی نسخه‌های قدیمی‌تر از ${RETENTION_DAYS} روز انجام شد" # ۵) سنجهٔ تازگی پشتیبان — Prometheus آن را می‌خواند؛ کهنه‌شدن یعنی کرون خفتی است printf 'backup_success_time %s\n' "$(date +%s)" > backups/backup_freshness.prom echo "[backup] ✔ تمام شد"