backup/stages/sqlite-backup.sh

#!/usr/bin/env bash
# backup/stages/sqlite-backup.sh
# Uses the `.backup` command so it is safe against concurrent writers.

set -euo pipefail

HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=/dev/null
. "${HERE}/../../scripts/lib/log.sh"

DEST_ROOT="${DEST_ROOT:-/srv/homelab/backup/snapshots/sqlite}"
LATEST="${LATEST:-/srv/homelab/backup/latest/sqlite}"
TS="$(date +%Y%m%dT%H%M%S)"
DEST="${DEST_ROOT}/${TS}"

# Pairs of "label:path"
DBS=(
    "grafana:/srv/homelab/stacks/monitoring/grafana/grafana.db"
    "authelia:/srv/homelab/stacks/auth/authelia/db.sqlite3"
    "lldap:/srv/homelab/stacks/auth/lldap/users.db"
    "bazarr:/srv/homelab/stacks/media/bazarr/db/bazarr.db"
    "jellyfin:/srv/homelab/stacks/media/jellyfin/data/jellyfin.db"
)

backup_one() {
    local label="$1" src="$2"
    if [[ ! -f "${src}" ]]; then
        log_info "skip ${label} (missing ${src})"
        return 0
    fi
    local tmp="${DEST}/${label}.sqlite"
    if sqlite3 "${src}" ".backup '${tmp}'"; then
        zstd -q -T0 -3 --rm "${tmp}"
        log_info "backed up ${label}"
    else
        log_err "backup failed for ${label}"
        return 1
    fi
}

main() {
    if ! command -v sqlite3 >/dev/null 2>&1; then
        log_err "sqlite3 not installed"
        exit 2
    fi
    install -d "${DEST}"
    local failed=0
    for pair in "${DBS[@]}"; do
        backup_one "${pair%%:*}" "${pair#*:}" || failed=$((failed + 1))
    done
    if (( failed > 0 )); then
        exit 1
    fi
    install -d "$(dirname "${LATEST}")"
    rm -f "${LATEST}"
    ln -s "${DEST}" "${LATEST}"
}

main "$@"