Compare commits

...
6 Commits
Author SHA1 Message Date
trnq1ll0 44674bdc38 migrate all backups scripts into new Repo called "Brücke" 2026-11-02 01:21:35 +01:00
Claude 46e13a4dd5 Offsite backup: write one JSON line per run to ~/Library/Logs
Each run (also failed or aborted ones, via the EXIT trap) appends start,
end, exit code, "complete" and the result per share to
bfds-offsite-backup.jsonl so a dashboard can show the last complete run.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 01:08:07 +01:00
trnq1ll0 f035a3dc0c einschlafen des Macbooks verhindern 2026-10-24 22:41:15 +02:00
trnq1ll0 3e1c561559 add probing before copy 2026-10-23 12:43:31 +02:00
trnq1ll0 6fe87107ce port OFFSITE backup to MacOS script with spotlight launcher 2026-10-21 09:31:21 +02:00
trnq1ll0 9f0eafee19 windows cmd für OFFSITE backup fehlte der photos Ordner 2026-10-21 09:11:42 +02:00
29 changed files with 0 additions and 1155 deletions

No files matched your search

-34
View File
@@ -1,34 +0,0 @@
#!/bin/sh
# this one makes a copy of the whole /var/www/html from boogie to BFDS
SCRIPTPATH=$(dirname `realpath $0`)
cd $SCRIPTPATH
# rsync source -> destination
SOURCE_PATH=/var/www/virtual/boogie/html
DEST_PATH_FILES=/mnt/bfds/rob/backup/server/boogiedev.net/var_www
# path of the database backups
DEST_PATH_DB=/mnt/bfds/rob/backup/server/boogiedev.net/db
# EXCLUDE="--exclude-from exclude-list"
# ### Get all databases
# kick off DB dumping
curl --insecure --silent https://backup.boogiedev.net/dump_all_databases.php
# download DB dumps
cd $DEST_PATH_DB
curl --insecure --silent https://backup.boogiedev.net/dumps.zip > dumps.zip
if [ -f dumps.zip ]
then
today=`date +%F`
mv dumps.zip all_databases_dump_$today.zip
else
echo "Datenbank Dump konnte nicht herunter geladen werden"
fi
## aufräumen
curl --insecure --silent https://backup.boogiedev.net/dump_all_databases.php?cleanup_dump=1
-7
View File
@@ -1,7 +0,0 @@
backup_all_databases_cMxNrCTVLBPrNo6qaonojeejCvCepekwssh
nextcloud/config/config.php
nextcloud/data/root/files/Extern
nextcloud/updater
nextcloud/backup
/nextcloud-data/updater-*/
files_trashbin/
@@ -1 +0,0 @@
credentials.php
@@ -1,6 +0,0 @@
The content of this folder hast to be on the Boogie server at:
/var/www/virtual/boogie/html/backup-boogie
Its the server side enpoint of the backup script running on BFDS.
The credentials.php has to be filled but never commited to the git
@@ -1,83 +0,0 @@
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
$zipfile = "dumps.zip";
if (isset($_GET['cleanup_dump']) && $_GET['cleanup_dump'] == 1)
{
Cleanup();
die("Cleanup done");
}
$dumps_path = 'dumps/';
if(file_exists($dumps_path))
{
array_map( 'unlink', array_filter((array) glob($dumps_path.'*') ) );
}
else
{
mkdir('dumps');
}
//read php file with credentials and parse it
fopen("credentials.php", "r") or die("Unable to open file!");
include 'credentials.php';
//should priovide the following variables:
// $db_host = 'host';
// $db_user = 'user';
// $db_pass = 'pw';
if ($db_host == NULL || $db_user == NULL || $db_pass == NULL) {
die("Please provide credentials in credentials.php");
}
$db_conn = new mysqli($db_host, $db_user, $db_pass);
$result = mysqli_query($db_conn,"SHOW DATABASES");
while ($row = mysqli_fetch_array($result)) {
if($row[0] == 'information_schema' || $row[0] == 'performance_schema')
{
continue;
}
$database = $row[0];
$dir = dirname(__FILE__) . '/'.$dumps_path.$database.'.sql';
exec("mysqldump --user={$db_user} --password={$db_pass} --host={$db_host} {$database} --result-file={$dir} 2>&1", $output);
if(!empty($output))
{
var_dump($output);
}
}
## Zip all dumps
Cleanup($zipfile);
$zip = new ZipArchive;
if($zip -> open($zipfile, ZipArchive::CREATE ) === TRUE)
{
$dir = opendir($dumps_path);
while($file = readdir($dir)) {
if(is_file($dumps_path.$file)) {
$zip -> addFile($dumps_path.$file, $file);
}
}
$zip ->close();
}
//eine funktion die auf den url parameter cleanup_dump=1 reagiert und dann alle dumps löscht
function Cleanup()
{
global $zipfile;
if(file_exists($zipfile))
{
unlink($zipfile);
}
}
-4
View File
@@ -1,4 +0,0 @@
bfds soll sich regelmässig eine copy des wikis und aller scripte auf boogedev.net (uberspace) holen
Um keine hindernden Passwort-Eingaben zu haben, wird ein dezediertes keyfile ohne PW verwendet.
-21
View File
@@ -1,21 +0,0 @@
#!/bin/sh
# Hint: to exclude any directory (for example an cache dir)
# place the CACHEDIR.TAG file in there: https://bford.info/cachedir/
# don't forget the signature in there and set the parameter: --exclude-caches
export RESTIC_REPOSITORY=bfds:/mnt/bfds/rob-restic-backups/restic-repo-t520-user
export RESTIC_PASSWORD=kopenhagenhatsuperinternetwirklich
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
# backup the whole user dir to bfds
#restic -r sftp:${RESTIC_REPOSITORY} backup /home/rob --exclude-caches --exclude-file=${SCRIPT_DIR}/backup.excludes
# find files in backup. $1 is the parameter from comand line.
# example: $ backup-list-files.sh /home/rob/Pictures > output.txt
# find all files containing Pictures path and out into output.txt
restic -r sftp:${RESTIC_REPOSITORY} find $1
#restic -r sftp:bfds:/mnt/bfds/rob-restic-backups/restic-repo-t520-user backup /home/rob --exclude-caches
-16
View File
@@ -1,16 +0,0 @@
#!/bin/sh
# Hint: to exclude any directory (for example an cache dir)
# place the CACHEDIR.TAG file in there: https://bford.info/cachedir/
# don't forget the signature in there and set the parameter: --exclude-caches
export RESTIC_REPOSITORY=bfds:/mnt/bfds/rob-restic-backups/restic-repo-t520-user
export RESTIC_PASSWORD=kopenhagenhatsuperinternetwirklich
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
# backup the whole user dir to bfds
# restic -r sftp:${RESTIC_REPOSITORY} backup /home/rob --exclude-caches --exclude-file=${SCRIPT_DIR}/backup.excludes
#restic stats -r sftp:${RESTIC_REPOSITORY}
restic snapshots -r sftp:${RESTIC_REPOSITORY}
-21
View File
@@ -1,21 +0,0 @@
#Just Caches
**/.cache/
**/cache/
**/Cache/
#Logs
**/log/
**/logs/
**/Log/
**/Logs/
*.log
# VSCode
/home/rob/.config/Code/Cache
/home/rob/.config/Code/CachedData
/home/rob/.config/Code/CachedExtensions
/home/rob/.config/Code/CachedExtensionVSIXs
# Firefox und Co.
/home/rob/.mozilla
/home/rob/Downloads
-16
View File
@@ -1,16 +0,0 @@
#!/bin/sh
# Hint: to exclude any directory (for example an cache dir)
# place the CACHEDIR.TAG file in there: https://bford.info/cachedir/
# don't forget the signature in there and set the parameter: --exclude-caches
export RESTIC_REPOSITORY=bfds:/mnt/bfds/rob-restic-backups/restic-repo-t520-user
export RESTIC_PASSWORD=kopenhagenhatsuperinternetwirklich
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
# backup the whole user dir to bfds
restic -r sftp:${RESTIC_REPOSITORY} backup /home/rob --exclude-caches --exclude-file=${SCRIPT_DIR}/backup.excludes
#restic -r sftp:bfds:/mnt/bfds/rob-restic-backups/restic-repo-t520-user backup /home/rob --exclude-caches
-12
View File
@@ -1,12 +0,0 @@
#!/bin/sh
# mount the backup into the file system
MOUNTPOINT="/mnt/restic-backup-me"
echo Erstelle Ordner $MOUNTPOINT via sudo
sudo mkdir $MOUNTPOINT
sudo chown rob:rob $MOUNTPOINT
echo Mount Backup
./restic-bfds.sh mount $MOUNTPOINT
-13
View File
@@ -1,13 +0,0 @@
#!/bin/sh
# Hint: to exclude any directory (for example an cache dir)
# place the CACHEDIR.TAG file in there: https://bford.info/cachedir/
# don't forget the signature in there and set the parameter: --exclude-caches
export RESTIC_REPOSITORY=bfds:/mnt/bfds/rob-restic-backups/restic-repo-t520-user
export RESTIC_PASSWORD=kopenhagenhatsuperinternetwirklich
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
# restic wrapper for use with command line arguments
restic -r sftp:${RESTIC_REPOSITORY} $1 $2 $3 $4 $5 $6 $7 $8 $9
-5
View File
@@ -1,5 +0,0 @@
#!/bin/sh
INCLUDE_FILES="/home/rob/Pictures/2022/*"
RESTORE_PATH="/home/rob/Pictures/from_restic/"
./restic-bfds.sh restore --target "$RESTORE_PATH" --include "$INCLUDE_FILES" latest
-7
View File
@@ -1,7 +0,0 @@
backup_all_databases_cMxNrCTVLBPrNo6qaonojeejCvCepekwssh
nextcloud/config/config.php
nextcloud/data/root/files/Extern
nextcloud/updater
nextcloud/backup
/nextcloud-data/updater-*/
files_trashbin/
-3
View File
@@ -1,3 +0,0 @@
This folder hast to be on the SBZ server
The credentials.php has to be filled but never commited to the git
@@ -1,58 +0,0 @@
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
$dumps_path = 'dumps/';
if(file_exists($dumps_path))
{
array_map( 'unlink', array_filter((array) glob($dumps_path.'*') ) );
}
else
{
mkdir('dumps');
}
$host = 'localhost';
$user = 'root';
$pass = "AdfQ3prfUDPu2MtaoLFtxvWbdBooJ6";
$db_conn = new mysqli($host, $user, $pass);
$result = mysqli_query($db_conn,"SHOW DATABASES");
while ($row = mysqli_fetch_array($result)) {
if($row[0] == 'information_schema' || $row[0] == 'performance_schema')
{
continue;
}
$database = $row[0];
$dir = dirname(__FILE__) . '/'.$dumps_path.$database.'.sql';
exec("mysqldump --user={$user} --password={$pass} --host={$host} {$database} --result-file={$dir} 2>&1", $output);
if(!empty($output))
{
var_dump($output);
}
}
## Zip all dumps
$zipfile = "dumps.zip";
if(file_exists($zipfile))
{
unlink($zipfile);
}
$zip = new ZipArchive;
if($zip -> open($zipfile, ZipArchive::CREATE ) === TRUE)
{
$dir = opendir($dumps_path);
while($file = readdir($dir)) {
if(is_file($dumps_path.$file)) {
$zip -> addFile($dumps_path.$file, $file);
}
}
$zip ->close();
}
-7
View File
@@ -1,7 +0,0 @@
bfds soll sich regelmässig eine copy der nextcloud instanz ziehen
Dazu wird der home-rob container im /home/rob gemountet und dann auf file basis eine Kopie vom kompletten /var/www/html
Nextcloud wird zu dem dazu in den maintenance mode versetzt.
Um keoine Passwort-Eingaben zu verhindern, wird ein dezediertes keyfile ohne PW verwendet.
-69
View File
@@ -1,69 +0,0 @@
#!/bin/sh
# this one makes a copy of the whole /var/www/html from sbz
#
# to recover nextcloud data see: https://docs.nextcloud.com/server/13/admin_manual/maintenance/restore.html
#
# rsync args:
# -a, --archive archive mode; equals -rlptgoD (no -H,-A,-X)
# -r, --recursive recurse into directories
# -l, --links When symlinks are encountered, recreate the symlink on the destination.
# -h, --human-readable Output numbers in a more human-readable format.
SCRIPTPATH=$(dirname `realpath $0`)
cd $SCRIPTPATH
# rsync source -> destination
SOURCE_PATH=/var/www/
DEST_PATH_FILES=/mnt/bfds/rob/backup/server/spaceboyz.net/var_www
# ssh credentials
PORT="42523"
KEY="${SCRIPTPATH}/sbz_keyless"
USER_AT_HOST="rob@spaceboyz.net"
# path of the database backups
DEST_PATH_DB=/mnt/bfds/rob/backup/server/spaceboyz.net/mariadb
## path where home-rob container is mouted into host (on SBZ)
SBZ_MOUNT_PATH=/home/rob/tmp_mnt
## path of the nextcloud instance (on SBZ)
NEXTCLOUD_PATH=/var/www/html/nextcloud
EXCLUDE="--exclude-from exclude-list"
# mount my lxc container into my home on sbz
ssh -p $PORT -i $KEY $USER_AT_HOST "sudo mount /dev/mapper/vms-home--rob $SBZ_MOUNT_PATH"
# grand read permissons if not given
ssh -p $PORT -i $KEY $USER_AT_HOST "sudo chmod -R +rx ${SBZ_MOUNT_PATH}/var/www/nextcloud-data"
# put nextcloud into maintenance, outside the home-rob container
ssh -p $PORT -i $KEY $USER_AT_HOST "sudo sed -i \"s|'maintenance' => false|'maintenance' => true|g\" $SBZ_MOUNT_PATH$NEXTCLOUD_PATH/config/config.php"
# rsync download from sbz
rsync --quiet -a -zz --progress $EXCLUDE -e "ssh -p $PORT -i $KEY" $USER_AT_HOST:${SBZ_MOUNT_PATH}${SOURCE_PATH} ${DEST_PATH_FILES}
# turn off nextcloud maintenancThis rsync lacks old-style --compress due to its external zlibe, outside the home-rob container
ssh -p $PORT -i $KEY $USER_AT_HOST "sudo sed -i \"s|'maintenance' => true|'maintenance' => false|g\" $SBZ_MOUNT_PATH$NEXTCLOUD_PATH/config/config.php"
# umount my container
ssh -p $PORT -i $KEY $USER_AT_HOST "sudo umount $SBZ_MOUNT_PATH"
### Get all databases
cd $DEST_PATH_DB
# kick off DB dumping
curl --insecure --silent https://rob.spaceboyz.net/backup_all_databases_cMxNrCTVLBPrNo6qaonojeejCvCepekwssh/dump_all_databases.php
# download DB dumps
curl --insecure --silent https://rob.spaceboyz.net/backup_all_databases_cMxNrCTVLBPrNo6qaonojeejCvCepekwssh/dumps.zip > dumps.zip
if [ -f dumps.zip ]
then
today=`date +%F`
mv dumps.zip all_databases_dump_$today.zip
else
echo "Datenbank Dump konnte nicht herunter geladen werden"
fi
-49
View File
@@ -1,49 +0,0 @@
-----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY-----
-1
View File
@@ -1 +0,0 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCrW6mg83W/cdQuaB/G0kjV3rTRUv76sXvTlyyauli6SjEsemfeDoVTDAsPUXu6ql+aKeAIE70dbq8uYdmu0hwZvUHNVcwLKIV9vTvoeMBAfNX81aTw+koXfZ6SR6MW8vaNK+b4eUYvTa4bS7FwrUXMgQV3mlnUpPLGgS+0K47id1hyxwOolzbLtGTdR1ucVzE+ZitHt3Vn6/AJ1KJV2+TFSZZXKhUp7lidHwrQt2x0n/zkQ/BW8+sL5b7/2G4kUL++OfF+nZjQGFZgnVIU28fmK1izkYtni2PYPzResy8r3eAwrqv5/8w7FqI3Lx2BkBHxKZU3sEOwV4Y5BBZACv4zk39VTJDaz55Asf7g+cCOFY0zqr/sYWek0zlPP2ccs9gi5gH/oLeLLCBPGCgefH6az8qfwvDu+gN0ugUo4aH4HfWEzZpS5klx3F12txxV514Z1RKSxvImuCg03SYvMX1cluusbO2uRj9VJ0VnA9g+UiEm/UJf9GgB450fXEcjJUyUyQ8eFyD7TPIKBzCxv0GDclKKzJeeUbfGkxQockpl3he1cIISfyoxPETyzTwIz/LOQsScMpC4W9r1yc0MJcNy8e0K6h/Bwev6tEqV0qIHzv5bnjVdDkKbEMMJ7LaBeUQstgJ4w3tgVxN6QgvTesDCRg4oWuqb5d82eAvl/9k+dQ== rob@d3v.localdomain
@@ -1,30 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.backup-recordings-to-bfds</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>/Users/rob/code/lokale-scripte/macos/Backup-Recordings/backup-recordings-to-bfds.sh</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>12</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
<key>StandardOutPath</key>
<string>/Users/rob/code/lokale-scripte/macos/Backup-Recordings/run-backup-recordings_output.log</string>
<key>StandardErrorPath</key>
<string>/Users/rob/code/lokale-scripte/macos/Backup-Recordings/run-backup-recordings_error.log</string>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
@@ -1,127 +0,0 @@
#!/bin/bash
# Logfile definieren
LOGFILE="$HOME/Library/Logs/backup-recordings.log"
MAX_LOG_SIZE=$((3 * 1024 * 1024)) # 3MB in Bytes
# Funktion zur Überprüfung und Rotation des Logfiles
rotate_log() {
if [ -f "$LOGFILE" ]; then
local size=$(stat -f%z "$LOGFILE" 2>/dev/null || echo 0)
if [ "$size" -ge "$MAX_LOG_SIZE" ]; then
# Behalte nur die letzten 50% des Logs
local keep_lines=$(($(wc -l < "$LOGFILE") / 2))
tail -n "$keep_lines" "$LOGFILE" > "${LOGFILE}.tmp"
mv "${LOGFILE}.tmp" "$LOGFILE"
fi
fi
}
# Funktion für Log-Ausgaben mit Zeitstempel
log() {
local message="[$(date '+%Y-%m-%d %H:%M:%S')] $1"
echo "$message" | tee -a "$LOGFILE"
}
# Funktion für macOS Benachrichtigungen
notify() {
local subtitle="$1"
local message="$2"
osascript -e "display notification \"$message\" with title \"Backup Recordings\" subtitle \"$subtitle\""
}
# Funktion zum Formatieren von Bytes in menschenlesbare Größe
format_bytes() {
local bytes=$1
if [ "$bytes" -lt 1024 ]; then
echo "${bytes} B"
elif [ "$bytes" -lt 1048576 ]; then
awk "BEGIN {printf \"%.1f KB\", $bytes/1024}"
elif [ "$bytes" -lt 1073741824 ]; then
awk "BEGIN {printf \"%.1f MB\", $bytes/1048576}"
else
awk "BEGIN {printf \"%.2f GB\", $bytes/1073741824}"
fi
}
# Log rotieren falls nötig
rotate_log
# Quelle und Ziel definieren
SOURCE="/Users/rob/Recordings/Work/00_MacOS/"
SERVER="bfds"
REMOTE_PATH="/mnt/bfds-nix/rob/recordings/Progress/Work/00_MacOS/"
TARGET="$SERVER:$REMOTE_PATH"
# Prüfen, ob SSH-Verbindung zum Server möglich ist
log "Prüfe SSH-Verbindung zu $SERVER..."
if ! ssh -o ConnectTimeout=5 -o BatchMode=yes "$SERVER" "exit" 2>/dev/null; then
log "Fehler: SSH-Verbindung zu $SERVER fehlgeschlagen"
notify "Fehler" "Server $SERVER nicht erreichbar!"
exit 1
fi
log "SSH-Verbindung erfolgreich"
# Prüfen, ob das Remote-Zielverzeichnis existiert
log "Prüfe Remote-Zielverzeichnis..."
if ! ssh "$SERVER" "test -d \"$REMOTE_PATH\""; then
log "Fehler: Remote-Verzeichnis existiert nicht: $REMOTE_PATH"
notify "Fehler" "Zielverzeichnis nicht gefunden!\n$REMOTE_PATH"
exit 1
fi
# Prüfen, ob Schreibrechte vorhanden sind
if ! ssh "$SERVER" "test -w \"$REMOTE_PATH\""; then
log "Fehler: Keine Schreibrechte für Remote-Verzeichnis: $REMOTE_PATH"
notify "Fehler" "Keine Schreibrechte!\n$REMOTE_PATH"
exit 1
fi
log "Remote-Verzeichnis ist bereit für Backup"
# Sicherung starten
log "Starte rsync..."
RSYNC_OUTPUT=$(mktemp)
log "Befehl: rsync -av --stats --no-owner --no-group \"$SOURCE\" > \"$RSYNC_OUTPUT\" \"$TARGET\""
rsync -av --stats --no-owner --no-group "$SOURCE" "$TARGET" > "$RSYNC_OUTPUT" 2>&1
# Rsync-Output komplett ins Logfile schreiben
cat "$RSYNC_OUTPUT" >> "$LOGFILE"
# -a → Archive-Modus (erhält Zeitstempel, Links, etc.)
# -v → Verbose
# --stats → Statistiken anzeigen
# --no-owner → Besitzer nicht übertragen
# --no-group → Gruppe nicht übertragen
# Statistiken auslesen - rsync gibt mehrere Formate aus, teste beide
TOTAL_FILES=$(grep -E "Number of (regular )?files transferred:" "$RSYNC_OUTPUT" | awk '{print $NF}' | tr -d ',')
if [ -z "$TOTAL_FILES" ]; then
# Alternativer Versuch
TOTAL_FILES=$(grep "files transferred:" "$RSYNC_OUTPUT" | awk '{print $(NF)}' | tr -d ',')
fi
# Dateigröße in Bytes auslesen
# macOS rsync gibt aus: "Total transferred file size: 86137390 B"
TOTAL_BYTES=$(grep "Total transferred file size:" "$RSYNC_OUTPUT" | awk '{print $5}')
# Konvertiere String in Integer
TOTAL_BYTES=$((TOTAL_BYTES))
# Formatiere Größe in menschenlesbares Format
TOTAL_SIZE=$(format_bytes "$TOTAL_BYTES")
# Fallback falls keine Werte gefunden wurden
[ -z "$TOTAL_FILES" ] && TOTAL_FILES="0"
# Temporäre Datei löschen
rm -f "$RSYNC_OUTPUT"
# Setze Besitzer und Rechte auf dem Server
log "Setze Besitzer und Rechte..."
ssh "$SERVER" "chown -R smbuser:smbuser \"$REMOTE_PATH\" && chmod -R 777 \"$REMOTE_PATH\" && find \"$REMOTE_PATH\" -type f -exec chmod 666 {} \;" 2>&1 | tee -a "$LOGFILE"
# Erfolgsmeldung ausgeben
log "Backup abgeschlossen: $TOTAL_FILES Dateien, $TOTAL_SIZE übertragen"
# Optional: Notification für macOS anzeigen
notify "Abgeschlossen" "$TOTAL_FILES Dateien übertragen ($TOTAL_SIZE)"
-96
View File
@@ -1,96 +0,0 @@
# Paperless-ngx-Backup von TrueNAS auf macOS
Das Script ist fuer die auf `bfds` erkannte Installation vorbereitet:
- TrueNAS 25.04.2.6
- TrueNAS-App `paperless-ngx` 1.6.37
- Paperless-ngx 3.0.2
- Container `ix-paperless-ngx-paperless-1`
Es pflegt einen dauerhaften Paperless-Export und synchronisiert ihn ungezippt
auf den Mac. Nach dem ersten vollstaendigen Lauf werden nur neue, geaenderte
oder geloeschte Dateien abgeglichen. Es entstehen keine Archive und keine
taeglichen Vollkopien.
Der lokale Stand befindet sich hier:
```text
~/Backups/paperless-ngx/current/
├── backup-info.txt
├── paperless-export/
│ ├── manifest.json
│ ├── metadata.json
│ ├── archive/
│ ├── originals/
│ └── thumbnails/
└── truenas-app-config/
```
`paperless-export` enthaelt Dokumente, Thumbnails, Metadaten, Benutzer und die
exportierten Datenbankinhalte. `truenas-app-config` enthaelt zusaetzlich die
TrueNAS-App-Einstellungen und die Konfiguration frueherer App-Versionen.
## Testen und ausfuehren
```bash
./backup-paperless-ngx.sh --check
./backup-paperless-ngx.sh
```
`--check` prueft SSH, Container und Mounts. Ausserdem wird ein kleiner
temporaerer `document_exporter --data-only`-Lauf ausgefuehrt und dessen
Manifest kontrolliert. Der Testexport wird sofort wieder entfernt.
Alle Werte lassen sich ueber `~/.config/paperless-backup/config` aendern;
siehe `config.example`. Voraussetzung ist, dass
`ssh -o BatchMode=yes bfds true` ohne Passwortdialog funktioniert.
## Taeglich um 03:00 Uhr ausfuehren
```bash
./install-launchagent.sh
```
Der Installer kopiert das Script nach
`~/Library/Application Support/PaperlessBackup`, legt bei Bedarf die
Konfiguration an und registriert einen LaunchAgent. Ein manueller Lauf des
installierten Jobs ist mit folgendem Befehl moeglich:
```bash
launchctl kickstart -k gui/$(id -u)/de.rob.paperless-ngx-backup
```
Logs stehen in `~/Library/Logs/paperless-ngx-backup.log` sowie in den beiden
`paperless-ngx-launchd.*.log`-Dateien.
## Wiederherstellung
1. Eine frische Paperless-Instanz mit derselben Paperless-Version wie in
`backup-info.txt` bereitstellen.
2. Den Inhalt von `current/paperless-export` in das Export-Volume der neuen
Instanz kopieren und dort `document_importer` ausfuehren.
3. `current/truenas-app-config` dient als Referenz fuer Ports, Storage,
Umgebungswerte und Secrets. Diese Dateien nicht blind in eine laufende
TrueNAS-App zurueckkopieren.
Der Paperless-Exporter sichert keine API-Tokens; sie muessen nach einem Import
neu erzeugt werden.
## Verhalten und Sicherheit
Das ist ein Spiegel des aktuellen Paperless-Stands. Wird ein Dokument in
Paperless geloescht, wird es beim naechsten erfolgreichen Lauf auch aus diesem
Export entfernt. Fuer historische Staende sollte der Ordner zusaetzlich durch
Time Machine oder ein anderes versionierendes Backup gesichert werden.
Der Export enthaelt alle Dokumente und moegliche Klartext-Secrets aus der
TrueNAS-App-Konfiguration. Das uebergeordnete Zielverzeichnis ist nur fuer den
aktuellen macOS-Benutzer zugaenglich. FileVault sollte aktiviert sein.
Auf dem TrueNAS-Apps-Pool bleibt ebenfalls ein ungezipptes Exportverzeichnis
`macos-backup-current` bestehen. Es wird von Paperless bei jedem Lauf
inkrementell aktualisiert.
Bereits mit einer aelteren Scriptversion erzeugte `.tar.gz`-Archive werden
nicht automatisch geloescht. Sie koennen nach erfolgreicher Kontrolle des
neuen `current`-Ordners manuell entfernt werden.
@@ -1,290 +0,0 @@
#!/bin/bash
# Unkomprimierter, inkrementell aktualisierter Paperless-Export auf diesen Mac.
# Kompatibel mit dem mit macOS ausgelieferten Bash 3.2.
set -euo pipefail
PATH="/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/homebrew/bin"
export PATH
SERVER="${SERVER:-bfds}"
APP_NAME="${APP_NAME:-paperless-ngx}"
PAPERLESS_CONTAINER="${PAPERLESS_CONTAINER:-ix-paperless-ngx-paperless-1}"
BACKUP_DIR="${BACKUP_DIR:-$HOME/Backups/paperless-ngx}"
SSH_CONNECT_TIMEOUT="${SSH_CONNECT_TIMEOUT:-10}"
NOTIFY="${NOTIFY:-1}"
CONFIG_FILE="${PAPERLESS_BACKUP_CONFIG:-$HOME/.config/paperless-backup/config}"
LOG_FILE="${PAPERLESS_BACKUP_LOG:-$HOME/Library/Logs/paperless-ngx-backup.log}"
if [ -f "$CONFIG_FILE" ]; then
# Die Datei ist eine benutzereigene Shell-Konfiguration (KEY=WERT).
# shellcheck disable=SC1090
. "$CONFIG_FILE"
fi
SSH=(/usr/bin/ssh -o BatchMode=yes -o ConnectTimeout="$SSH_CONNECT_TIMEOUT")
RSYNC_RSH="/usr/bin/ssh -o BatchMode=yes -o ConnectTimeout=$SSH_CONNECT_TIMEOUT"
APP_CONFIG_DIR="/mnt/.ix-apps/app_configs/$APP_NAME"
REMOTE_CURRENT_DIR="/usr/src/paperless/export/macos-backup-current"
LOCAL_CURRENT_DIR="$BACKUP_DIR/current"
LOCK_DIR="${TMPDIR:-/tmp}/paperless-ngx-backup-${UID}.lock"
LOCAL_CHECK_DIR=""
REMOTE_EXPORT_DIR=""
REMOTE_EXPORT_HOST_DIR=""
BACKUP_SUCCEEDED=0
usage() {
cat <<'EOF'
Aufruf: backup-paperless-ngx.sh [--check | --help]
ohne Option Lokalen Paperless-Export inkrementell aktualisieren
--check Zusaetzlich einen temporaeren Datenbankexport testen
--help Diese Hilfe anzeigen
Optionale Konfiguration: ~/.config/paperless-backup/config
EOF
}
log() {
local message
message="[$(date '+%Y-%m-%d %H:%M:%S')] $*"
printf '%s\n' "$message" | /usr/bin/tee -a "$LOG_FILE"
}
warn() {
log "WARNUNG: $*"
}
notify() {
[ "$NOTIFY" = "1" ] || return 0
/usr/bin/osascript - "$1" "$2" >/dev/null 2>&1 <<'APPLESCRIPT' || true
on run argv
display notification (item 2 of argv) with title "Paperless-ngx Backup" subtitle (item 1 of argv)
end run
APPLESCRIPT
}
die() {
log "FEHLER: $*"
exit 1
}
cleanup() {
local exit_code=$?
trap - EXIT INT TERM
set +e
if [ -n "$REMOTE_EXPORT_DIR" ]; then
case "$REMOTE_EXPORT_DIR" in
/usr/src/paperless/export/macos-backup-check-*)
"${SSH[@]}" "$SERVER" \
"docker exec '$PAPERLESS_CONTAINER' rm -rf -- '$REMOTE_EXPORT_DIR'" \
>/dev/null 2>&1 || true
;;
esac
fi
if [ -n "$LOCAL_CHECK_DIR" ] && [ -d "$LOCAL_CHECK_DIR" ]; then
case "$LOCAL_CHECK_DIR" in
"$BACKUP_DIR"/.check.*) /bin/rm -rf -- "$LOCAL_CHECK_DIR" ;;
esac
fi
/bin/rmdir "$LOCK_DIR" >/dev/null 2>&1 || true
if [ "$exit_code" -ne 0 ] && [ "$BACKUP_SUCCEEDED" -ne 1 ]; then
notify "Fehlgeschlagen" "Details: $LOG_FILE"
fi
exit "$exit_code"
}
validate_configuration() {
case "$SERVER" in
*[!A-Za-z0-9_.:@-]*|'') die "Ungueltiger SERVER-Wert: $SERVER" ;;
esac
case "$APP_NAME" in
*[!A-Za-z0-9_.-]*|'') die "Ungueltiger APP_NAME-Wert: $APP_NAME" ;;
esac
case "$PAPERLESS_CONTAINER" in
*[!A-Za-z0-9_.-]*|'') die "Ungueltiger Containername: $PAPERLESS_CONTAINER" ;;
esac
case "$SSH_CONNECT_TIMEOUT" in
*[!0-9]*|'') die "SSH_CONNECT_TIMEOUT muss eine ganze Zahl sein" ;;
esac
case "$BACKUP_DIR" in
/*) ;;
*) die "BACKUP_DIR muss ein absoluter Pfad sein" ;;
esac
[ "$BACKUP_DIR" != "/" ] || die "Das Wurzelverzeichnis darf nicht BACKUP_DIR sein"
}
check_remote() {
local running
log "Pruefe SSH-Verbindung zu $SERVER ..."
"${SSH[@]}" "$SERVER" true >/dev/null 2>&1 || \
die "SSH-Verbindung fehlgeschlagen (fuer launchd ist ein SSH-Key ohne Rueckfrage erforderlich)"
running=$("${SSH[@]}" "$SERVER" \
"docker inspect --format='{{.State.Running}}' '$PAPERLESS_CONTAINER' 2>/dev/null") || \
die "Paperless-Container $PAPERLESS_CONTAINER wurde nicht gefunden"
[ "$running" = "true" ] || die "Paperless-Container laeuft nicht"
"${SSH[@]}" "$SERVER" "test -d '$APP_CONFIG_DIR' && test -r '$APP_CONFIG_DIR'" || \
die "TrueNAS-App-Konfiguration ist nicht lesbar: $APP_CONFIG_DIR"
REMOTE_EXPORT_HOST_DIR=$("${SSH[@]}" "$SERVER" \
"docker inspect --format='{{range .Mounts}}{{if eq .Destination \"/usr/src/paperless/export\"}}{{.Source}}{{end}}{{end}}' '$PAPERLESS_CONTAINER'")
case "$REMOTE_EXPORT_HOST_DIR" in
/mnt/.ix-apps/docker/volumes/*/_data) ;;
*) die "Export-Mount des Paperless-Containers konnte nicht sicher ermittelt werden" ;;
esac
# document_exporter wird vom Container-Wrapper von root auf den Benutzer
# paperless heruntergestuft. Daher muessen Check und Ordnererstellung mit
# genau diesem Benutzer erfolgen.
"${SSH[@]}" "$SERVER" \
"docker exec --user paperless:paperless '$PAPERLESS_CONTAINER' test -w /usr/src/paperless/export" || \
die "Paperless-Exportverzeichnis ist nicht beschreibbar"
log "Paperless-Container und TrueNAS-Speicherorte sind bereit"
}
write_backup_info() {
local info_file="$1"
{
printf 'Backup created on Mac: %s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
printf 'SSH server: %s\n' "$SERVER"
printf 'TrueNAS app name: %s\n' "$APP_NAME"
printf 'Paperless container: %s\n' "$PAPERLESS_CONTAINER"
"${SSH[@]}" "$SERVER" "printf 'TrueNAS version: '; cat /etc/version; printf '\nContainer image (Paperless version): '; docker inspect --format='{{.Config.Image}}' '$PAPERLESS_CONTAINER'"
} > "$info_file"
}
check_capacity() {
local source_kb remote_current_kb local_current_kb
local remote_needed_kb local_needed_kb remote_free_kb local_free_kb
source_kb=$("${SSH[@]}" "$SERVER" \
"du -sk /mnt/.ix-apps/app_mounts/'$APP_NAME'/media /mnt/.ix-apps/app_mounts/'$APP_NAME'/data 2>/dev/null | awk '{s+=\$1} END {print s+0}'") || source_kb=0
remote_free_kb=$("${SSH[@]}" "$SERVER" \
"df -Pk '$REMOTE_EXPORT_HOST_DIR' | awk 'NR==2 {print \$4}'") || remote_free_kb=0
local_free_kb=$(/bin/df -Pk "$BACKUP_DIR" | /usr/bin/awk 'NR==2 {print $4}')
remote_current_kb=$("${SSH[@]}" "$SERVER" \
"du -sk '$REMOTE_EXPORT_HOST_DIR/macos-backup-current' 2>/dev/null | awk '{print \$1+0}'") || remote_current_kb=0
local_current_kb=$(/usr/bin/du -sk "$LOCAL_CURRENT_DIR/paperless-export" 2>/dev/null | /usr/bin/awk '{print $1+0}') || local_current_kb=0
if [ "$source_kb" -gt 0 ]; then
log "Geschaetzte Paperless-Datenmenge: $((source_kb / 1024)) MB"
remote_needed_kb=$((source_kb - remote_current_kb))
local_needed_kb=$((source_kb - local_current_kb))
[ "$remote_needed_kb" -gt 0 ] || remote_needed_kb=0
[ "$local_needed_kb" -gt 0 ] || local_needed_kb=0
[ "$remote_free_kb" -gt "$remote_needed_kb" ] || \
warn "Auf dem TrueNAS-Apps-Pool koennte der Platz fuer den Export knapp sein"
[ "$local_free_kb" -gt "$local_needed_kb" ] || \
warn "Auf dem Mac koennte der Platz fuer den lokalen Export knapp sein"
fi
}
check_document_exporter() {
local check_id check_host_dir
check_id="$(date '+%Y-%m-%d_%H-%M-%S')-$$"
REMOTE_EXPORT_DIR="/usr/src/paperless/export/macos-backup-check-$check_id"
check_host_dir="$REMOTE_EXPORT_HOST_DIR/macos-backup-check-$check_id"
LOCAL_CHECK_DIR=$(/usr/bin/mktemp -d "$BACKUP_DIR/.check.XXXXXX")
log "Teste document_exporter und inkrementelle Aktualisierung ..."
if ! "${SSH[@]}" "$SERVER" \
"docker exec --user paperless:paperless '$PAPERLESS_CONTAINER' mkdir -p '$REMOTE_EXPORT_DIR' && docker exec '$PAPERLESS_CONTAINER' document_exporter --no-progress-bar --data-only --compare-json --delete --use-folder-prefix '$REMOTE_EXPORT_DIR' && docker exec '$PAPERLESS_CONTAINER' document_exporter --no-progress-bar --data-only --compare-json --delete --use-folder-prefix '$REMOTE_EXPORT_DIR' && docker exec '$PAPERLESS_CONTAINER' test -s '$REMOTE_EXPORT_DIR/manifest.json'" \
2>&1 | /usr/bin/tee -a "$LOG_FILE"; then
die "Test des document_exporter ist fehlgeschlagen"
fi
log "Teste inkrementelle rsync-Uebertragung auf den Mac ..."
/usr/bin/rsync -a --delete --no-owner --no-group -e "$RSYNC_RSH" \
"$SERVER:$check_host_dir/" "$LOCAL_CHECK_DIR/" \
>> "$LOG_FILE" 2>&1 || die "Test der rsync-Uebertragung ist fehlgeschlagen"
[ -s "$LOCAL_CHECK_DIR/manifest.json" ] || \
die "Der rsync-Test hat kein gueltiges Manifest uebertragen"
"${SSH[@]}" "$SERVER" \
"docker exec '$PAPERLESS_CONTAINER' rm -rf -- '$REMOTE_EXPORT_DIR'" \
>/dev/null 2>&1 || die "Temporaerer Testexport konnte nicht entfernt werden"
REMOTE_EXPORT_DIR=""
/bin/rm -rf -- "$LOCAL_CHECK_DIR"
LOCAL_CHECK_DIR=""
log "document_exporter, Aktualisierung, Manifest und rsync erfolgreich getestet"
}
main() {
local mode="backup" export_size remote_current_host
case "${1:-}" in
'') ;;
--check) mode="check" ;;
--help|-h) usage; exit 0 ;;
*) usage >&2; exit 2 ;;
esac
case "$BACKUP_DIR" in
/*) ;;
*) printf 'FEHLER: BACKUP_DIR muss ein absoluter Pfad sein\n' >&2; exit 1 ;;
esac
[ "$BACKUP_DIR" != "/" ] || { printf 'FEHLER: Ungueltiger BACKUP_DIR\n' >&2; exit 1; }
/bin/mkdir -p "$(dirname "$LOG_FILE")" "$BACKUP_DIR"
/bin/chmod 700 "$BACKUP_DIR" 2>/dev/null || true
validate_configuration
if ! /bin/mkdir "$LOCK_DIR" 2>/dev/null; then
die "Ein Paperless-Backup laeuft bereits (Lock: $LOCK_DIR)"
fi
trap cleanup EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
check_remote
if [ "$mode" = "check" ]; then
check_document_exporter
write_backup_info /dev/stdout
log "Pruefung erfolgreich; temporaerer Testexport entfernt und kein Backup auf dem Mac gespeichert"
exit 0
fi
check_capacity
/bin/mkdir -p "$LOCAL_CURRENT_DIR/paperless-export" "$LOCAL_CURRENT_DIR/truenas-app-config"
remote_current_host="$REMOTE_EXPORT_HOST_DIR/macos-backup-current"
log "Aktualisiere den dauerhaften Paperless-Export ..."
if ! "${SSH[@]}" "$SERVER" \
"docker exec --user paperless:paperless '$PAPERLESS_CONTAINER' mkdir -p '$REMOTE_CURRENT_DIR' && docker exec '$PAPERLESS_CONTAINER' document_exporter --no-progress-bar --compare-json --delete --use-folder-prefix '$REMOTE_CURRENT_DIR'" \
2>&1 | /usr/bin/tee -a "$LOG_FILE"; then
die "document_exporter ist fehlgeschlagen"
fi
log "Synchronisiere neue und geaenderte Dokumente auf den Mac ..."
/usr/bin/rsync -a --delete --no-owner --no-group -e "$RSYNC_RSH" \
"$SERVER:$remote_current_host/" "$LOCAL_CURRENT_DIR/paperless-export/" \
>> "$LOG_FILE" 2>&1 || die "Uebertragung des Paperless-Exports ist fehlgeschlagen"
[ -s "$LOCAL_CURRENT_DIR/paperless-export/manifest.json" ] || \
die "Der lokale Export enthaelt kein gueltiges manifest.json"
log "Sichere die TrueNAS-App-Konfiguration ..."
/usr/bin/rsync -a --delete --no-owner --no-group -e "$RSYNC_RSH" \
"$SERVER:$APP_CONFIG_DIR/" "$LOCAL_CURRENT_DIR/truenas-app-config/" \
>> "$LOG_FILE" 2>&1 || die "Uebertragung der App-Konfiguration ist fehlgeschlagen"
write_backup_info "$LOCAL_CURRENT_DIR/backup-info.txt"
export_size=$(/usr/bin/du -sh "$LOCAL_CURRENT_DIR" | /usr/bin/awk '{print $1}')
BACKUP_SUCCEEDED=1
log "Backup erfolgreich aktualisiert: $LOCAL_CURRENT_DIR ($export_size)"
notify "Erfolgreich aktualisiert" "$LOCAL_CURRENT_DIR – $export_size"
}
main "$@"
-12
View File
@@ -1,12 +0,0 @@
# Diese Datei nach ~/.config/paperless-backup/config kopieren und anpassen.
# Die Standardwerte passen bereits zur untersuchten Installation auf "bfds".
SERVER="bfds"
APP_NAME="paperless-ngx"
PAPERLESS_CONTAINER="ix-paperless-ngx-paperless-1"
BACKUP_DIR="$HOME/Backups/paperless-ngx"
# 1 = macOS-Mitteilung bei Erfolg/Fehler, 0 = keine Mitteilung.
NOTIFY=1
SSH_CONNECT_TIMEOUT=10
@@ -1,57 +0,0 @@
#!/bin/bash
set -euo pipefail
SOURCE_DIR=$(cd "$(dirname "$0")" && pwd)
INSTALL_DIR="$HOME/Library/Application Support/PaperlessBackup"
CONFIG_DIR="$HOME/.config/paperless-backup"
PLIST="$HOME/Library/LaunchAgents/de.rob.paperless-ngx-backup.plist"
LOG_DIR="$HOME/Library/Logs"
UID_VALUE=$(id -u)
mkdir -p "$INSTALL_DIR" "$CONFIG_DIR" "$(dirname "$PLIST")" "$LOG_DIR"
install -m 700 "$SOURCE_DIR/backup-paperless-ngx.sh" "$INSTALL_DIR/backup-paperless-ngx.sh"
if [ ! -f "$CONFIG_DIR/config" ]; then
install -m 600 "$SOURCE_DIR/config.example" "$CONFIG_DIR/config"
echo "Konfiguration angelegt: $CONFIG_DIR/config"
else
echo "Vorhandene Konfiguration bleibt unveraendert: $CONFIG_DIR/config"
fi
cat > "$PLIST" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>de.rob.paperless-ngx-backup</string>
<key>ProgramArguments</key>
<array>
<string>$INSTALL_DIR/backup-paperless-ngx.sh</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>3</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
<key>ProcessType</key>
<string>Background</string>
<key>StandardOutPath</key>
<string>$LOG_DIR/paperless-ngx-launchd.out.log</string>
<key>StandardErrorPath</key>
<string>$LOG_DIR/paperless-ngx-launchd.err.log</string>
</dict>
</plist>
EOF
chmod 600 "$PLIST"
plutil -lint "$PLIST"
launchctl bootout "gui/$UID_VALUE" "$PLIST" >/dev/null 2>&1 || true
launchctl bootstrap "gui/$UID_VALUE" "$PLIST"
echo "LaunchAgent installiert: taeglich um 03:00 Uhr"
echo "Vor dem ersten echten Lauf bitte pruefen:"
echo " $INSTALL_DIR/backup-paperless-ngx.sh --check"
@@ -1,70 +0,0 @@
@ECHO OFF
CHCP 65001 >NUL
REM **************************************************
REM Dieser command sichert die Inhalt von bfds in windows
REM auf der OFFSITE platte (Veracrypt Partition) mittels
REM SMB Freigaben von bfds und xcopy.
REM **************************************************
SETLOCAL
REM **************************************************
REM Einbinden der Veracrypt Partition
REM **************************************************
echo Festplatte NASOFFSITE1 wird entschlüsselt und eingebunden
SET VOLUME_LABEL=NASOFFSITE1
SET VERACRYPT_PATH="C:\Program Files\VeraCrypt\VeraCrypt.exe"
SET VOLUME_MOUNT_PATH="B:\"
SET VOLUME_PASSWORD=123456
REM get the Password for the volume from the user
SET /P VOLUME_PASSWORD=Bitte geben Sie das Passwort für die Festplatte %VOLUME_LABEL% ein:
%VERACRYPT_PATH% /q /v "\Device\Harddisk2\Partition1" /l B /p "%VOLUME_PASSWORD%"
SET SOURCE=\\bfds
SET DESTINATION="B:\\"
echo Festplatte NASOFFSITE1 wird gesucht
IF NOT EXIST %DESTINATION% (
echo Festplatte NASOFFSITE1 nicht gefunden, bitte in Veracrypt als Laufwerk B:\ einbinden!
PAUSE
exit /B
)
CALL :RunRobocopy "ebooks"
CALL :RunRobocopy "movies"
CALL :RunRobocopy "music"
CALL :RunRobocopy "peggy"
CALL :RunRobocopy "rob"
exit /B %ERRORLEVEL%
REM **************************************************
REM Funktion zum Ausführen von robocopy
:RunRobocopy
SET COPYPATH=%~1
IF NOT EXIST %SOURCE%\%COPYPATH% (
echo Quelle nicht gefunden! Bitte öffne die SMB Freigbe: %SOURCE%\%COPYPATH%
PAUSE
exit /B %ERRORLEVEL%
)
SET SCRIPT_PATH=%~dp0
SET TODAY=%DATE:~6,4%%DATE:~3,2%%DATE:~0,2%
SET LOGFILE="%SCRIPT_PATH%robocopy_log_%COPYPATH%_%TODAY%.txt"
echo LOGFILE: %LOGFILE%
robocopy %SOURCE%\%COPYPATH% %DESTINATION%\%COPYPATH% /E /mt /z /J /COPY:DAT /R:3 /W:5 /LOG+:%LOGFILE% /TEE
REM Erklärung der Parameter:
REM /E: Kopiert Unterverzeichnisse inklusive leerer Verzeichnisse
REM /mt: Multithreaded copying
REM /z: Kopiert Dateien im Restart-Modus (d.h. kopiert Dateien, die bereits existieren)
REM /COPY:DAT: Kopiert Dateiattribute, Zeitstempel und Sicherheitsinformationen
REM /R:3: Anzahl der Wiederholungsversuche bei Fehlern
REM /W:5: Wartezeit zwischen den Wiederholungsversuchen
REM /J: Kopiert mit unbuffered I/O (empfohlen für große Dateien)
REM /LOG+: Protokolliert den Kopiervorgang in eine Datei
REM /TEE: Protokolliert den Kopiervorgang auch in der Konsole
GOTO :EOF
REM **************************************************
REM **************************************************
REM Partiton wieder aushängen
REM **************************************************
%VERACRYPT_PATH% /d B /q
-8
View File
@@ -1,8 +0,0 @@
bfds soll ein funktionierendes off-site backup bekommen
Dazu sollte am besten eine USB-HDD direkt an bfds gehangen werdn das automatisch überspielt wird.
Oder die Platte kommt an den Lappi und ein rsync zieht sich alle Änderungen von bfds:/mnt/bfds nach /run/media/rob/BFDS-OFFSITE-1/
Label HDD1: BFDS-OFFSITE-1
Label HDD2: BFDS-OFFSITE-2
-32
View File
@@ -1,32 +0,0 @@
#!/bin/sh
# FIRST Encrypt!
## von Hand und mit Dolphin
# only run when BFDS-OFFSITE-1 or BFDS-OFFSITE-2 is attached
# find correct drive `ls -l /dev/disk/by-label/BFDS-OFFSITE-1` or `lsblk -o name,mountpoint,label,size,uuid /dev/sdb1 | grep BFDS-OFFSITE-`
# BFDS-OFFSITE-1 UUID: 45f76efd-1cb0-4ade-b610-0443eb8079e9
# find correct drive `ls -l /dev/disk/by-label/BFDS-OFFSITE-1` or `lsblk -o name,mountpoint,label,size,uuid /dev/sdb1 | grep BFDS-OFFSITE-`
echo "#####################################################"
echo "## ##"
echo "## BFDS-OFFSITE BACKUP ##"
echo "## ##"
echo "#####################################################"
echo "## FIRST Encrypt HDD! von Hand und mit Dolphin ##"
echo "#####################################################"
string=`ls -l /dev/disk/by-label/BFDS-OFFSITE-*`
if [[ $string != *"BFDS-OFFSITE-"* ]]; then
echo "\n ABBRUCH! Keine angeschlossene OFFSITE Festplatte gefunden."
exit 0
fi
# run backup
sourceDir="bfds:/mnt/bfds/" # on BFDS
destinationDir="/run/media/rob/BFDS-OFFSITE-1/BFDS"
rsync -a -zz --stats --progress $sourceDir $destinationDir
read -p "Backupprozess beendet. Taste drücken für weiter.."