Files
nas-tools/scripts/cloud-backup.sh
T
Gan, Jimmy dd64420de1
Run Tests / Secret Detection (pull_request) Failing after 6m45s
Run Tests / Backend Tests (pull_request) Failing after 3m2s
Run Tests / Frontend Tests (pull_request) Failing after 30s
feat: Phase 13 — off-site backup with rclone + crypt encryption
Adds cloud sync extension to the daily backup script using rclone
with client-side AES-256 encryption. Includes interactive setup
script for configuring OneDrive, Google Drive, or any rclone provider.

- scripts/setup-rclone.sh: interactive NAS rclone installer + config
- scripts/cloud-backup.sh: sourced by backup.sh when CLOUD_ENABLED=true
- backup.sh: cloud sync step runs after local backup

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-06 01:21:41 +08:00

80 lines
2.6 KiB
Bash
Executable File

#!/bin/bash
# Off-site Backup — rclone cloud sync extension for backup.sh
#
# This script is sourced by backup.sh when CLOUD_ENABLED=true.
# It syncs local backups to a cloud provider via rclone with encryption.
#
# Prerequisites (handled by setup-rclone.sh):
# 1. rclone installed (Docker image: rclone/rclone)
# 2. Cloud remote configured (e.g., `onedrive`, `gdrive`)
# 3. Crypt remote configured (e.g., `nas-backup-crypt`)
#
# Env vars (set in .env or exported before running backup.sh):
# CLOUD_ENABLED=false # Set to true to enable cloud sync
# RCLONE_REMOTE=nas-backup # Base rclone remote name
# RCLONE_CRYPT_REMOTE="" # If set, uses encrypted remote (e.g., nas-backup-crypt:)
# CLOUD_RETENTION_DAYS=30 # Keep cloud copies for this many days
# RCLONE_EXTRA_FLAGS="" # Extra flags for rclone sync (e.g., "--bwlimit 2M")
CLOUD_ENABLED="${CLOUD_ENABLED:-false}"
RCLONE_REMOTE="${RCLONE_REMOTE:-nas-backup}"
RCLONE_CRYPT_REMOTE="${RCLONE_CRYPT_REMOTE:-}"
CLOUD_RETENTION_DAYS="${CLOUD_RETENTION_DAYS:-30}"
CLOUD_ERRORS=0
# Resolve the target remote (plain or crypt)
if [ -n "$RCLONE_CRYPT_REMOTE" ]; then
_cloud_target="${RCLONE_CRYPT_REMOTE}"
else
_cloud_target="${RCLONE_REMOTE}:nas-tools"
fi
_rclone() {
docker run --rm \
-v "$RCLONE_CONFIG_DIR:/config/rclone" \
-v "$BACKUP_DIR:/data:ro" \
rclone/rclone:latest \
"$@" --config /config/rclone/rclone.conf
}
cloud_backup() {
log "=== Cloud backup started ==="
# 1. Sync backup files to cloud
log "Syncing $BACKUP_DIR to $_cloud_target ..."
if _rclone sync /data/ "$_cloud_target" \
--verbose \
--progress \
--copy-links \
$RCLONE_EXTRA_FLAGS; then
log "Cloud sync OK"
else
log "FAILED: cloud sync"
CLOUD_ERRORS=$((CLOUD_ERRORS + 1))
fi
# 2. Cloud retention — delete files older than CLOUD_RETENTION_DAYS
log "Applying cloud retention: ${CLOUD_RETENTION_DAYS} days ..."
if _rclone delete "$_cloud_target" \
--min-age "${CLOUD_RETENTION_DAYS}d" \
--verbose; then
log "Cloud retention OK"
else
log "FAILED: cloud retention cleanup"
CLOUD_ERRORS=$((CLOUD_ERRORS + 1))
fi
# 3. Show cloud usage summary
log "Cloud storage summary:"
_rclone size "$_cloud_target" --json 2>/dev/null | python3 -c "
import json, sys
try:
d = json.load(sys.stdin)
print(f' {d.get(\"count\",0)} files, {d.get(\"bytes\",0)/1024/1024:.1f} MB')
except: pass
" || true
log "=== Cloud backup finished ($CLOUD_ERRORS errors) ==="
return $CLOUD_ERRORS
}