#!/bin/bash # scripts/restore.sh - Restauration d'un backup set -euo pipefail # Variables globales SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" cd "$PROJECT_ROOT" # Charger .env en premier if [ ! -f .env ]; then echo "ERROR: Fichier .env introuvable" exit 1 fi set -a # shellcheck disable=SC1091 source .env set +a # Configuration des logs LOG_DIR="./logs" LOG_FILE="$LOG_DIR/restore_$(date +%Y%m%d_%H%M%S).log" TEMP_DIR="" mkdir -p "$LOG_DIR" # Charger les fonctions communes (log avec couleurs) # shellcheck disable=SC1091 source "$SCRIPT_DIR/common.sh" # Vérifier les variables requises if [ -z "${MYSQL_USER:-}" ] || [ -z "${MYSQL_PASSWORD:-}" ] || [ -z "${MYSQL_DATABASE:-}" ]; then log "ERROR" "Variables MySQL non définies dans .env" exit 1 fi # Vérifier les arguments if [ -z "${1:-}" ]; then log "ERROR" "Usage: $0 " exit 1 fi BACKUP_FILE="$1" # Valider le chemin du fichier (éviter path traversal) if [[ "$BACKUP_FILE" =~ \.\. ]] || [[ "$BACKUP_FILE" == /* && ! "$BACKUP_FILE" =~ ^/home/ && ! "$BACKUP_FILE" =~ ^/tmp/ ]]; then log "ERROR" "Chemin de fichier invalide ou non autorisé" exit 1 fi # Résoudre le chemin absolu BACKUP_FILE=$(realpath "$BACKUP_FILE" 2>/dev/null) || { log "ERROR" "Impossible de résoudre le chemin: $1" exit 1 } if [ ! -f "$BACKUP_FILE" ]; then log "ERROR" "Fichier introuvable: $BACKUP_FILE" exit 1 fi # Vérifier que c'est bien un fichier tar.gz if ! file "$BACKUP_FILE" | grep -q "gzip compressed"; then log "ERROR" "Le fichier n'est pas une archive gzip valide" exit 1 fi # Vérifier le checksum si disponible CHECKSUM_FILE="${BACKUP_FILE}.sha256" if [ -f "$CHECKSUM_FILE" ]; then log "INFO" "Vérification du checksum..." BACKUP_DIR=$(dirname "$BACKUP_FILE") BACKUP_NAME=$(basename "$BACKUP_FILE") if cd "$BACKUP_DIR" && sha256sum -c "$BACKUP_NAME.sha256" 2>>"$LOG_FILE"; then cd "$PROJECT_ROOT" log "INFO" "Checksum valide" else cd "$PROJECT_ROOT" log "ERROR" "Checksum invalide! Le fichier pourrait être corrompu" exit 1 fi else log "WARN" "Pas de fichier checksum trouvé, impossible de vérifier l'intégrité" fi log "INFO" "=== Restauration depuis: $BACKUP_FILE ===" log "INFO" "Log file: $LOG_FILE" echo "" log "WARN" "ATTENTION: Cette opération va écraser les données actuelles!" read -r -p "Voulez-vous créer un backup de sécurité avant de continuer? (yes/no): " create_backup if [ "$create_backup" = "yes" ]; then log "INFO" "Création du backup de sécurité..." if bash scripts/backup.sh 2>&1 | tee -a "$LOG_FILE"; then log "INFO" "Backup de sécurité créé avec succès" else log "ERROR" "Échec du backup de sécurité" read -r -p "Continuer quand même? (yes/no): " force_continue if [ "$force_continue" != "yes" ]; then log "INFO" "Restauration annulée" exit 0 fi fi fi echo "" read -r -p "Continuer la restauration? (yes/no): " confirm if [ "$confirm" != "yes" ]; then log "INFO" "Restauration annulée" exit 0 fi # Fonction de nettoyage cleanup() { local exit_code=$? if [ -n "$TEMP_DIR" ] && [ -d "$TEMP_DIR" ]; then log "INFO" "Nettoyage du répertoire temporaire..." rm -rf "${TEMP_DIR:?}" fi if [ "$exit_code" -eq 0 ]; then log "SUCCESS" "Restauration terminée avec succès" else log "ERROR" "Restauration échouée avec code: $exit_code" log "ERROR" "Pour récupérer, utilisez: bash scripts/recover.sh" fi exit "$exit_code" } trap cleanup EXIT INT TERM # Extraire le backup TEMP_DIR=$(mktemp -d) log "INFO" "Extraction vers $TEMP_DIR..." if ! tar -xzf "$BACKUP_FILE" -C "$TEMP_DIR" 2>>"$LOG_FILE"; then log "ERROR" "Erreur lors de l'extraction" exit 1 fi # Trouver le répertoire de backup BACKUP_DIR=$(find "$TEMP_DIR" -mindepth 1 -maxdepth 1 -type d | head -n1) if [ -z "$BACKUP_DIR" ]; then log "ERROR" "Aucun répertoire trouvé dans l'archive" exit 1 fi log "INFO" "Backup extrait: $(basename "$BACKUP_DIR")" # Vérifier que les fichiers requis existent if [ ! -f "$BACKUP_DIR/database.sql" ]; then log "ERROR" "Fichier database.sql manquant dans l'archive" exit 1 fi if [ ! -f "$BACKUP_DIR/config.tar.gz" ]; then log "ERROR" "Fichier config.tar.gz manquant dans l'archive" exit 1 fi # Arrêter les services log "INFO" "Arrêt des services..." docker-compose down 2>>"$LOG_FILE" # Démarrer uniquement la base de données log "INFO" "Démarrage de la base de données..." docker-compose up -d db 2>>"$LOG_FILE" # Attendre que la base de données soit prête log "INFO" "Attente de la base de données..." for i in {1..30}; do if docker-compose exec -T db mysqladmin ping -h localhost --silent 2>>"$LOG_FILE"; then log "INFO" "Base de données prête" break fi if [ "$i" -eq 30 ]; then log "ERROR" "Timeout: La base de données n'est pas prête" exit 1 fi sleep 1 done # Backup de sécurité de la DB actuelle log "INFO" "Backup de sécurité de la base de données actuelle..." SAFETY_BACKUP="$TEMP_DIR/safety_db_backup.sql" if docker-compose exec -T db sh -c "MYSQL_PWD=\"\$MYSQL_PASSWORD\" mysqldump \ -u\"\$MYSQL_USER\" \ \"\$MYSQL_DATABASE\" \ --single-transaction \ --quick" > "$SAFETY_BACKUP" 2>>"$LOG_FILE"; then log "INFO" "Backup de sécurité créé: $SAFETY_BACKUP" else log "WARN" "Impossible de créer un backup de sécurité de la DB" fi # Restaurer la base de données log "INFO" "Restauration de la base de données..." if ! docker-compose exec -T db sh -c "MYSQL_PWD=\"\$MYSQL_PASSWORD\" mysql \ -u\"\$MYSQL_USER\" \ \"\$MYSQL_DATABASE\"" < "$BACKUP_DIR/database.sql" 2>>"$LOG_FILE"; then log "ERROR" "Erreur lors de la restauration de la base de données" log "ERROR" "Backup de sécurité disponible: $SAFETY_BACKUP" exit 1 fi log "INFO" "Base de données restaurée" # Démarrer tous les services log "INFO" "Démarrage de tous les services..." docker-compose up -d 2>>"$LOG_FILE" # Attendre que Nextcloud soit prêt log "INFO" "Attente du démarrage de Nextcloud..." for i in {1..60}; do if docker-compose exec -T nextcloud curl -f http://localhost/status.php >/dev/null 2>&1; then log "INFO" "Nextcloud prêt" break fi if [ "$i" -eq 60 ]; then log "ERROR" "Timeout: Nextcloud n'est pas prêt" log "ERROR" "Vérifiez les logs: docker-compose logs nextcloud" exit 1 fi sleep 1 done # Restaurer la configuration log "INFO" "Restauration de la configuration..." if ! docker-compose exec -T -u www-data nextcloud tar -xzf - -C /var/www/html/config < "$BACKUP_DIR/config.tar.gz" 2>>"$LOG_FILE"; then log "ERROR" "Erreur lors de la restauration de la configuration" exit 1 fi log "INFO" "Configuration restaurée" # Restaurer les données if [ -f "$BACKUP_DIR/data.tar.gz" ]; then log "INFO" "Restauration des données utilisateurs..." if ! docker-compose exec -T -u www-data nextcloud tar -xzf - -C /var/www/html/data < "$BACKUP_DIR/data.tar.gz" 2>>"$LOG_FILE"; then log "ERROR" "Erreur lors de la restauration des données" exit 1 fi log "INFO" "Données restaurées" else log "WARN" "Pas de fichier data.tar.gz trouvé" fi # Restaurer les apps personnalisées if [ -f "$BACKUP_DIR/apps.tar.gz" ]; then log "INFO" "Restauration des apps personnalisées..." if docker-compose exec -T -u www-data nextcloud tar -xzf - -C /var/www/html/custom_apps < "$BACKUP_DIR/apps.tar.gz" 2>>"$LOG_FILE"; then log "INFO" "Apps restaurées" else log "WARN" "Erreur lors de la restauration des apps" fi else log "INFO" "Pas d'apps personnalisées à restaurer" fi # Désactiver le mode maintenance log "INFO" "Désactivation du mode maintenance..." docker-compose exec -T -u www-data nextcloud php occ maintenance:mode --off 2>>"$LOG_FILE" || log "WARN" "Impossible de désactiver le mode maintenance" # Réparer et scanner log "INFO" "Réparation de l'installation..." docker-compose exec -T -u www-data nextcloud php occ maintenance:repair 2>>"$LOG_FILE" || log "WARN" "Erreur lors de la réparation" log "INFO" "Scan des fichiers..." docker-compose exec -T -u www-data nextcloud php occ files:scan --all 2>>"$LOG_FILE" || log "WARN" "Erreur lors du scan" log "INFO" "=== Restauration terminée ===" log "INFO" "Vérifiez que tout fonctionne: make health"