diff --git a/.gitignore b/.gitignore index 318f71a..9a02762 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -alpine.qcow2 -tmp/ -exchange/ -backup/ +/alpine.qcow2 +/tmp/ +/exchange/ +/backup/ diff --git a/app/sbin/backup/N8N b/app/sbin/backup/N8N new file mode 100755 index 0000000..ce203b4 --- /dev/null +++ b/app/sbin/backup/N8N @@ -0,0 +1,18 @@ +#!/bin/bash + +set -euo pipefail # Fail on error, undefined variables, and pipe errors + +# Create backup filename +FILENAME="$(date +"%Y%m%d_%H%M").zip" +BACKUP_DIR="/BACKUP/$UUID" +mkdir -p "$BACKUP_DIR" + +tar -czvf "$BACKUP_DIR/$FILENAME" -C "/4server/data" "$UUID" + +# Remove old backups beyond the configured slots +ls -t "$BACKUP_DIR"/[0-9]*.zip 2>/dev/null | tail -n +$((BACKUP_SLOTS + 1)) | while read -r file; do + echo "Deleting old backup: $file" + doas rm -f "$file" +done + + diff --git a/app/sbin/backup/ODOO b/app/sbin/backup/ODOO new file mode 100755 index 0000000..bb1cc7c --- /dev/null +++ b/app/sbin/backup/ODOO @@ -0,0 +1,80 @@ +#!/bin/bash +# Backup Odoo 19 database script +# Description: Dumps Odoo DB, manages backup rotation, and maintains a current.zip symlink + +set -euo pipefail # Fail on error, undefined variables, and pipe errors + +log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; } + +# Load helper functions +source /4server/sbin/helpers + +# Get contract info (also exports ODOO_DB_PASSWORD via helpers) +get_contract_info + +log "UUID: $UUID" +log "Backup slots: $BACKUP_SLOTS" +log "DB host: $POSTGRES_HOST" + +# Build paths +FILENAME="$(date +"%Y%m%d_%H%M").zip" +BACKUP_DIR="/BACKUP/$UUID" +BB_DIR="$BACKUP_DIR/bb" + +# Ensure backup directories exist (requires elevated permissions like other ops) +doas mkdir -p "$BACKUP_DIR" +doas mkdir -p "$BB_DIR" + +# Perform database dump inside the container +log "Starting dump -> $BACKUP_DIR/$FILENAME" +doas docker exec "$UUID" odoo db \ + --db_host "$POSTGRES_HOST" \ + -r "$UUID" \ + -w "$ODOO_DB_PASSWORD" \ + --data-dir /home/odoo/.local/share/Odoo/ \ + dump "$UUID" "/mnt/backup/$FILENAME" + +log "Dump complete: $(doas du -sh "$BACKUP_DIR/$FILENAME" | cut -f1)" + +# Secure timestamped backups (scoped glob — leaves current.zip symlink untouched) +doas chmod 666 "$BACKUP_DIR"/[0-9]*.zip + +# Hand ownership back to odoo inside the container +doas docker exec "$UUID" chown odoo:odoo -R /mnt/backup + +# Update the current.zip symlink inside bb/ pointing at the backup just created. +# Target is relative (../$FILENAME) because the symlink lives one level deeper +# than the actual .zip files, so it stays valid if the directory is moved. +doas ln -sf "../$FILENAME" "$BB_DIR/current.zip" +log "bb/current.zip -> ../$FILENAME" + +# Remove old backups beyond the configured slot count +# Process substitution (< <(...)) keeps the loop in the main shell so +# set -e / exit codes propagate correctly — unlike a pipe subshell. +while IFS= read -r old_file; do + log "Deleting old backup: $old_file" + doas rm -f "$old_file" +done < <(ls -t "$BACKUP_DIR"/[0-9]*.zip 2>/dev/null | tail -n +$((BACKUP_SLOTS + 3))) + +log "Backup finished successfully ." + +# --- Custom modules snapshot --- +# Archive /4server/data/$UUID/git-server/repos/local/custom/ into .current.tar.gz. +# Rebuilt only when at least one file inside is newer than the existing archive, +# so borg backup dedup sees an identical blob on unchanged runs. +CUSTOM_SRC="/4server/data/$UUID/git-server/repos/local/custom" +CUSTOM_ARCHIVE="$BB_DIR/current_modules.tar.gz" + +if [ ! -d "$CUSTOM_SRC" ]; then + log "Custom modules directory not found, skipping: $CUSTOM_SRC" +elif [ ! -f "$CUSTOM_ARCHIVE" ] || \ + doas find "$CUSTOM_SRC" -newer "$CUSTOM_ARCHIVE" -print -quit | grep -q .; then + log "Changes detected in custom modules — rebuilding current.tar.gz" + # Write to a temp file first; mv is atomic, so borg never sees a partial archive + doas tar -czf "${CUSTOM_ARCHIVE}.tmp" \ + -C "/4server/data/$UUID/git-server/repos/local" custom + doas mv "${CUSTOM_ARCHIVE}.tmp" "$CUSTOM_ARCHIVE" + log ".current.tar.gz updated: $(doas du -sh "$CUSTOM_ARCHIVE" | cut -f1)" +else + log "No changes in custom modules — current.tar.gz is up to date" +fi diff --git a/app/sbin/backupAll b/app/sbin/backupAll new file mode 100755 index 0000000..485a43c --- /dev/null +++ b/app/sbin/backupAll @@ -0,0 +1,18 @@ +#!/bin/bash +exec >> /4server/data/log/backupAll.log 2>&1 +echo "$(date '+%Y-%m-%d %H:%M') Starting backupAll" + +BIN_PATH="/4server/sbin" + +containers=$(docker ps --format '{{.Names}}' | grep -E '^[0-9]{3}-[0-9]{3}') + +if [[ -z "$containers" ]]; then + echo "No containers matching pattern NNN-NNN found." + exit 0 +fi + +echo "Found containers:" +while IFS= read -r container; do + echo " $container" + touch "/4server/data/$container/cc/backup" +done <<< "$containers" diff --git a/app/sbin/borgpush b/app/sbin/borgpush index 4bedf9f..c19b95f 100755 --- a/app/sbin/borgpush +++ b/app/sbin/borgpush @@ -8,7 +8,7 @@ export BORG_PASSPHRASE="Airbus12" HOSTNAME=$(hostname) DATE=$(date +%Y-%m-%d_%H-%M-%S) -ARCHIVE_NAME="${HOSTNAME}-${DATE}" +ARCHIVE_NAME="${DATE}-${HOSTNAME}" # Resolve symlinks externally and feed real paths to borg via --paths-from-stdin. # - First find: locates all current* symlinks under /BACKUP and resolves them with realpath. diff --git a/app/vault/host_vars.img b/app/vault/host_vars.img index ddf447b..10d4685 100644 Binary files a/app/vault/host_vars.img and b/app/vault/host_vars.img differ