- Docker container with rmapi cloud backup, PDF conversion pipeline - Web UI: file browser, multi-select, delete/move, thumbnail preview - Sync: backup from reMarkable cloud, rmdoc→PDF conversion via rmc+Inkscape - Excluded-files mechanism to prevent deleted items from returning after sync Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
114 lines
3.4 KiB
Bash
Executable File
114 lines
3.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# reMarkable Backup (offizielle Cloud via rmapi)
|
|
#
|
|
# Ziel:
|
|
# - Inkrementeller Download in "current/".
|
|
# - Datierte Snapshots in "snapshots/YYYY-MM-DD_HHMMSS/".
|
|
# - Versionierung via Hardlinks (unveraenderte Dateien belegen kaum Zusatzplatz).
|
|
#
|
|
# Nutzung:
|
|
# ./backup-official-cloud.sh /www/remarkable/notizen /
|
|
#
|
|
set -euo pipefail
|
|
|
|
BASE_DIR="${1:-/www/remarkable/notizen}"
|
|
REMOTE_DIR="${2:-/}"
|
|
TIMESTAMP="$(date +%F_%H%M%S)"
|
|
|
|
CURRENT_DIR="${BASE_DIR}/current"
|
|
SNAPSHOT_ROOT="${BASE_DIR}/snapshots"
|
|
SNAPSHOT_DIR="${SNAPSHOT_ROOT}/${TIMESTAMP}"
|
|
LATEST_LINK="${SNAPSHOT_ROOT}/latest"
|
|
LOG_DIR="${BASE_DIR}/logs"
|
|
LOCK_FILE="${BASE_DIR}/.backup.lock"
|
|
|
|
# Optional: Pfad, der ein echter Mountpoint sein muss (z. B. /www/remarkable/notizen).
|
|
# Wenn gesetzt und kein Mountpoint, wird abgebrochen (sicher gegen "auf Root-FS schreiben").
|
|
MOUNT_CHECK_PATH="${MOUNT_CHECK_PATH:-}"
|
|
|
|
# Optional: Anzahl alter Snapshots behalten (0 = unbegrenzt).
|
|
SNAPSHOT_KEEP="${SNAPSHOT_KEEP:-30}"
|
|
|
|
mkdir -p "${CURRENT_DIR}" "${SNAPSHOT_ROOT}" "${LOG_DIR}"
|
|
|
|
if [[ -n "${MOUNT_CHECK_PATH}" ]] && ! mountpoint -q "${MOUNT_CHECK_PATH}"; then
|
|
echo "Fehler: ${MOUNT_CHECK_PATH} ist nicht gemountet. Backup abgebrochen."
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v rmapi >/dev/null 2>&1; then
|
|
echo "Fehler: rmapi nicht im PATH."
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v rsync >/dev/null 2>&1; then
|
|
echo "Fehler: rsync fehlt. Bitte installieren."
|
|
exit 1
|
|
fi
|
|
|
|
exec 9>"${LOCK_FILE}"
|
|
if ! flock -n 9; then
|
|
echo "Backup laeuft bereits (Lock: ${LOCK_FILE})."
|
|
exit 1
|
|
fi
|
|
|
|
LOG_FILE="${LOG_DIR}/backup-${TIMESTAMP}.log"
|
|
exec > >(tee -a "${LOG_FILE}") 2>&1
|
|
|
|
echo "== reMarkable Backup Start =="
|
|
echo "Base: ${BASE_DIR}"
|
|
echo "Remote: ${REMOTE_DIR}"
|
|
echo "Zeit: ${TIMESTAMP}"
|
|
|
|
MGET_FLAGS=(-i)
|
|
if [[ "${BACKUP_FULL:-}" == "1" ]]; then
|
|
MGET_FLAGS=()
|
|
fi
|
|
if [[ "${BACKUP_MIRROR_DELETE:-}" == "1" ]]; then
|
|
MGET_FLAGS+=(-d)
|
|
fi
|
|
|
|
echo "1) Aktualisiere current/ via rmapi mget ${MGET_FLAGS[*]:-(none)} ..."
|
|
rmapi mget "${MGET_FLAGS[@]}" -o "${CURRENT_DIR}" "${REMOTE_DIR}"
|
|
|
|
# Leere Ordner anlegen (rmapi mget erstellt keine leeren Verzeichnisse)
|
|
echo "1b) Lege fehlende Ordner an ..."
|
|
rmapi find "${REMOTE_DIR}" 2>/dev/null | grep '^\[d\]' | sed 's/^\[d\] *//' | while read -r DIR; do
|
|
LOCAL_DIR="${CURRENT_DIR}/${DIR}"
|
|
mkdir -p "${LOCAL_DIR}"
|
|
done
|
|
|
|
echo "2) Erzeuge Snapshot: ${SNAPSHOT_DIR}"
|
|
mkdir -p "${SNAPSHOT_DIR}"
|
|
|
|
if [[ -L "${LATEST_LINK}" ]] && [[ -d "$(readlink -f "${LATEST_LINK}")" ]]; then
|
|
PREV_SNAPSHOT="$(readlink -f "${LATEST_LINK}")"
|
|
rsync -a --delete --link-dest="${PREV_SNAPSHOT}" "${CURRENT_DIR}/" "${SNAPSHOT_DIR}/"
|
|
else
|
|
rsync -a --delete "${CURRENT_DIR}/" "${SNAPSHOT_DIR}/"
|
|
fi
|
|
|
|
ln -sfn "${SNAPSHOT_DIR}" "${LATEST_LINK}"
|
|
|
|
if [[ "${SNAPSHOT_KEEP}" =~ ^[0-9]+$ ]] && (( SNAPSHOT_KEEP > 0 )); then
|
|
echo "3) Pruefe Retention (keep=${SNAPSHOT_KEEP}) ..."
|
|
mapfile -t SNAPSHOTS < <(ls -1 "${SNAPSHOT_ROOT}" | grep -E '^[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{6}$' | sort)
|
|
if (( ${#SNAPSHOTS[@]} > SNAPSHOT_KEEP )); then
|
|
DELETE_COUNT=$(( ${#SNAPSHOTS[@]} - SNAPSHOT_KEEP ))
|
|
for OLD in "${SNAPSHOTS[@]:0:DELETE_COUNT}"; do
|
|
rm -rf "${SNAPSHOT_ROOT}/${OLD}"
|
|
echo " entfernt: ${SNAPSHOT_ROOT}/${OLD}"
|
|
done
|
|
fi
|
|
fi
|
|
|
|
# PDF-Konvertierung
|
|
if command -v remarkable-convert.sh >/dev/null 2>&1; then
|
|
echo "4) Konvertiere .rmdoc zu PDF ..."
|
|
remarkable-convert.sh "${CURRENT_DIR}"
|
|
fi
|
|
|
|
echo "== Backup fertig =="
|
|
echo "Current: ${CURRENT_DIR}"
|
|
echo "Snapshot: ${SNAPSHOT_DIR}"
|