commit a0752952ce5647a310ebdd6ce32b04244d844023 Author: Thorsten Date: Tue May 12 15:50:39 2026 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3d5ac5c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +shell_proxmox_backup_copy_own.env diff --git a/README.md b/README.md new file mode 100644 index 0000000..ba3fb3c --- /dev/null +++ b/README.md @@ -0,0 +1,116 @@ +# Proxmox Backup Copy Script + +Dieses Bash-Skript automatisiert das Kopieren von **Proxmox Backups** auf ein Netzlaufwerk via **rsync über SSH**. +Es stellt sicher, dass Backups zuverlässig und verlustfrei übertragen werden, prüft deren Integrität und räumt alte Sicherungen automatisch auf. + +--- + +## ✨ Funktionen + +- **Integritätsprüfung** jedes Backups mit `zstd -t` +- **Kopieren via rsync über SSH** (inkl. Prüfsummen-Abgleich) +- **Fortsetzen abgebrochener Kopien** durch `rsync --partial` +- **Automatisches Löschen fehlerhafter Backups** (`*_CORRUPT.zst`) +- **Aufräumen alter Backups** (nur die letzten *n* Backups pro VM/LXC werden behalten) +- **Automatische Besitzeranpassung** am Zielsystem (`chown`) +- **Benachrichtigung über ntfy** (optional) + +--- + +## ⚙️ Ablauf + +1. Neue Backups im lokalen Proxmox-Backup-Verzeichnis werden erkannt. +2. Konsistenzprüfung mit `zstd -t`. + - Fehlerhafte Backups werden als `*_CORRUPT` markiert und bleiben lokal. +3. Erfolgreiche Backups werden per `rsync` nach `$SSH_DEST` kopiert. + - Abgebrochene Übertragungen werden fortgesetzt. +4. Nach erfolgreicher Prüfsummen-Überprüfung wird das lokale Backup gelöscht. +5. Besitzer am Zielserver wird auf `$SSH_BACKUP_OWNER` gesetzt. +6. Ältere Backups werden nach Vorgabe (`BACKUPS_TO_KEEP`) entfernt. + +--- + +## 🔑 SSH-Setup + +Damit das Skript ohne Passwortabfrage läuft, muss ein SSH-Key erstellt und auf den Zielserver kopiert werden. +Auf dem Proxmox Server: + +```bash +ssh-keygen -t ed25519 -f ~/.ssh/proxmox # Passwort leer lassen +ssh-copy-id -i ~/.ssh/proxmox.pub root@192.168.178.100 +``` + +*(IP anpassen, Passwort des Zielservers eingeben)* + +--- + +## 📅 Automatischer Aufruf (Cron) + +Das Skript lässt nur eine Instanz gleichzeitig zu und kann daher gefahrlos regelmäßig per **cron** gestartet werden, z. B.: + +Alle 6 Stunden: +```cron +0 */6 * * * /root/shell_proxmox_backup_copy.sh >> /var/log/backup_cron.log 2>&1 +``` + +Oder täglich um 02:30 Uhr: +```cron +30 2 * * * /root/shell_proxmox_backup_copy.sh >> /var/log/backup_cron.log 2>&1 +``` + +--- + +## 🛠️ Konfiguration + +Im Skript können die wichtigsten Variablen angepasst werden: + +| Variable | Beschreibung | +|--------------------|--------------| +| `PROX_BACKUP_DIR` | Lokales Proxmox-Backup-Verzeichnis (z. B. `/var/lib/vz/dump`) | +| `SSH_USER` | SSH-Benutzer für die Verbindung zum NAS | +| `SSH_BACKUP_OWNER` | Besitzer der Backups am Zielsystem (z. B. `thorsten`) | +| `SSH_HOST` | IP oder Hostname des Zielservers | +| `SSH_DEST` | Zielpfad am NAS (muss per SSH erreichbar sein) | +| `LOGFILE` | Pfad zur Logdatei. Nur Pfade oder Mounts auf Proxmox möglich. Kein SSH Pfad!| +| `SSH_KEY` | Pfad zum SSH-Key | +| `BACKUPS_TO_KEEP` | Anzahl der neuesten Backups, die behalten werden | + +--- + +## 🔔 Benachrichtigungen (ntfy) + +Optional kann eine Benachrichtigung per [ntfy](https://ntfy.sh) aktiviert werden. +Dazu die folgenden Variablen im Skript anpassen: + +| Variable | Beschreibung | +|----------------|--------------| +| `NTFY_USE` | `true` = aktivieren, `false` = deaktivieren | +| `NTFY_TOPIC` | ntfy Topic-Name (z. B. `Proxmox`) | +| `NTFY_SERVER` | Serveradresse (z. B. `192.168.178.25:5885`) | +| `NTFY_AUTH` | Authentifizierungstoken (z. B. `Bearer `) | +| `NTFY_TITLE` | Titel der Nachricht | +| `NTFY_ICON` | Optionales Icon | + +--- + +## 📋 Rsync + +Das Script verwendet rsync zum kopieren der Backups. Bei manchen NAS muss rsync expliziet aktiviert werden. + +| NAS | Link | +|----------------|--------------| +|**Synology** | https://kb.synology.com/de-de/DSM/help/DSM/AdminCenter/file_rsync?version=7 +|**QNAS** | https://www.qnap.com/de-de/how-to/faq/article/wie-kann-ich-daten-von-einem-synology-nas-%C3%BCber-rsync-auf-einem-qnap-nas-sichern + +--- + +## ✅ Beispielausgabe + +```text +2025-09-01 12:00:00 [INFO] Starte das Übertragen von Backups +2025-09-01 12:00:01 [INFO] Verarbeite Backup vm-101-disk-2025_09_01-00_00_00.vma.zst +2025-09-01 12:00:20 [INFO] Kopieren abgeschlossen. Checksumme wird überprüft... +2025-09-01 12:00:21 [INFO] Checksumme stimmt. +2025-09-01 12:00:22 [INFO] Lösche Backup vm-101-disk-2025_09_01-00_00_00 auf Proxmox... +2025-09-01 12:00:23 [INFO] Abgeschlossen! +``` diff --git a/shell_proxmox_backup_copy.env b/shell_proxmox_backup_copy.env new file mode 100644 index 0000000..5976c78 --- /dev/null +++ b/shell_proxmox_backup_copy.env @@ -0,0 +1,16 @@ +# ---------------------------------- Konfiguration ----------------------------------- +PROX_BACKUP_DIR="/var/lib/vz/dump" # Proxmox Verzeichnis in dem Lokale Backups landen +SSH_USER="root" # Benutzer für die SSH Verbindung zum NAS +SSH_BACKUP_OWNER="thorsten" # Auf diesen Benutzer werden die Backups am Zielort übertragen falls er von SSH_USER abweicht (chown) +SSH_HOST="192.168.178.100" # IP des NAS Servers +SSH_DEST="/mnt/user/Home Server/Volumes/Proxmox/dump" # Zielpfad / Ein per ssh erreichbarer Pfad auf dem NAS, kein lokaler Pfad auf Proxmox +LOGFILE="/mnt/unRAID/Home Server/Volumes/Proxmox/dump/_shell_proxmox_backup_copy.log" # Logfile (Muss ein lokaler Pfad auf Proxmox sein!) +SSH_KEY="$HOME/.ssh/proxmox" # Pfad zum SSH Key +BACKUPS_TO_KEEP=3 # Anzahl der neuesten Backups, die behalten werden +# -------------------------------- Benachrichtigung ---------------------------------- +NTFY_USE=true +NTFY_TOPIC="Proxmox" +NTFY_SERVER="192.168.178.25:5885" +NTFY_AUTH="Bearer tk_jmt0ucnmephixst6p9tleb5klf0ra" +NTFY_TITLE="Backup Übertragung" +NTFY_ICON="" \ No newline at end of file diff --git a/shell_proxmox_backup_copy.sh b/shell_proxmox_backup_copy.sh new file mode 100644 index 0000000..512f7d1 --- /dev/null +++ b/shell_proxmox_backup_copy.sh @@ -0,0 +1,367 @@ +#!/bin/bash +# +# shell_proxmox_backup_copy.sh +# +# ----------------------------------- Beschreibung ----------------------------------- +# Automatisches Kopieren von lokalen Proxmox-Backups nach zu einem Netzlaufwerk via rsync über SSH. +# Dadurch wird sichergestellt, dass die Backups verlustfrei übertragen werden. +# Im Gegensatz zu einer einfachen Samba-Übertragung prüft rsync die Integrität +# und setzt abgebrochene Kopien zuverlässig fort +# +# -------------------------------------- Ablauf -------------------------------------- +# Wird ein neues Backup im lokalen Proxmox-Verzeichnis (PROX_BACKUP_DIR) gefunden, wird: +# 1. Das Backup mit „zstd -t“ auf Konsistenz geprüft. +# Inkonsistente Backups werden als CORRUPT markiert und verbleiben im PROX_BACKUP_DIR. +# 2. Das Backup per rsync über SSH nach SSH_DEST kopiert. +# Unvollständige Dateien werden automatisch fortgesetzt. +# 3. Die Quelldatei wird gelöscht, wenn die Prüfsumme mit der am Zielort übereinstimmt. +# Andernfalls bleibt sie bestehen und wird beim nächsten Durchlauf erneut kopiert/fortgesetzt. +# 4. Da die Backups auf ggf. mit einem anderen Benutzer übertragen wurden wird der Eigentümer nach +# SSH_BACKUP_OWNER geändert. (Bei unRAID z.B. hat in der Regel nur root SSH Rechte) +# 5. Entsprechend der Vorgabe BACKUPS_TO_KEEP werden im Quellverzeichnis die ältesten +# Backups der jeweiligen VM/LXC gelöscht. +# +# -------------------------------------- SSH-Key ------------------------------------- +# Damit das Skript ohne Passwortabfrage läuft, muss ein SSH-Key erstellt werden: +# ssh-keygen -t ed25519 -f ~/.ssh/proxmox (Passwort leer lassen) +# ssh-copy-id -i ~/.ssh/proxmox.pub root@192.168.178.100 +# (IP von NAS ggf. anpassen, Passwort des root-Users eingeben) +# +# --------------------------------------- rsync -------------------------------------- +# Das Script verwendet rsync zum kopieren der Backups. Bei manchen NAS muss rsync expliziet +# aktiviert werden. +# Synology: https://kb.synology.com/de-de/DSM/help/DSM/AdminCenter/file_rsync?version=7 +# +# +# -------------------------------------- Aufruf -------------------------------------- +# Das Skript lässt nur eine Instanz gleichzeitig zu. Es kann daher gefahrlos über cron +# regelmäßig gestartet werden, z. B. alle 6 Stunden: +# 0 */6 * * * /root/shell_proxmox_backup_copy.sh >> /var/log/backup_cron.log 2>&1 +# oder um 02:00 Uhr +# 0 2 * * * /root/shell_proxmox_backup_copy.sh >> /var/log/backup_cron.log 2>&1 + + + + + +# Pfad zur .env-Datei dynamisch ermitteln +ENV_FILE="$(dirname "$(realpath "$0")")/$(basename "$0" .sh).env" + +# Prüfen, ob die .env-Datei existiert +if [[ ! -f "$ENV_FILE" ]]; then + exit 1 +fi + +# .env-Datei laden +source "$ENV_FILE" + + +# === Flag für Aufräumaktion === +CLEANUP_REQUIRED=false +# === Flag für Logging === +LOG_ALLOWED=false # Die Logdatei nur beschreiben wenn es wirklich neue Backups gibt + +# === Variablen für den Bericht === +BACKUPS_FOUND=0 +BACKUPS_COPIED=0 +BACKUPS_FAILED_CORRUPT=0 +BACKUPS_FAILED_RSYNC=0 +BACKUPS_FAILED_CHECKSUM=0 + + +# Farbdefinitionen +RED='\e[31m' +GREEN='\e[32m' +YELLOW='\e[33m' +BLUE='\e[34m' +NC='\e[0m' # No Color + +# === Logging Funktion === + +log() { + local ts="$(date '+%F %T')" + local msg="$1" + + # Konsolenausgabe: ts grau hinterlegt, msg normal + echo -e "\e[100;97m $ts \e[0m $msg" + + # Logfile ohne ANSI-Codes, nur wenn LOG_ALLOWED=true + if [ "$LOG_ALLOWED" = true ]; then + local clean_msg + clean_msg="$(echo -e "$msg" | sed -r 's/\x1B\[[0-9;]*[A-Za-z]//g')" + echo "$ts $clean_msg" >> "$LOGFILE" + fi +} + +log_info() { log "${GREEN}[INFO]${NC} $1"; } +log_warn() { log "${YELLOW}[WARN]${NC} $1"; } +log_error() { log "${RED}[ERROR]${NC} $1"; } +log_debug() { log "${BLUE}[DEBUG]${NC} $1"; } + + +# Grau hinterlegte Logs (z.B. Start/Ende) +log_highlight() { + local ts="$(date '+%F %T')" + # Konsole farbig + echo -e "\e[100;97m $ts \e[0m \e[100;97m $1 \e[0m" + + # Logfile nur wenn LOG_ALLOWED=true + if [ "$LOG_ALLOWED" = true ]; then + echo "$ts $1" >> "$LOGFILE" + fi +} + +log_logfile_only() { + local ts="$(date '+%F %T')" + echo "$ts $1" >> "$LOGFILE" +} + +# === Start-Info (immer Konsole, nie Logfile) === +echo -e "\e[100;97m $(date '+%F %T') ──────────────────────────────────────────────────────────────── \e[0m" +echo -e "\e[100;97m $(date '+%F %T') \e[0m \e[32m[INFO]\e[0m Starte das Übertragen von Backups" + + + + +cleanup_old_backups() { + log_info "Starte Aufräumen alter Backups im Zielverzeichnis, max $BACKUPS_TO_KEEP Backups pro VM/LXC behalten..." + + # Finde nur die echten Backup-Dateien. Die -not -name "*.notes" Anweisung filtert die .zst.notes Dateien aus. + find_output=$($SSH_CMD "$SSH_USER@$SSH_HOST" "find \"$SSH_DEST\" -maxdepth 1 \( -name \"*.vma.zst\" -o -name \"*.tar.zst\" \) -not -name \"*.notes\"" || true) + + if [[ -z "$find_output" ]]; then + log_info "Keine Backups zum Aufräumen gefunden." + return + fi + + + # Gruppiere die Backups nach ihrem Prefix (VM/LXC-ID). + declare -A BACKUP_GROUPS + while IFS= read -r f; do + FILE_BASENAME=$(basename "$f") + PREFIX=$(echo "$FILE_BASENAME" | sed -E 's/-(20[0-9]{2}_[0-9]{2}_[0-9]{2}-[0-9]{2}_[0-9]{2}_[0-9]{2})\.(vma|tar)\.zst$//') + BACKUP_GROUPS["$PREFIX"]+="$f"$'\n' + done <<< "$find_output" + + # Durchsuche das Array nach leeren Einträgen und lösche sie. + for PREFIX in "${!BACKUP_GROUPS[@]}"; do + BACKUP_GROUPS["$PREFIX"]=$(echo -e "${BACKUP_GROUPS[$PREFIX]}" | grep -v '^$' || true) + done + + + for PREFIX in "${!BACKUP_GROUPS[@]}"; do + readarray -t FILES_ZST < <(echo -e "${BACKUP_GROUPS[$PREFIX]}" | sort -V) + COUNT=${#FILES_ZST[@]} + + log_info "Liste der gefundenen Backups für '$PREFIX':" + for file_path in "${FILES_ZST[@]}"; do + log_info " - $file_path" + done + + log_info "Gefundene $COUNT Backups für VM/LXC '$PREFIX'." + + if [[ $COUNT -le $BACKUPS_TO_KEEP ]]; then + log_info "Kein Löschen notwendig für '$PREFIX', nur $COUNT Backups gefunden (max $BACKUPS_TO_KEEP)." + continue + fi + + FILES_TO_DELETE=("${FILES_ZST[@]:0:$((COUNT - BACKUPS_TO_KEEP))}") + + log_info "Lösche $((${#FILES_TO_DELETE[@]})) älteste Backups für '$PREFIX'..." + + for f in "${FILES_TO_DELETE[@]}"; do + base_filename=$(basename "$f") + # Entfernt sowohl .tar.zst als auch .vma.zst, um den korrekten Basisnamen zu erhalten + base_name=${base_filename%.vma.zst} + base_name=${base_name%.tar.zst} + + #log_debug "DEBUG: Korrekter Basis-Name für die Löschung: $base_name" + + $SSH_CMD "$SSH_USER@$SSH_HOST" "rm -f \"$SSH_DEST/$base_name\"*" + + log_info "Lösche Backup: $base_name und zugehörige Dateien." + done + done + log_info "Aufräumen alter Backups abgeschlossen." +} + + +# Pfad zur PID-Datei +PIDFILE="/tmp/backup_cleanup.pid" + +# Prüfe, ob die PID-Datei existiert +if [ -f "$PIDFILE" ]; then + # Wenn ja, lies die PID + PID=$(cat "$PIDFILE") + # Prüfe, ob der Prozess mit dieser PID noch läuft + if ps -p "$PID" > /dev/null; then # HIER WURDE "$PID" KORRIGIERT + log_warn "Skript läuft bereits mit PID $PID. Beende." + exit 1 + else + log_info "Veraltete PID-Datei gefunden, lösche sie..." + rm "$PIDFILE" + fi +fi + +# Erstelle die neue PID-Datei mit der aktuellen PID +echo $$ > "$PIDFILE" + +# Stelle sicher, dass die PID-Datei bei normalem Beenden oder Fehler gelöscht wird +trap "rm -f \"$PIDFILE\"" EXIT + + + + +# SSH-Befehl mit Key +SSH_CMD="ssh -i $SSH_KEY -o StrictHostKeyChecking=no" + +# === Alle Backups im lokalen Verzeichnis (LXC: *.tar.zst, QEMU: *.vma.zst) === +for BACKUP_FILE in "$PROX_BACKUP_DIR"/*.zst; do + [[ -e "$BACKUP_FILE" ]] || continue + + # Wir haben eine Datei gefunden, also erhöhen wir den Zähler + BACKUPS_FOUND=$((BACKUPS_FOUND+1)) + + + # Wenn wir hier ankommen, heißt das: mindestens eine Backup-Datei liegt vor + if [ "$LOG_ALLOWED" = false ]; then + LOG_ALLOWED=true + # Jetzt erst ins Logfile schreiben + log_logfile_only "────────────────────────────────────────────────────────────────" + log_logfile_only "[INFO] Starte das Übertragen von Backups" + fi + + + + # Defekte Backups überspringen + if [[ "$BACKUP_FILE" == *_CORRUPT.zst ]]; then + log_warn "Überspringe defektes Backup: $(basename "$BACKUP_FILE")" + BACKUPS_FAILED_CORRUPT=$((BACKUPS_FAILED_CORRUPT+1)) + continue + fi + + # Dateiendung entfernen (.vma.zst oder .tar.zst) + BASENAME=$(basename "$BACKUP_FILE" | sed -E 's/\.(vma|tar)\.zst$//') + FILES_TO_COPY=("$PROX_BACKUP_DIR/$BASENAME".*) + + log_info "Verarbeite Backup $BASENAME" + + # Prüfen, ob Backup fertig ist (Dateigröße stabil) + log_info "Überprüfe ob das Backup in Bearbeitung ist..." + SIZE1=$(stat -c %s "$BACKUP_FILE") + sleep 10 + SIZE2=$(stat -c %s "$BACKUP_FILE") + if [[ "$SIZE1" != "$SIZE2" ]]; then + log_info "Backup noch in Bearbeitung, überspringe vorerst: $BASENAME" + continue + fi + + # zstd Test + log_info "Backup wird überprüft (zstd -t $BACKUP_FILE)" + zstd -t "$BACKUP_FILE" + if [[ $? -ne 0 ]]; then + log_error "Backup defekt, wird nicht kopiert: $BASENAME" + BACKUPS_FAILED_CORRUPT=$((BACKUPS_FAILED_CORRUPT+1)) + # Alle Dateien der Gruppe umbenennen mit _CORRUPT + for f in "${FILES_TO_COPY[@]}"; do + mv "$f" "${f}_CORRUPT" + done + log_info "Backup umbenannt: ${BASENAME}_CORRUPT" + continue + fi + + # Zielverzeichnis auf dem NAS erstellen + $SSH_CMD "$SSH_USER@$SSH_HOST" "mkdir -p \"$SSH_DEST\"" + + # Exakt denselben Dateinamen wie lokal verwenden (egal ob .tar.zst oder .vma.zst) + REMOTE_FILE="$SSH_DEST/$(basename "$BACKUP_FILE")" + + log_info "Starte Kopie zum Ziel mit rsync" + rsync -avh --progress --partial --checksum -e "$SSH_CMD" "${FILES_TO_COPY[@]}" "$SSH_USER@$SSH_HOST:$SSH_DEST" + RSYNC_STATUS=$? + if [[ $RSYNC_STATUS -ne 0 ]]; then + log_error "rsync Fehlercode $RSYNC_STATUS für $BASENAME" + BACKUPS_FAILED_RSYNC=$((BACKUPS_FAILED_RSYNC+1)) + continue + fi + + # Prüfen Checksumme der .zst auf Ziel (gleicher Dateiname wie lokal) + log_info "Kopieren abgeschlossen. Checksumme wird überprüft..." + LOCAL_SUM=$(sha256sum "$BACKUP_FILE" | awk '{print $1}') + REMOTE_SUM=$($SSH_CMD "$SSH_USER@$SSH_HOST" "sha256sum \"$REMOTE_FILE\"" | awk '{print $1}') + if [[ "$LOCAL_SUM" == "$REMOTE_SUM" ]]; then + log_info "Checksumme stimmt." + + # Eigentümer ggf. anpassen + if $SSH_USERS <> $SSH_BACKUP_OWNER; then + log_info "Setze Besitzer auf $SSH_BACKUP_OWNER für Backup $BASENAME" + $SSH_CMD "$SSH_USER@$SSH_HOST" "chown $SSH_BACKUP_OWNER \"$SSH_DEST/$BASENAME\".*" + fi + + # === Flag setzen, da Kopie erfolgreich war === + CLEANUP_REQUIRED=true + BACKUPS_COPIED=$((BACKUPS_COPIED+1)) + + # Erfolgreiche Backup auf Proxmox löschen + log_info "Lösche Backup $BASENAME auf Proxmox..." + rm -f "${FILES_TO_COPY[@]}" + else + log_error "Checksumme stimmt nicht überein! Backup wird nicht gelöscht." + BACKUPS_FAILED_CHECKSUM=$((BACKUPS_FAILED_CHECKSUM+1)) + continue + fi +done + + +# === Bedingter Aufruf der Aufräumfunktion === +if $CLEANUP_REQUIRED; then + cleanup_old_backups +else + log_info "Keine neuen Backups übertragen. Aufräumen wird übersprungen." +fi + +# === Ende-Log === +log_info "Abgeschlossen!" +log_highlight "────────────────────────────────────────────────────────────────" + + +# === NEU: ntfy Push Benachrichtigung === +if [[ "$BACKUPS_FOUND" -gt 0 && "$NTFY_USE" == "true" ]]; then + + log_info "NTFY Zusammenfassung wird gesendet" + + # Setze Priorität auf "high", wenn es Fehler gab + NTFY_PRIORITY="default" + NTFY_TAGS="heavy_check_mark" + MESSAGE_BODY="Neu Proxmox Backups wurden auf Fehler überprüft und erfolgreich aufs NAS Server übetragen.\n\n" + if [ "$BACKUPS_FAILED_CORRUPT" -gt 0 ] || [ "$BACKUPS_FAILED_RSYNC" -gt 0 ] || [ "$BACKUPS_FAILED_CHECKSUM" -gt 0 ]; then + MESSAGE_BODY="Es gibt Fehlerhafte Backups!\n\n" + NTFY_PRIORITY="high" + NTFY_TAGS="skull" + fi + + # Nachrichtentext für den PUT-Befehl + MESSAGE_BODY+="${BACKUPS_FOUND} gefundene Backups\n" + MESSAGE_BODY+="${BACKUPS_COPIED} erfolgreich kopiert\n" + + if [ "$BACKUPS_FAILED_CORRUPT" -gt 0 ]; then + MESSAGE_BODY+="${BACKUPS_FAILED_CORRUPT} Fehlerhaft (zstd)\n" + fi + if [ "$BACKUPS_FAILED_RSYNC" -gt 0 ]; then + MESSAGE_BODY+="${BACKUPS_FAILED_RSYNC} Fehlerhaft (rsync)\n" + fi + if [ "$BACKUPS_FAILED_CHECKSUM" -gt 0 ]; then + MESSAGE_BODY+="${BACKUPS_FAILED_CHECKSUM} Fehlerhaft (Checksumme)\n" + fi + + # Hier wird der Nachrichtentext direkt an den curl-Befehl übergeben + # Dies ist der einfachste Weg, um Zeilenumbrüche zu gewährleisten + echo -e "$MESSAGE_BODY" | curl -s -X POST \ + -H "Authorization: $NTFY_AUTH" \ + -H "X-Priority: $NTFY_PRIORITY" \ + -H "X-Title: $NTFY_TITLE" \ + -H "X-Tags: $NTFY_TAGS" \ + -H "X-Icon: $NTFY_ICON" \ + --data-binary @- \ + "http://$NTFY_SERVER/$NTFY_TOPIC" +fi \ No newline at end of file