Files
seiche/scripts/backup.sh
T

53 lines
1.7 KiB
Bash
Executable File

#!/usr/bin/env bash
# Seiche — sauvegarde SQLite (en ligne, cohérente, sans arrêter le serveur).
#
# Usage :
# ./scripts/backup.sh [répertoire_de_sortie] # défaut : ./backups
#
# Prérequis : le conteneur `seiche` tourne (docker compose up -d).
#
# Principe :
# 1. `VACUUM INTO` dans le conteneur (snapshot cohérent garanti par SQLite,
# le serveur continue de servir pendant l'opération),
# 2. copie HORS du volume (un backup dans le même volume n'est pas un backup),
# 3. nettoyage du fichier temporaire dans le volume.
set -euo pipefail
cd "$(dirname "$0")/.."
OUT_DIR="${1:-backups}"
mkdir -p "$OUT_DIR"
TS="$(date +%Y%m%d-%H%M%S)"
HOST_FILE="$OUT_DIR/seiche-$TS.db"
TMP_IN_CONTAINER="/data/.seiche-backup-tmp.db"
# 1. Snapshot cohérente dans le conteneur (libsql = dépendance du serveur,
# résolu depuis le WORKDIR /app/server ; l'exécution est en CJS stdin,
# d'où l'import dynamique).
docker compose exec -T seiche node <<'NODE'
import("@libsql/client").then(async (m) => {
const c = m.createClient({ url: "file:/data/seiche.db" });
await c.execute("VACUUM INTO '/data/.seiche-backup-tmp.db'");
await c.close();
}).catch((e) => {
console.error(e);
process.exit(1);
});
NODE
# 2. Copie hors du volume.
docker compose cp "seiche:$TMP_IN_CONTAINER" "$HOST_FILE"
# 3. Nettoyage dans le volume.
docker compose exec -T seiche rm -f "$TMP_IN_CONTAINER"
# 4. Vérification minimale : en-tête SQLite + fichier non vide.
MAGIC="$(head -c 15 "$HOST_FILE" 2>/dev/null || true)"
if [ "$MAGIC" != "SQLite format 3" ] || [ ! -s "$HOST_FILE" ]; then
echo "ERREUR : backup invalide ($HOST_FILE), suppression." >&2
rm -f "$HOST_FILE"
exit 1
fi
echo "OK : $HOST_FILE ($(du -h "$HOST_FILE" | cut -f1))"