53 lines
1.7 KiB
Bash
Executable File
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))"
|