108 lines
4.2 KiB
Bash
Executable File
108 lines
4.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -Eeuo pipefail
|
|
umask 027
|
|
|
|
# ==============================================================================
|
|
# app-restore.sh
|
|
# - Restore from a "run folder" (local dir or rclone remote folder)
|
|
# - Applies archives per component (meta/db/wordpress/nextcloud/nextcloud-data/gitea...)
|
|
# ==============================================================================
|
|
|
|
LOG_DIR="/var/log/app-backup"
|
|
mkdir -p "$LOG_DIR"
|
|
ts="$(date '+%Y-%m-%d_%H-%M-%S')"
|
|
LOG_FILE="${LOG_DIR}/app-restore_${ts}.log"
|
|
exec > >(tee -a "$LOG_FILE" | systemd-cat -t app-restore -p info) 2>&1
|
|
|
|
CONFIG_FILE="/etc/app-backup/app-backup.conf"
|
|
if [[ -r "$CONFIG_FILE" ]]; then
|
|
# shellcheck disable=SC1090
|
|
source "$CONFIG_FILE"
|
|
else
|
|
echo "ERROR: Config not found/readable: $CONFIG_FILE"
|
|
exit 2
|
|
fi
|
|
|
|
: "${WORKDIR:=/var/backups/app-backup}"
|
|
: "${RESTORE_ROOT:=${WORKDIR}/restore}"
|
|
: "${ARCHIVE_PREFIX:=appbackup}"
|
|
|
|
: "${RCLONE_BIN:=rclone}"
|
|
: "${RCLONE_REMOTE_BASE:=}"
|
|
|
|
|
|
# --- Remote base resolution & crypt enforcement ---
|
|
# Prefer explicit RCLONE_REMOTE_BASE from config.
|
|
# If only RCLONE_REMOTE is provided (e.g. "OneDriveCrypt:Sicherung"), derive the host folder.
|
|
: "${RCLONE_REMOTE:=}"
|
|
if [[ -z "${RCLONE_REMOTE_BASE}" ]]; then
|
|
if [[ -n "${RCLONE_REMOTE}" ]]; then
|
|
RCLONE_REMOTE_BASE="${RCLONE_REMOTE%/}/JRITServerBackups/$(hostname -s)"
|
|
fi
|
|
fi
|
|
|
|
require_crypt_remote() {
|
|
local base="$1"
|
|
local remote_name="${base%%:*}"
|
|
[[ -n "${remote_name}" && "${remote_name}" != "${base}" ]] || die "RCLONE_REMOTE_BASE must include an rclone remote prefix like 'OneDriveCrypt:...'. Got: ${base}"
|
|
have "${RCLONE_BIN}" || die "rclone missing"
|
|
|
|
# rclone prints full config; we only need the remote block.
|
|
local cfg
|
|
cfg="$("${RCLONE_BIN}" config show "${remote_name}" 2>/dev/null || true)"
|
|
[[ -n "${cfg}" ]] || die "rclone remote '${remote_name}' not found. Check 'rclone listremotes' and your config."
|
|
|
|
local typ
|
|
typ="$(printf "%s\n" "${cfg}" | awk -F' = ' '$1=="type"{print $2; exit}')"
|
|
[[ "${typ}" == "crypt" ]] || die "Remote '${remote_name}' is type='${typ:-?}', not 'crypt'. Refusing to restore from non-crypt remote."
|
|
|
|
# Enforce filename/directory encryption so OneDrive web does not show cleartext names.
|
|
local fe de
|
|
fe="$(printf "%s\n" "${cfg}" | awk -F' = ' '$1=="filename_encryption"{print $2; exit}')"
|
|
de="$(printf "%s\n" "${cfg}" | awk -F' = ' '$1=="directory_name_encryption"{print $2; exit}')"
|
|
[[ "${fe}" == "standard" ]] || die "Remote '${remote_name}': filename_encryption='${fe:-?}' (expected 'standard'). Otherwise filenames can appear in cleartext."
|
|
[[ "${de}" == "true" ]] || die "Remote '${remote_name}': directory_name_encryption='${de:-?}' (expected 'true'). Otherwise directory names can appear in cleartext."
|
|
}
|
|
|
|
: "${DRY_RUN:=false}"
|
|
: "${RESTORE_DB:=true}"
|
|
: "${RESTORE_FILES:=true}"
|
|
: "${RESTORE_STRICT_DELETE:=false}"
|
|
|
|
: "${ENABLE_NEXTCLOUD_MAINTENANCE:=true}"
|
|
: "${NC_OCC_USER:=apache}"
|
|
: "${NC_FILES_SCAN_AFTER_RESTORE:=false}"
|
|
|
|
: "${ENABLE_GITEA_SERVICE_STOP:=true}"
|
|
: "${GITEA_SERVICE_NAME:=gitea}"
|
|
|
|
: "${ENABLE_HTTPD_STOP:=false}"
|
|
: "${HTTPD_SERVICE_NAME:=httpd}"
|
|
: "${ENABLE_PHPFPM_STOP:=false}"
|
|
: "${PHPFPM_SERVICE_NAME:=php-fpm}"
|
|
|
|
die() { echo "ERROR: $*"; exit 1; }
|
|
have() { command -v "$1" >/dev/null 2>&1; }
|
|
|
|
run_cmd() { [[ "${DRY_RUN}" == "true" ]] && echo "[DRY_RUN] $*" || "$@"; }
|
|
|
|
NC_MAINTENANCE_ON=false
|
|
nc_maintenance_off() {
|
|
if [[ "${NC_MAINTENANCE_ON}" == "true" ]]; then
|
|
echo "-- Nextcloud maintenance mode OFF (trap)..."
|
|
run_cmd sudo -u "${NC_OCC_USER}" php "${NC_DIR}/occ" maintenance:mode --off || true
|
|
NC_MAINTENANCE_ON=false
|
|
fi
|
|
}
|
|
|
|
GITEA_WAS_STOPPED=false
|
|
HTTPD_WAS_STOPPED=false
|
|
PHPFPM_WAS_STOPPED=false
|
|
gitea_start() { [[ "${GITEA_WAS_STOPPED}" == "true" ]] && { echo "-- Starting gitea (trap)"; run_cmd systemctl start "${GITEA_SERVICE_NAME}" || true; GITEA_WAS_STOPPED=false; }; }
|
|
httpd_start() { [[ "${HTTPD_WAS_STOPPED}" == "true" ]] && { echo "-- Starting httpd (trap)"; run_cmd systemctl start "${HTTPD_SERVICE_NAME}" || true; HTTPD_WAS_STOPPED=false; }; }
|
|
phpfpm_start(){ [[ "${PHPFPM_WAS_STOPPED}" == "true" ]] && { echo "-- Starting php-fpm (trap)"; run_cmd systemctl start "${PHPFPM_SERVICE_NAME}" || true; PHPFPM_WAS_STOPPED=false; }; }
|
|
|
|
echo "== app-restore done: ${ts} =="
|
|
echo "-- Working dir: ${RUN_DIR}"
|
|
echo "-- Log: ${LOG_FILE}"
|