diff --git a/backup.sh b/backup.sh new file mode 100755 index 0000000..b2758db --- /dev/null +++ b/backup.sh @@ -0,0 +1,135 @@ +#!/bin/sh +set -eu + +# ============================================================================ +# E-COSPLAY - Script de sauvegarde pour migration vers un autre serveur +# ---------------------------------------------------------------------------- +# Sauvegarde : +# - Base de données PostgreSQL (pg_dump logique) +# - Volume MinIO (fichiers utilisateurs / médias) +# - Volume Vault (secrets HashiCorp Vault) +# - Fichiers d'environnement (.env.local, google.json, account.json) +# +# Le tout est empaqueté dans une archive tar.gz horodatée, prête à être +# transférée (scp/rsync) puis restaurée avec restore.sh sur le nouveau serveur. +# ============================================================================ + +RED='\033[0;31m' +ORANGE='\033[0;33m' +GREEN='\033[0;32m' +CYAN='\033[0;36m' +RESET='\033[0m' + +PROJECT_DIR="$(cd "$(dirname "$0")" && pwd)" +cd "$PROJECT_DIR" + +TIMESTAMP="$(date +%Y%m%d_%H%M%S)" +BACKUP_DIR="${BACKUP_DIR:-$PROJECT_DIR/var/backups}" +WORK_DIR="$BACKUP_DIR/tmp_$TIMESTAMP" +ARCHIVE="$BACKUP_DIR/e-cosplay_backup_$TIMESTAMP.tar.gz" + +DB_CONTAINER="${DB_CONTAINER:-e-cosplay_db}" +DB_NAME="${DB_NAME:-app_db}" +DB_USER="${DB_USER:-symfony_user}" + +MINIO_CONTAINER="${MINIO_CONTAINER:-e-cosplay_minio}" +VAULT_CONTAINER="${VAULT_CONTAINER:-e-cosplay_vault}" + +echo "${CYAN}############################${RESET}" +echo "${CYAN}# E-COSPLAY BACKUP START #${RESET}" +echo "${CYAN}############################${RESET}" +echo "${CYAN}Destination : ${ARCHIVE}${RESET}" + +mkdir -p "$WORK_DIR" + +# --------------------------------------------------------------------------- +# 1) Vérifie que docker tourne et que les conteneurs sont up +# --------------------------------------------------------------------------- +if ! command -v docker >/dev/null 2>&1; then + echo "${RED}docker introuvable dans le PATH${RESET}" >&2 + exit 1 +fi + +ensure_running() { + name="$1" + if ! docker ps --format '{{.Names}}' | grep -q "^${name}$"; then + echo "${ORANGE}> Conteneur ${name} arrêté, démarrage...${RESET}" + docker compose up -d "$(echo "$name" | sed 's/^e-cosplay_//')" || true + sleep 3 + fi +} + +ensure_running "$DB_CONTAINER" +ensure_running "$MINIO_CONTAINER" +ensure_running "$VAULT_CONTAINER" + +# --------------------------------------------------------------------------- +# 2) Dump PostgreSQL (logique, portable entre versions mineures) +# --------------------------------------------------------------------------- +echo "${CYAN}> Dump de la base PostgreSQL (${DB_NAME})...${RESET}" +docker exec -t "$DB_CONTAINER" \ + pg_dump -U "$DB_USER" -d "$DB_NAME" --clean --if-exists --no-owner --no-privileges \ + | gzip -9 > "$WORK_DIR/database.sql.gz" +echo "${GREEN} ✓ database.sql.gz ($(du -h "$WORK_DIR/database.sql.gz" | cut -f1))${RESET}" + +# --------------------------------------------------------------------------- +# 3) Snapshot des données MinIO (tar du dossier /data du conteneur) +# --------------------------------------------------------------------------- +echo "${CYAN}> Sauvegarde des données MinIO...${RESET}" +docker exec "$MINIO_CONTAINER" tar -C /data -czf - . > "$WORK_DIR/minio_data.tar.gz" +echo "${GREEN} ✓ minio_data.tar.gz ($(du -h "$WORK_DIR/minio_data.tar.gz" | cut -f1))${RESET}" + +# --------------------------------------------------------------------------- +# 4) Snapshot des données Vault +# --------------------------------------------------------------------------- +echo "${CYAN}> Sauvegarde des données Vault...${RESET}" +if docker exec "$VAULT_CONTAINER" sh -c '[ -d /vault ]' 2>/dev/null; then + docker exec "$VAULT_CONTAINER" tar -C /vault -czf - . > "$WORK_DIR/vault_data.tar.gz" + echo "${GREEN} ✓ vault_data.tar.gz ($(du -h "$WORK_DIR/vault_data.tar.gz" | cut -f1))${RESET}" +else + echo "${ORANGE} ! /vault introuvable, vault ignoré${RESET}" +fi + +# --------------------------------------------------------------------------- +# 5) Fichiers d'environnement et credentials +# --------------------------------------------------------------------------- +echo "${CYAN}> Copie des fichiers d'environnement et credentials...${RESET}" +mkdir -p "$WORK_DIR/env" +for f in .env .env.local .env.prod .env.prod.local google.json account.json; do + if [ -f "$PROJECT_DIR/$f" ]; then + cp -a "$PROJECT_DIR/$f" "$WORK_DIR/env/$f" + echo "${GREEN} ✓ $f${RESET}" + fi +done + +# --------------------------------------------------------------------------- +# 6) Métadonnées (utiles côté restore pour vérifier la cohérence) +# --------------------------------------------------------------------------- +{ + echo "timestamp=$TIMESTAMP" + echo "hostname=$(hostname)" + echo "git_commit=$(git -C "$PROJECT_DIR" rev-parse HEAD 2>/dev/null || echo unknown)" + echo "git_branch=$(git -C "$PROJECT_DIR" rev-parse --abbrev-ref HEAD 2>/dev/null || echo unknown)" + echo "db_container=$DB_CONTAINER" + echo "db_name=$DB_NAME" + echo "db_user=$DB_USER" + echo "minio_container=$MINIO_CONTAINER" + echo "vault_container=$VAULT_CONTAINER" +} > "$WORK_DIR/manifest.txt" + +# --------------------------------------------------------------------------- +# 7) Empaquetage final +# --------------------------------------------------------------------------- +echo "${CYAN}> Empaquetage de l'archive finale...${RESET}" +tar -C "$BACKUP_DIR" -czf "$ARCHIVE" "tmp_$TIMESTAMP" +rm -rf "$WORK_DIR" + +echo "${GREEN}############################${RESET}" +echo "${GREEN}# BACKUP TERMINÉ #${RESET}" +echo "${GREEN}############################${RESET}" +echo "${GREEN}Archive : ${ARCHIVE}${RESET}" +echo "${GREEN}Taille : $(du -h "$ARCHIVE" | cut -f1)${RESET}" +echo +echo "${CYAN}Pour transférer vers le nouveau serveur :${RESET}" +echo " scp \"$ARCHIVE\" user@nouveau-serveur:/chemin/vers/e-cosplay/var/backups/" +echo " ssh user@nouveau-serveur 'cd /chemin/vers/e-cosplay && ./restore.sh \"var/backups/$(basename "$ARCHIVE")\"'" diff --git a/restore.sh b/restore.sh new file mode 100755 index 0000000..375e1dc --- /dev/null +++ b/restore.sh @@ -0,0 +1,180 @@ +#!/bin/sh +set -eu + +# ============================================================================ +# E-COSPLAY - Script de restauration sur un nouveau serveur +# ---------------------------------------------------------------------------- +# Usage : ./restore.sh +# +# Étapes : +# 1. Décompresse l'archive de sauvegarde +# 2. Restaure les fichiers d'environnement (.env.local, etc.) +# 3. Démarre la stack docker compose +# 4. Restaure la base PostgreSQL via psql +# 5. Restaure les volumes MinIO et Vault +# 6. Vide le cache Symfony et lance les migrations Doctrine éventuelles +# +# ATTENTION : la restauration ÉCRASE les données existantes (DB + volumes). +# ============================================================================ + +RED='\033[0;31m' +ORANGE='\033[0;33m' +GREEN='\033[0;32m' +CYAN='\033[0;36m' +RESET='\033[0m' + +if [ $# -lt 1 ]; then + echo "${RED}Usage : $0 ${RESET}" >&2 + exit 1 +fi + +ARCHIVE="$1" +if [ ! -f "$ARCHIVE" ]; then + echo "${RED}Archive introuvable : $ARCHIVE${RESET}" >&2 + exit 1 +fi + +PROJECT_DIR="$(cd "$(dirname "$0")" && pwd)" +cd "$PROJECT_DIR" + +# Résout l'archive en chemin absolu AVANT de cd dans le dossier de travail +case "$ARCHIVE" in + /*) ARCHIVE_ABS="$ARCHIVE" ;; + *) ARCHIVE_ABS="$(pwd)/$ARCHIVE" ;; +esac + +DB_CONTAINER="${DB_CONTAINER:-e-cosplay_db}" +DB_NAME="${DB_NAME:-app_db}" +DB_USER="${DB_USER:-symfony_user}" +MINIO_CONTAINER="${MINIO_CONTAINER:-e-cosplay_minio}" +VAULT_CONTAINER="${VAULT_CONTAINER:-e-cosplay_vault}" + +WORK_DIR="$(mktemp -d /tmp/e-cosplay_restore.XXXXXX)" +trap 'rm -rf "$WORK_DIR"' EXIT + +echo "${CYAN}#############################${RESET}" +echo "${CYAN}# E-COSPLAY RESTORE START #${RESET}" +echo "${CYAN}#############################${RESET}" +echo "${CYAN}Archive : ${ARCHIVE_ABS}${RESET}" + +# --------------------------------------------------------------------------- +# Confirmation interactive (skip avec FORCE=1) +# --------------------------------------------------------------------------- +if [ "${FORCE:-0}" != "1" ]; then + printf "${ORANGE}La restauration va ÉCRASER la base et les volumes existants. Continuer ? [y/N] ${RESET}" + read -r answer + case "$answer" in + y|Y|yes|YES) ;; + *) echo "Annulé."; exit 0 ;; + esac +fi + +# --------------------------------------------------------------------------- +# 1) Décompression +# --------------------------------------------------------------------------- +echo "${CYAN}> Décompression de l'archive...${RESET}" +tar -xzf "$ARCHIVE_ABS" -C "$WORK_DIR" +SRC="$(find "$WORK_DIR" -mindepth 1 -maxdepth 1 -type d | head -n1)" +if [ -z "$SRC" ] || [ ! -f "$SRC/manifest.txt" ]; then + echo "${RED}Archive invalide : manifest.txt introuvable${RESET}" >&2 + exit 1 +fi +echo "${GREEN} ✓ Contenu :${RESET}" +ls -la "$SRC" +echo "${GREEN} ✓ Manifest :${RESET}" +sed 's/^/ /' "$SRC/manifest.txt" + +# --------------------------------------------------------------------------- +# 2) Restauration des fichiers d'environnement +# --------------------------------------------------------------------------- +if [ -d "$SRC/env" ]; then + echo "${CYAN}> Restauration des fichiers d'environnement...${RESET}" + for f in "$SRC"/env/*; do + [ -e "$f" ] || continue + name="$(basename "$f")" + if [ -f "$PROJECT_DIR/$name" ]; then + cp -a "$PROJECT_DIR/$name" "$PROJECT_DIR/$name.bak.$(date +%s)" + fi + cp -a "$f" "$PROJECT_DIR/$name" + echo "${GREEN} ✓ $name${RESET}" + done +fi + +# --------------------------------------------------------------------------- +# 3) Démarrage de la stack docker compose +# --------------------------------------------------------------------------- +echo "${CYAN}> Démarrage de la stack docker compose...${RESET}" +docker compose up -d db minio vault +echo "${CYAN} Attente que PostgreSQL soit prêt...${RESET}" +for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do + if docker exec "$DB_CONTAINER" pg_isready -U "$DB_USER" -d "$DB_NAME" >/dev/null 2>&1; then + echo "${GREEN} ✓ PostgreSQL prêt${RESET}" + break + fi + sleep 2 + if [ "$i" = "15" ]; then + echo "${RED}PostgreSQL n'est pas devenu disponible à temps${RESET}" >&2 + exit 1 + fi +done + +# --------------------------------------------------------------------------- +# 4) Restauration PostgreSQL +# --------------------------------------------------------------------------- +if [ -f "$SRC/database.sql.gz" ]; then + echo "${CYAN}> Restauration de la base PostgreSQL...${RESET}" + gunzip -c "$SRC/database.sql.gz" \ + | docker exec -i "$DB_CONTAINER" psql -U "$DB_USER" -d "$DB_NAME" -v ON_ERROR_STOP=1 >/dev/null + echo "${GREEN} ✓ Base restaurée${RESET}" +else + echo "${ORANGE} ! database.sql.gz absent, étape ignorée${RESET}" +fi + +# --------------------------------------------------------------------------- +# 5) Restauration MinIO +# --------------------------------------------------------------------------- +if [ -f "$SRC/minio_data.tar.gz" ]; then + echo "${CYAN}> Restauration des données MinIO...${RESET}" + docker exec "$MINIO_CONTAINER" sh -c 'rm -rf /data/* /data/..?* /data/.[!.]* 2>/dev/null || true' + docker exec -i "$MINIO_CONTAINER" tar -C /data -xzf - < "$SRC/minio_data.tar.gz" + echo "${GREEN} ✓ MinIO restauré${RESET}" +else + echo "${ORANGE} ! minio_data.tar.gz absent, étape ignorée${RESET}" +fi + +# --------------------------------------------------------------------------- +# 6) Restauration Vault +# --------------------------------------------------------------------------- +if [ -f "$SRC/vault_data.tar.gz" ]; then + echo "${CYAN}> Restauration des données Vault...${RESET}" + docker exec "$VAULT_CONTAINER" sh -c 'rm -rf /vault/file 2>/dev/null || true' + docker exec -i "$VAULT_CONTAINER" tar -C /vault -xzf - < "$SRC/vault_data.tar.gz" + echo "${CYAN} Redémarrage du conteneur Vault...${RESET}" + docker compose restart vault + echo "${GREEN} ✓ Vault restauré${RESET}" +else + echo "${ORANGE} ! vault_data.tar.gz absent, étape ignorée${RESET}" +fi + +# --------------------------------------------------------------------------- +# 7) Démarrage du reste de la stack + cache + migrations +# --------------------------------------------------------------------------- +echo "${CYAN}> Démarrage du reste de la stack...${RESET}" +docker compose up -d + +if docker ps --format '{{.Names}}' | grep -q '^e-cosplay_php$'; then + echo "${CYAN}> Vidage du cache Symfony...${RESET}" + docker exec e-cosplay_php php bin/console cache:clear --no-warmup || true + docker exec e-cosplay_php php bin/console cache:warmup || true + + echo "${CYAN}> Application des migrations Doctrine éventuelles...${RESET}" + docker exec e-cosplay_php php bin/console doctrine:migrations:migrate --no-interaction --allow-no-migration || true +fi + +echo "${GREEN}#############################${RESET}" +echo "${GREEN}# RESTORE TERMINÉ #${RESET}" +echo "${GREEN}#############################${RESET}" +echo "${GREEN}Pense à vérifier :${RESET}" +echo " - les variables d'environnement spécifiques au nouveau serveur (PATH_URL, DEV_URL, etc.)" +echo " - les certificats et la configuration Caddy" +echo " - le déverrouillage de Vault si tu utilises un mode non-dev"