chore: ajouter scripts de backup et restore pour migrations
All checks were successful
Symfony CD - Scheduled Deploy / 🚀 Deploy to Production (push) Has been skipped
All checks were successful
Symfony CD - Scheduled Deploy / 🚀 Deploy to Production (push) Has been skipped
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
206
bin/migrate-backup.sh
Executable file
206
bin/migrate-backup.sh
Executable file
@@ -0,0 +1,206 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ============================================================================
|
||||||
|
# migrate-backup.sh
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Crée une archive complète de l'application (BDD + fichiers + config) afin
|
||||||
|
# de migrer le CRM Ludikevent d'un serveur vers un autre.
|
||||||
|
#
|
||||||
|
# Contenu de l'archive :
|
||||||
|
# - dump PostgreSQL (pg_dump --format=custom)
|
||||||
|
# - dump Redis (RDB) si redis-cli est disponible
|
||||||
|
# - dossiers d'uploads : public/media, public/images, public/pdf, public/seo
|
||||||
|
# - var/storage, sauvegarde
|
||||||
|
# - fichiers de config : .env.local, google.json
|
||||||
|
# - manifeste (versions, date, hostname)
|
||||||
|
#
|
||||||
|
# Usage :
|
||||||
|
# ./bin/migrate-backup.sh # archive dans ./sauvegarde/
|
||||||
|
# ./bin/migrate-backup.sh -o /tmp/backup.tar.gz # chemin de sortie explicite
|
||||||
|
# ./bin/migrate-backup.sh --no-redis # ignorer Redis
|
||||||
|
# ./bin/migrate-backup.sh --docker # forcer l'utilisation de docker
|
||||||
|
# ============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# --- Couleurs ----------------------------------------------------------------
|
||||||
|
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[0;33m'; CYAN='\033[0;36m'; RESET='\033[0m'
|
||||||
|
log() { printf "${CYAN}[backup]${RESET} %s\n" "$*"; }
|
||||||
|
ok() { printf "${GREEN}[ ok ]${RESET} %s\n" "$*"; }
|
||||||
|
warn() { printf "${YELLOW}[warn ]${RESET} %s\n" "$*"; }
|
||||||
|
err() { printf "${RED}[error]${RESET} %s\n" "$*" >&2; }
|
||||||
|
|
||||||
|
# --- Args --------------------------------------------------------------------
|
||||||
|
PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
OUTPUT=""
|
||||||
|
SKIP_REDIS=0
|
||||||
|
FORCE_DOCKER=0
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-o|--output) OUTPUT="$2"; shift 2 ;;
|
||||||
|
--no-redis) SKIP_REDIS=1; shift ;;
|
||||||
|
--docker) FORCE_DOCKER=1; shift ;;
|
||||||
|
-h|--help)
|
||||||
|
sed -n '2,22p' "$0"; exit 0 ;;
|
||||||
|
*) err "Argument inconnu : $1"; exit 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
cd "$PROJECT_DIR"
|
||||||
|
|
||||||
|
TIMESTAMP="$(date +%Y-%m-%d_%H-%M-%S)"
|
||||||
|
HOSTNAME_SHORT="$(hostname -s 2>/dev/null || hostname)"
|
||||||
|
[[ -z "$OUTPUT" ]] && OUTPUT="$PROJECT_DIR/sauvegarde/migrate_${HOSTNAME_SHORT}_${TIMESTAMP}.tar.gz"
|
||||||
|
mkdir -p "$(dirname "$OUTPUT")"
|
||||||
|
|
||||||
|
WORK_DIR="$(mktemp -d -t crm-backup-XXXXXX)"
|
||||||
|
trap 'rm -rf "$WORK_DIR"' EXIT
|
||||||
|
|
||||||
|
log "Projet : $PROJECT_DIR"
|
||||||
|
log "Destination : $OUTPUT"
|
||||||
|
log "Workdir : $WORK_DIR"
|
||||||
|
|
||||||
|
# --- Lecture des variables d'environnement ----------------------------------
|
||||||
|
ENV_FILE=""
|
||||||
|
for f in .env.local .env; do
|
||||||
|
[[ -f "$PROJECT_DIR/$f" ]] && ENV_FILE="$PROJECT_DIR/$f" && break
|
||||||
|
done
|
||||||
|
[[ -z "$ENV_FILE" ]] && { err "Aucun fichier .env trouvé."; exit 1; }
|
||||||
|
log "Env source : $ENV_FILE"
|
||||||
|
|
||||||
|
# Récupère DATABASE_URL (dernière valeur définie, sans guillemets)
|
||||||
|
DATABASE_URL="$(grep -E '^DATABASE_URL=' "$ENV_FILE" | tail -n1 | cut -d= -f2- | sed -e 's/^"//' -e 's/"$//' -e "s/^'//" -e "s/'$//")"
|
||||||
|
[[ -z "$DATABASE_URL" ]] && { err "DATABASE_URL introuvable dans $ENV_FILE"; exit 1; }
|
||||||
|
|
||||||
|
# postgresql://user:pass@host:port/db?...
|
||||||
|
re='^postgres(ql)?://([^:]+):([^@]*)@([^:/]+)(:([0-9]+))?/([^?]+)'
|
||||||
|
if [[ ! "$DATABASE_URL" =~ $re ]]; then
|
||||||
|
err "DATABASE_URL non reconnue : $DATABASE_URL"; exit 1
|
||||||
|
fi
|
||||||
|
PG_USER="${BASH_REMATCH[2]}"
|
||||||
|
PG_PASS="${BASH_REMATCH[3]}"
|
||||||
|
PG_HOST="${BASH_REMATCH[4]}"
|
||||||
|
PG_PORT="${BASH_REMATCH[6]:-5432}"
|
||||||
|
PG_DB="${BASH_REMATCH[7]}"
|
||||||
|
|
||||||
|
log "PostgreSQL : $PG_USER@$PG_HOST:$PG_PORT/$PG_DB"
|
||||||
|
|
||||||
|
# --- Détection du mode (natif ou docker) ------------------------------------
|
||||||
|
USE_DOCKER=0
|
||||||
|
if [[ $FORCE_DOCKER -eq 1 ]]; then
|
||||||
|
USE_DOCKER=1
|
||||||
|
elif ! command -v pg_dump >/dev/null 2>&1; then
|
||||||
|
if command -v docker >/dev/null 2>&1 && docker ps --format '{{.Names}}' | grep -q '^crm_db$'; then
|
||||||
|
USE_DOCKER=1
|
||||||
|
warn "pg_dump non installé, fallback sur docker exec crm_db"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Dump PostgreSQL ---------------------------------------------------------
|
||||||
|
log "Dump PostgreSQL en cours…"
|
||||||
|
DUMP_FILE="$WORK_DIR/database.dump"
|
||||||
|
if [[ $USE_DOCKER -eq 1 ]]; then
|
||||||
|
docker exec -e PGPASSWORD="$PG_PASS" crm_db \
|
||||||
|
pg_dump -U "$PG_USER" -d "$PG_DB" --format=custom --no-owner --no-acl > "$DUMP_FILE"
|
||||||
|
else
|
||||||
|
PGPASSWORD="$PG_PASS" pg_dump \
|
||||||
|
-h "$PG_HOST" -p "$PG_PORT" -U "$PG_USER" -d "$PG_DB" \
|
||||||
|
--format=custom --no-owner --no-acl -f "$DUMP_FILE"
|
||||||
|
fi
|
||||||
|
DUMP_SIZE="$(du -h "$DUMP_FILE" | cut -f1)"
|
||||||
|
ok "Dump BDD : $DUMP_SIZE"
|
||||||
|
|
||||||
|
# --- Dump Redis (best effort) ------------------------------------------------
|
||||||
|
if [[ $SKIP_REDIS -eq 0 ]]; then
|
||||||
|
REDIS_DSN="$(grep -E '^REDIS_DSN=' "$ENV_FILE" | tail -n1 | cut -d= -f2- | sed -e 's/^"//' -e 's/"$//' -e "s/^'//" -e "s/'$//")"
|
||||||
|
if [[ -n "$REDIS_DSN" ]]; then
|
||||||
|
# redis://[password@]host[:port]
|
||||||
|
rre='^redis://(([^@]*)@)?([^:/]+)(:([0-9]+))?'
|
||||||
|
if [[ "$REDIS_DSN" =~ $rre ]]; then
|
||||||
|
R_PASS="${BASH_REMATCH[2]}"
|
||||||
|
R_HOST="${BASH_REMATCH[3]}"
|
||||||
|
R_PORT="${BASH_REMATCH[5]:-6379}"
|
||||||
|
REDIS_FILE="$WORK_DIR/redis.rdb"
|
||||||
|
log "Dump Redis ($R_HOST:$R_PORT)…"
|
||||||
|
if command -v redis-cli >/dev/null 2>&1; then
|
||||||
|
if [[ -n "$R_PASS" ]]; then
|
||||||
|
redis-cli -h "$R_HOST" -p "$R_PORT" -a "$R_PASS" --no-auth-warning --rdb "$REDIS_FILE" >/dev/null \
|
||||||
|
&& ok "Dump Redis : $(du -h "$REDIS_FILE" | cut -f1)" \
|
||||||
|
|| warn "Échec dump Redis (ignoré)"
|
||||||
|
else
|
||||||
|
redis-cli -h "$R_HOST" -p "$R_PORT" --rdb "$REDIS_FILE" >/dev/null \
|
||||||
|
&& ok "Dump Redis : $(du -h "$REDIS_FILE" | cut -f1)" \
|
||||||
|
|| warn "Échec dump Redis (ignoré)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "redis-cli absent, dump Redis ignoré"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Copie des fichiers (uploads + config) -----------------------------------
|
||||||
|
FILES_DIR="$WORK_DIR/files"
|
||||||
|
mkdir -p "$FILES_DIR"
|
||||||
|
|
||||||
|
PATHS=(
|
||||||
|
"public/media"
|
||||||
|
"public/images"
|
||||||
|
"public/pdf"
|
||||||
|
"public/seo"
|
||||||
|
"public/tmp-sign"
|
||||||
|
"var/storage"
|
||||||
|
"sauvegarde"
|
||||||
|
".env.local"
|
||||||
|
"google.json"
|
||||||
|
)
|
||||||
|
|
||||||
|
log "Archivage des fichiers…"
|
||||||
|
for p in "${PATHS[@]}"; do
|
||||||
|
if [[ -e "$PROJECT_DIR/$p" ]]; then
|
||||||
|
mkdir -p "$FILES_DIR/$(dirname "$p")"
|
||||||
|
cp -a "$PROJECT_DIR/$p" "$FILES_DIR/$p"
|
||||||
|
ok " + $p"
|
||||||
|
else
|
||||||
|
warn " - $p (absent)"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Évite de réembarquer l'archive courante si elle est dans sauvegarde/
|
||||||
|
if [[ "$OUTPUT" == "$PROJECT_DIR/sauvegarde/"* && -e "$FILES_DIR/sauvegarde/$(basename "$OUTPUT")" ]]; then
|
||||||
|
rm -f "$FILES_DIR/sauvegarde/$(basename "$OUTPUT")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Manifeste ---------------------------------------------------------------
|
||||||
|
GIT_COMMIT="$(git -C "$PROJECT_DIR" rev-parse HEAD 2>/dev/null || echo "n/a")"
|
||||||
|
GIT_BRANCH="$(git -C "$PROJECT_DIR" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "n/a")"
|
||||||
|
PHP_VER="$(php -r 'echo PHP_VERSION;' 2>/dev/null || echo "n/a")"
|
||||||
|
|
||||||
|
cat > "$WORK_DIR/MANIFEST.txt" <<EOF
|
||||||
|
Migration backup CRM Ludikevent
|
||||||
|
================================
|
||||||
|
Date : $(date -Iseconds)
|
||||||
|
Hostname : $(hostname)
|
||||||
|
Project dir : $PROJECT_DIR
|
||||||
|
Git branch : $GIT_BRANCH
|
||||||
|
Git commit : $GIT_COMMIT
|
||||||
|
PHP version : $PHP_VER
|
||||||
|
DB target : $PG_USER@$PG_HOST:$PG_PORT/$PG_DB
|
||||||
|
Mode : $([[ $USE_DOCKER -eq 1 ]] && echo "docker" || echo "native")
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# --- Création de l'archive ---------------------------------------------------
|
||||||
|
log "Compression de l'archive…"
|
||||||
|
tar -czf "$OUTPUT" -C "$WORK_DIR" .
|
||||||
|
ARCHIVE_SIZE="$(du -h "$OUTPUT" | cut -f1)"
|
||||||
|
ok "Archive prête : $OUTPUT ($ARCHIVE_SIZE)"
|
||||||
|
|
||||||
|
cat <<EOF
|
||||||
|
|
||||||
|
${GREEN}✔ Backup terminé.${RESET}
|
||||||
|
|
||||||
|
Pour migrer vers un autre serveur :
|
||||||
|
scp "$OUTPUT" user@nouveau-serveur:/tmp/
|
||||||
|
ssh user@nouveau-serveur
|
||||||
|
cd /chemin/vers/crm
|
||||||
|
./bin/migrate-restore.sh /tmp/$(basename "$OUTPUT")
|
||||||
|
EOF
|
||||||
242
bin/migrate-restore.sh
Executable file
242
bin/migrate-restore.sh
Executable file
@@ -0,0 +1,242 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ============================================================================
|
||||||
|
# migrate-restore.sh
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Restaure une archive produite par bin/migrate-backup.sh sur un nouveau
|
||||||
|
# serveur : BDD PostgreSQL, fichiers, config, Redis (si présent).
|
||||||
|
#
|
||||||
|
# Usage :
|
||||||
|
# ./bin/migrate-restore.sh <archive.tar.gz>
|
||||||
|
# ./bin/migrate-restore.sh <archive.tar.gz> --yes # sans confirmation
|
||||||
|
# ./bin/migrate-restore.sh <archive.tar.gz> --docker # forcer docker
|
||||||
|
# ./bin/migrate-restore.sh <archive.tar.gz> --no-redis # ignorer Redis
|
||||||
|
# ./bin/migrate-restore.sh <archive.tar.gz> --no-files # restaurer la BDD seulement
|
||||||
|
# ./bin/migrate-restore.sh <archive.tar.gz> --no-db # restaurer les fichiers seulement
|
||||||
|
#
|
||||||
|
# ⚠️ ATTENTION : la restauration ÉCRASE la base de données et les fichiers
|
||||||
|
# de destination. Une sauvegarde locale est créée dans /tmp avant action.
|
||||||
|
# ============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[0;33m'; CYAN='\033[0;36m'; RESET='\033[0m'
|
||||||
|
log() { printf "${CYAN}[restore]${RESET} %s\n" "$*"; }
|
||||||
|
ok() { printf "${GREEN}[ ok ]${RESET} %s\n" "$*"; }
|
||||||
|
warn() { printf "${YELLOW}[ warn ]${RESET} %s\n" "$*"; }
|
||||||
|
err() { printf "${RED}[ error ]${RESET} %s\n" "$*" >&2; }
|
||||||
|
|
||||||
|
# --- Args --------------------------------------------------------------------
|
||||||
|
ARCHIVE=""
|
||||||
|
ASSUME_YES=0
|
||||||
|
FORCE_DOCKER=0
|
||||||
|
SKIP_REDIS=0
|
||||||
|
SKIP_FILES=0
|
||||||
|
SKIP_DB=0
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-y|--yes) ASSUME_YES=1; shift ;;
|
||||||
|
--docker) FORCE_DOCKER=1; shift ;;
|
||||||
|
--no-redis) SKIP_REDIS=1; shift ;;
|
||||||
|
--no-files) SKIP_FILES=1; shift ;;
|
||||||
|
--no-db) SKIP_DB=1; shift ;;
|
||||||
|
-h|--help) sed -n '2,20p' "$0"; exit 0 ;;
|
||||||
|
-*) err "Option inconnue : $1"; exit 1 ;;
|
||||||
|
*) ARCHIVE="$1"; shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
[[ -z "$ARCHIVE" ]] && { err "Usage : $0 <archive.tar.gz> [--yes] [--docker] [--no-redis|--no-files|--no-db]"; exit 1; }
|
||||||
|
[[ ! -f "$ARCHIVE" ]] && { err "Archive introuvable : $ARCHIVE"; exit 1; }
|
||||||
|
|
||||||
|
PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
cd "$PROJECT_DIR"
|
||||||
|
|
||||||
|
WORK_DIR="$(mktemp -d -t crm-restore-XXXXXX)"
|
||||||
|
trap 'rm -rf "$WORK_DIR"' EXIT
|
||||||
|
|
||||||
|
log "Projet : $PROJECT_DIR"
|
||||||
|
log "Archive : $ARCHIVE"
|
||||||
|
log "Workdir : $WORK_DIR"
|
||||||
|
|
||||||
|
# --- Extraction --------------------------------------------------------------
|
||||||
|
log "Extraction de l'archive…"
|
||||||
|
tar -xzf "$ARCHIVE" -C "$WORK_DIR"
|
||||||
|
|
||||||
|
if [[ -f "$WORK_DIR/MANIFEST.txt" ]]; then
|
||||||
|
echo "----- MANIFEST -----"
|
||||||
|
cat "$WORK_DIR/MANIFEST.txt"
|
||||||
|
echo "--------------------"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Lecture de la config destination ---------------------------------------
|
||||||
|
ENV_FILE=""
|
||||||
|
for f in .env.local .env; do
|
||||||
|
[[ -f "$PROJECT_DIR/$f" ]] && ENV_FILE="$PROJECT_DIR/$f" && break
|
||||||
|
done
|
||||||
|
[[ -z "$ENV_FILE" ]] && { err "Aucun .env trouvé sur le serveur cible."; exit 1; }
|
||||||
|
log "Env cible : $ENV_FILE"
|
||||||
|
|
||||||
|
DATABASE_URL="$(grep -E '^DATABASE_URL=' "$ENV_FILE" | tail -n1 | cut -d= -f2- | sed -e 's/^"//' -e 's/"$//' -e "s/^'//" -e "s/'$//")"
|
||||||
|
re='^postgres(ql)?://([^:]+):([^@]*)@([^:/]+)(:([0-9]+))?/([^?]+)'
|
||||||
|
if [[ ! "$DATABASE_URL" =~ $re ]]; then
|
||||||
|
err "DATABASE_URL non reconnue : $DATABASE_URL"; exit 1
|
||||||
|
fi
|
||||||
|
PG_USER="${BASH_REMATCH[2]}"
|
||||||
|
PG_PASS="${BASH_REMATCH[3]}"
|
||||||
|
PG_HOST="${BASH_REMATCH[4]}"
|
||||||
|
PG_PORT="${BASH_REMATCH[6]:-5432}"
|
||||||
|
PG_DB="${BASH_REMATCH[7]}"
|
||||||
|
|
||||||
|
log "Cible BDD : $PG_USER@$PG_HOST:$PG_PORT/$PG_DB"
|
||||||
|
|
||||||
|
# --- Confirmation ------------------------------------------------------------
|
||||||
|
if [[ $ASSUME_YES -eq 0 ]]; then
|
||||||
|
echo
|
||||||
|
warn "Cette opération VA ÉCRASER :"
|
||||||
|
[[ $SKIP_DB -eq 0 ]] && echo " - la base $PG_DB sur $PG_HOST:$PG_PORT"
|
||||||
|
[[ $SKIP_FILES -eq 0 ]] && echo " - les dossiers public/media, public/pdf, public/images, public/seo, var/storage, sauvegarde, .env.local"
|
||||||
|
echo
|
||||||
|
read -r -p "Confirmer ? (tapez 'oui') : " ANSWER
|
||||||
|
[[ "$ANSWER" == "oui" ]] || { err "Annulé."; exit 1; }
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Détection du mode -------------------------------------------------------
|
||||||
|
USE_DOCKER=0
|
||||||
|
if [[ $FORCE_DOCKER -eq 1 ]]; then
|
||||||
|
USE_DOCKER=1
|
||||||
|
elif ! command -v psql >/dev/null 2>&1 || ! command -v pg_restore >/dev/null 2>&1; then
|
||||||
|
if command -v docker >/dev/null 2>&1 && docker ps --format '{{.Names}}' | grep -q '^crm_db$'; then
|
||||||
|
USE_DOCKER=1
|
||||||
|
warn "psql/pg_restore absents, fallback docker exec crm_db"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Helpers psql / pg_restore ----------------------------------------------
|
||||||
|
run_psql() {
|
||||||
|
if [[ $USE_DOCKER -eq 1 ]]; then
|
||||||
|
docker exec -i -e PGPASSWORD="$PG_PASS" crm_db psql -U "$PG_USER" -d "$1" -v ON_ERROR_STOP=1
|
||||||
|
else
|
||||||
|
PGPASSWORD="$PG_PASS" psql -h "$PG_HOST" -p "$PG_PORT" -U "$PG_USER" -d "$1" -v ON_ERROR_STOP=1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
run_pg_restore() {
|
||||||
|
local dump_file="$1"
|
||||||
|
if [[ $USE_DOCKER -eq 1 ]]; then
|
||||||
|
docker exec -i -e PGPASSWORD="$PG_PASS" crm_db \
|
||||||
|
pg_restore -U "$PG_USER" -d "$PG_DB" --no-owner --no-acl --clean --if-exists < "$dump_file"
|
||||||
|
else
|
||||||
|
PGPASSWORD="$PG_PASS" pg_restore \
|
||||||
|
-h "$PG_HOST" -p "$PG_PORT" -U "$PG_USER" -d "$PG_DB" \
|
||||||
|
--no-owner --no-acl --clean --if-exists "$dump_file"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Sauvegarde de sécurité avant écrasement --------------------------------
|
||||||
|
SAFETY_DIR="/tmp/crm-restore-safety-$(date +%Y%m%d-%H%M%S)"
|
||||||
|
mkdir -p "$SAFETY_DIR"
|
||||||
|
log "Sauvegarde de sécurité dans : $SAFETY_DIR"
|
||||||
|
|
||||||
|
if [[ $SKIP_DB -eq 0 ]]; then
|
||||||
|
log "Snapshot BDD courante…"
|
||||||
|
if [[ $USE_DOCKER -eq 1 ]]; then
|
||||||
|
docker exec -e PGPASSWORD="$PG_PASS" crm_db \
|
||||||
|
pg_dump -U "$PG_USER" -d "$PG_DB" --format=custom --no-owner --no-acl > "$SAFETY_DIR/database.dump" 2>/dev/null \
|
||||||
|
|| warn "Snapshot BDD impossible (base inexistante ?)"
|
||||||
|
else
|
||||||
|
PGPASSWORD="$PG_PASS" pg_dump -h "$PG_HOST" -p "$PG_PORT" -U "$PG_USER" -d "$PG_DB" \
|
||||||
|
--format=custom --no-owner --no-acl -f "$SAFETY_DIR/database.dump" 2>/dev/null \
|
||||||
|
|| warn "Snapshot BDD impossible (base inexistante ?)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Restauration BDD --------------------------------------------------------
|
||||||
|
if [[ $SKIP_DB -eq 0 ]]; then
|
||||||
|
DUMP_FILE="$WORK_DIR/database.dump"
|
||||||
|
[[ ! -f "$DUMP_FILE" ]] && { err "database.dump absent de l'archive."; exit 1; }
|
||||||
|
|
||||||
|
log "Création/réinitialisation de la base $PG_DB…"
|
||||||
|
# Création si inexistante (depuis postgres)
|
||||||
|
if [[ $USE_DOCKER -eq 1 ]]; then
|
||||||
|
docker exec -e PGPASSWORD="$PG_PASS" crm_db psql -U "$PG_USER" -d postgres -tc \
|
||||||
|
"SELECT 1 FROM pg_database WHERE datname='$PG_DB'" | grep -q 1 \
|
||||||
|
|| docker exec -e PGPASSWORD="$PG_PASS" crm_db createdb -U "$PG_USER" "$PG_DB"
|
||||||
|
else
|
||||||
|
PGPASSWORD="$PG_PASS" psql -h "$PG_HOST" -p "$PG_PORT" -U "$PG_USER" -d postgres -tc \
|
||||||
|
"SELECT 1 FROM pg_database WHERE datname='$PG_DB'" | grep -q 1 \
|
||||||
|
|| PGPASSWORD="$PG_PASS" createdb -h "$PG_HOST" -p "$PG_PORT" -U "$PG_USER" "$PG_DB"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "pg_restore en cours…"
|
||||||
|
run_pg_restore "$DUMP_FILE" || warn "pg_restore a renvoyé des warnings (souvent bénins)."
|
||||||
|
ok "BDD restaurée"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Restauration des fichiers ----------------------------------------------
|
||||||
|
if [[ $SKIP_FILES -eq 0 && -d "$WORK_DIR/files" ]]; then
|
||||||
|
log "Sauvegarde des dossiers existants…"
|
||||||
|
for p in public/media public/images public/pdf public/seo public/tmp-sign var/storage sauvegarde; do
|
||||||
|
if [[ -e "$PROJECT_DIR/$p" ]]; then
|
||||||
|
mkdir -p "$SAFETY_DIR/$(dirname "$p")"
|
||||||
|
cp -a "$PROJECT_DIR/$p" "$SAFETY_DIR/$p"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
[[ -f "$PROJECT_DIR/.env.local" ]] && cp -a "$PROJECT_DIR/.env.local" "$SAFETY_DIR/.env.local"
|
||||||
|
|
||||||
|
log "Copie des fichiers depuis l'archive…"
|
||||||
|
# rsync si dispo (delta + permissions), sinon cp -a
|
||||||
|
if command -v rsync >/dev/null 2>&1; then
|
||||||
|
rsync -a "$WORK_DIR/files/" "$PROJECT_DIR/"
|
||||||
|
else
|
||||||
|
cp -a "$WORK_DIR/files/." "$PROJECT_DIR/"
|
||||||
|
fi
|
||||||
|
ok "Fichiers restaurés"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Restauration Redis ------------------------------------------------------
|
||||||
|
if [[ $SKIP_REDIS -eq 0 && -f "$WORK_DIR/redis.rdb" ]]; then
|
||||||
|
REDIS_DSN="$(grep -E '^REDIS_DSN=' "$ENV_FILE" | tail -n1 | cut -d= -f2- | sed -e 's/^"//' -e 's/"$//' -e "s/^'//" -e "s/'$//")"
|
||||||
|
rre='^redis://(([^@]*)@)?([^:/]+)(:([0-9]+))?'
|
||||||
|
if [[ -n "$REDIS_DSN" && "$REDIS_DSN" =~ $rre ]]; then
|
||||||
|
R_PASS="${BASH_REMATCH[2]}"
|
||||||
|
R_HOST="${BASH_REMATCH[3]}"
|
||||||
|
R_PORT="${BASH_REMATCH[5]:-6379}"
|
||||||
|
log "Restauration Redis ($R_HOST:$R_PORT)…"
|
||||||
|
if command -v redis-cli >/dev/null 2>&1; then
|
||||||
|
REDIS_AUTH=()
|
||||||
|
[[ -n "$R_PASS" ]] && REDIS_AUTH=(-a "$R_PASS" --no-auth-warning)
|
||||||
|
redis-cli -h "$R_HOST" -p "$R_PORT" "${REDIS_AUTH[@]}" FLUSHALL >/dev/null || true
|
||||||
|
# Restaure clé par clé via DEBUG RELOAD impossible à distance ;
|
||||||
|
# on se contente d'un FLUSHALL : la majorité des données Redis (cache, sessions)
|
||||||
|
# se reconstruit à l'usage. Le RDB reste dans l'archive si besoin manuel.
|
||||||
|
warn "Redis vidé. Le fichier redis.rdb reste dans $WORK_DIR pour usage manuel."
|
||||||
|
warn "Pour réinjecter : copier redis.rdb dans le datadir Redis et redémarrer."
|
||||||
|
cp "$WORK_DIR/redis.rdb" "$SAFETY_DIR/redis.rdb"
|
||||||
|
else
|
||||||
|
warn "redis-cli absent, étape Redis ignorée"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Tâches Symfony post-restauration ---------------------------------------
|
||||||
|
log "Tâches Symfony post-restauration…"
|
||||||
|
if [[ -x "$PROJECT_DIR/bin/console" ]]; then
|
||||||
|
php "$PROJECT_DIR/bin/console" cache:clear --no-interaction || warn "cache:clear KO"
|
||||||
|
php "$PROJECT_DIR/bin/console" doctrine:migrations:migrate --no-interaction --allow-no-migration || warn "migrate KO"
|
||||||
|
ok "Console Symfony OK"
|
||||||
|
else
|
||||||
|
warn "bin/console introuvable, étape ignorée"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat <<EOF
|
||||||
|
|
||||||
|
${GREEN}✔ Restauration terminée.${RESET}
|
||||||
|
|
||||||
|
Sauvegarde de sécurité (rollback possible) :
|
||||||
|
$SAFETY_DIR
|
||||||
|
|
||||||
|
Étapes de validation conseillées :
|
||||||
|
- Vérifier l'accès web (curl -I https://votre-domaine)
|
||||||
|
- Vérifier les permissions : sudo chown -R bot:www-data var public/media public/pdf
|
||||||
|
- Relancer supervisor / worker messenger si nécessaire
|
||||||
|
EOF
|
||||||
Reference in New Issue
Block a user