Files
oliver af2fe940e7 hmm
2026-06-22 06:38:27 -03:00

119 lines
4.3 KiB
Bash
Executable File

#!/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 "Retention: daily 14d, weekly 12w"
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"
# --- Backup retention ---
# Policy:
# - Keep all backups from the last 14 days (daily tier)
# - For backups older than 14 days but within 3 months (12 weeks),
# keep one per ISO week (the newest in each week)
# - Delete everything else
BACKUP_DAYS_DAILY=14
BACKUP_WEEKS_WEEKLY=12 # ~3 months
# Map of kept files (absolute path -> 1)
declare -A kept_files
# Track which ISO weeks already have a kept weekly backup
declare -A weekly_kept
now_ts=$(date +%s)
while IFS= read -r file; do
filename=$(basename "$file")
filedate="${filename:0:8}" # YYYYMMDD from filename
# BusyBox date (Alpine) requires YYYY-MM-DD; compact YYYYMMDD is rejected
filedate_fmt="${filedate:0:4}-${filedate:4:2}-${filedate:6:2}"
file_ts=$(date -d "$filedate_fmt" +%s 2>/dev/null) || continue
age_days=$(( (now_ts - file_ts) / 86400 ))
if [ "$age_days" -le "$BACKUP_DAYS_DAILY" ]; then
# --- Daily tier: keep everything within the last 14 days ---
kept_files["$file"]=1
elif [ "$age_days" -le $((BACKUP_DAYS_DAILY + BACKUP_WEEKS_WEEKLY * 7)) ]; then
# --- Weekly tier: keep 1 per ISO week ---
week_key=$(date -d "$filedate_fmt" +%G%V) # ISO year + week number
if [ -z "${weekly_kept[$week_key]-}" ]; then
weekly_kept[$week_key]=1
kept_files["$file"]=1
fi
fi
done < <(ls -t "$BACKUP_DIR"/[0-9]*.zip 2>/dev/null)
# Delete any backup not in the keep map
for file in "$BACKUP_DIR"/[0-9]*.zip; do
[ -f "$file" ] || continue
if [ -z "${kept_files[$file]-}" ]; then
log "Deleting old backup: $file"
doas rm -f "$file"
fi
done
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