feat: backup_trilium.sh - backup local automatise de la base
Etage 1 de la strategie de backup (reco n1 de l audit). Copie coherente via ETAPI (jamais de SQLite a chaud - le WAL faisait 4 Mo), sortie du dossier Docker, verification d integrite avec suppression si KO, rotation 30, chmod 600. Tache DSM quotidienne 03h00 en root (dossier backup en 700).
This commit is contained in:
@@ -0,0 +1,55 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# backup_trilium.sh - Backup local horodate de la base Trilium (etage 1).
|
||||||
|
# Execute en ROOT par le planificateur DSM (le dossier backup/ est en 700 uid 1000).
|
||||||
|
# 1) demande a Trilium une copie coherente via ETAPI (jamais de copie SQLite a chaud)
|
||||||
|
# 2) la sort du dossier Docker vers /volume1/backups/trilium, horodatee
|
||||||
|
# 3) verifie l integrite du fichier copie
|
||||||
|
# 4) garde les 30 derniers
|
||||||
|
# Sortie : "OK ..." ou "ERREUR ..." (pour alerte mail DSM).
|
||||||
|
|
||||||
|
BASE=/volume1/homes/Master/App/Context_continuity
|
||||||
|
PY=$BASE/venv/bin/python3
|
||||||
|
SRC=/volume1/docker/trilium/backup/backup-quotidien.db
|
||||||
|
DEST=/volume1/backups/trilium
|
||||||
|
URL=http://localhost:4292
|
||||||
|
GARDER=30
|
||||||
|
|
||||||
|
STAMP=$(date +%Y%m%d_%H%M)
|
||||||
|
CIBLE=$DEST/trilium_$STAMP.db
|
||||||
|
|
||||||
|
TOKEN=$(grep '^TRILIUM_TOKEN=' $BASE/.env | cut -d= -f2- | tr -d '"' | tr -d "'")
|
||||||
|
if [ -z "$TOKEN" ]; then echo "ERREUR token ETAPI introuvable dans $BASE/.env"; exit 1; fi
|
||||||
|
|
||||||
|
mkdir -p $DEST
|
||||||
|
|
||||||
|
# 1. Demander a Trilium de regenerer la copie (nom dedie : n ecrase pas daily/weekly/monthly)
|
||||||
|
CODE=$(curl -s -o /dev/null -w "%{http_code}" -X PUT "$URL/etapi/backup/quotidien" -H "Authorization: $TOKEN" --max-time 60)
|
||||||
|
if [ "$CODE" != "204" ]; then echo "ERREUR ETAPI backup HTTP $CODE"; exit 1; fi
|
||||||
|
|
||||||
|
# 2. Verifier que le fichier existe et vient d etre ecrit, puis le sortir du dossier Docker
|
||||||
|
if [ ! -f "$SRC" ]; then echo "ERREUR fichier source absent : $SRC"; exit 1; fi
|
||||||
|
find "$SRC" -mmin -5 | grep -q . || { echo "ERREUR source non regeneree (plus de 5 min)"; exit 1; }
|
||||||
|
cp "$SRC" "$CIBLE" || { echo "ERREUR copie vers $CIBLE"; exit 1; }
|
||||||
|
|
||||||
|
# 3. Verifier l integrite de la COPIE (un backup non verifie n est pas un backup)
|
||||||
|
INTEG=$($PY -c "import sqlite3,sys
|
||||||
|
try:
|
||||||
|
c=sqlite3.connect('$CIBLE')
|
||||||
|
r=c.execute('PRAGMA integrity_check').fetchone()[0]
|
||||||
|
n=c.execute('SELECT count(*) FROM notes').fetchone()[0]
|
||||||
|
c.close()
|
||||||
|
print('%s|%d' % (r,n))
|
||||||
|
except Exception as e:
|
||||||
|
print('erreur|%s' % e)")
|
||||||
|
RESU=$(echo "$INTEG" | cut -d'|' -f1)
|
||||||
|
NBNOTES=$(echo "$INTEG" | cut -d'|' -f2)
|
||||||
|
if [ "$RESU" != "ok" ]; then rm -f "$CIBLE"; echo "ERREUR integrite KO ($INTEG) - copie supprimee"; exit 1; fi
|
||||||
|
|
||||||
|
# 4. Rotation : garder les N plus recents
|
||||||
|
cd $DEST || exit 1
|
||||||
|
ls -1t trilium_*.db 2>/dev/null | tail -n +$((GARDER+1)) | while read f; do rm -f "$f"; done
|
||||||
|
|
||||||
|
chmod 600 "$CIBLE"
|
||||||
|
TAILLE=$(du -h "$CIBLE" | cut -f1)
|
||||||
|
RESTANT=$(ls -1 trilium_*.db 2>/dev/null | wc -l)
|
||||||
|
echo "OK $CIBLE ($TAILLE, $NBNOTES notes, $RESTANT backups conserves)"
|
||||||
Reference in New Issue
Block a user