#!/bin/bash # ============================================ # Script de restauration Gitea # ============================================ # Ce script restaure une sauvegarde complète de Gitea # ATTENTION: Cette opération est destructive! set -euo pipefail # Arrêter en cas d'erreur, variables non définies, erreurs dans pipes # Couleurs pour l'affichage RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Vérifier qu'un fichier de sauvegarde a été fourni if [ -z "${1:-}" ]; then echo -e "${RED}Erreur: Aucun fichier de sauvegarde spécifié${NC}" echo "Usage: $0 " echo "Exemple: $0 backups/gitea_backup_20240101_120000.tar.gz" exit 1 fi BACKUP_FILE="$1" # Vérifier que le fichier existe if [ ! -f "$BACKUP_FILE" ]; then echo -e "${RED}Erreur: Le fichier $BACKUP_FILE n'existe pas${NC}" exit 1 fi # Vérifier que c'est bien un fichier gzip if ! file "$BACKUP_FILE" | grep -q "gzip compressed"; then echo -e "${RED}Erreur: Le fichier n'est pas une archive gzip valide${NC}" exit 1 fi # Validation du chemin (sécurité contre path traversal) BACKUP_FILE=$(realpath "$BACKUP_FILE") BACKUPS_DIR=$(realpath "./backups" 2>/dev/null || echo "") if [ -n "$BACKUPS_DIR" ] && [[ "$BACKUP_FILE" != "$BACKUPS_DIR"* ]]; then echo -e "${YELLOW}Avertissement: Le fichier n'est pas dans le répertoire backups${NC}" read -p "Continuer quand même? (oui/non) " -r if [[ ! $REPLY =~ ^[Oo][Uu][Ii]$ ]]; then echo "Restauration annulée." exit 0 fi fi # Vérifier le checksum si disponible CHECKSUM_FILE="${BACKUP_FILE}.sha256" if [ -f "$CHECKSUM_FILE" ]; then echo -e "${BLUE}Vérification du checksum...${NC}" if cd "$(dirname "$BACKUP_FILE")" && sha256sum -c "$(basename "$CHECKSUM_FILE")" > /dev/null 2>&1; then echo -e "${GREEN}✓ Checksum valide${NC}" cd - > /dev/null else echo -e "${RED}Erreur: Checksum invalide! Le fichier peut être corrompu.${NC}" cd - > /dev/null 2>&1 || true read -p "Continuer quand même? (oui/non) " -r if [[ ! $REPLY =~ ^[Oo][Uu][Ii]$ ]]; then echo "Restauration annulée." exit 0 fi fi else echo -e "${YELLOW}Avertissement: Aucun fichier checksum trouvé${NC}" fi # Charger les variables d'environnement de manière sécurisée if [ -f .env ]; then set -a source .env set +a else echo -e "${RED}Erreur: Fichier .env non trouvé${NC}" exit 1 fi # Vérifier que les variables nécessaires sont définies if [ -z "${POSTGRES_DATABASE:-}" ] || [ -z "${POSTGRES_USER:-}" ] || [ -z "${POSTGRES_PASSWORD:-}" ]; then echo -e "${RED}Erreur: Variables PostgreSQL non définies dans .env${NC}" exit 1 fi # Variables TEMP_DIR=$(mktemp -d) BACKUP_NAME=$(basename "$BACKUP_FILE" .tar.gz) LOG_DIR="./logs" LOG_FILE="${LOG_DIR}/restore_$(date +%Y%m%d_%H%M%S).log" # Créer le répertoire de logs mkdir -p "$LOG_DIR" # Fonction de logging log() { echo -e "$@" | tee -a "$LOG_FILE" } # Fonction de nettoyage cleanup() { local exit_code=$? if [ -d "$TEMP_DIR" ]; then log "${YELLOW}Nettoyage des fichiers temporaires...${NC}" rm -rf "$TEMP_DIR" fi if [ $exit_code -ne 0 ]; then log "${RED}Restauration échouée avec le code: $exit_code${NC}" log "${YELLOW}Les services peuvent être dans un état incohérent.${NC}" log "Consultez le log: $LOG_FILE" fi } # Piège pour nettoyer en cas d'erreur ou d'interruption trap cleanup EXIT INT TERM log "${RED}=== ATTENTION: Restauration de Gitea ===${NC}" log "Fichier de sauvegarde: $BACKUP_FILE" log "${RED}Cette opération va ÉCRASER toutes les données actuelles!${NC}" echo "" read -p "Êtes-vous sûr de vouloir continuer? (oui/non) " -r if [[ ! $REPLY =~ ^[Oo][Uu][Ii]$ ]]; then log "Restauration annulée par l'utilisateur." exit 0 fi # Créer un backup de sécurité de la DB actuelle avant de la dropper SAFETY_BACKUP_DIR="${TEMP_DIR}/safety_backup" mkdir -p "$SAFETY_BACKUP_DIR" log "" log "${GREEN}=== Début de la restauration ===${NC}" log "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')]${NC}" log "${YELLOW}[1/6] Extraction de l'archive...${NC}" if ! tar xzf "$BACKUP_FILE" -C "$TEMP_DIR" 2>> "$LOG_FILE"; then log "${RED}Erreur: Échec de l'extraction de l'archive${NC}" exit 1 fi # Trouver le répertoire de sauvegarde BACKUP_DIR=$(find "$TEMP_DIR" -maxdepth 1 -type d -name "gitea_backup_*" | head -n 1) if [ -z "$BACKUP_DIR" ]; then log "${RED}Erreur: Structure de sauvegarde invalide${NC}" exit 1 fi # Vérifier que les fichiers nécessaires existent if [ ! -f "$BACKUP_DIR/database.dump" ] || [ ! -f "$BACKUP_DIR/gitea_data.tar.gz" ]; then log "${RED}Erreur: Fichiers de sauvegarde manquants${NC}" log "Contenu trouvé: $(ls -la "$BACKUP_DIR" 2>&1)" exit 1 fi log "${GREEN}✓ Archive extraite et vérifiée${NC}" log "${YELLOW}[2/6] Arrêt des services...${NC}" if ! docker compose down 2>> "$LOG_FILE"; then log "${YELLOW}Avertissement: Erreur lors de l'arrêt des services${NC}" fi log "${GREEN}✓ Services arrêtés${NC}" log "${YELLOW}[3/6] Démarrage de PostgreSQL et backup de sécurité...${NC}" docker compose up -d db log "Attente du démarrage de PostgreSQL..." # Attendre que PostgreSQL soit prêt MAX_TRIES=30 COUNTER=0 until docker compose exec -T db pg_isready -U "$POSTGRES_USER" > /dev/null 2>&1; do COUNTER=$((COUNTER + 1)) if [ $COUNTER -gt $MAX_TRIES ]; then log "${RED}Erreur: PostgreSQL ne démarre pas${NC}" exit 1 fi log "En attente de PostgreSQL... ($COUNTER/$MAX_TRIES)" sleep 2 done log "${GREEN}✓ PostgreSQL prêt${NC}" # Créer un backup de sécurité de la DB actuelle log "${BLUE}Création d'un backup de sécurité de la DB actuelle...${NC}" if docker compose exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" db pg_dump \ -U "$POSTGRES_USER" \ -d "$POSTGRES_DATABASE" \ --format=custom \ > "$SAFETY_BACKUP_DIR/current_db.dump" 2>> "$LOG_FILE"; then log "${GREEN}✓ Backup de sécurité créé${NC}" else log "${YELLOW}Avertissement: Impossible de créer le backup de sécurité (DB peut ne pas exister)${NC}" fi log "${YELLOW}[4/6] Restauration de la base de données...${NC}" # Supprimer et recréer la base de données if ! docker compose exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" db psql \ -U "$POSTGRES_USER" -d postgres \ -c "DROP DATABASE IF EXISTS $POSTGRES_DATABASE;" 2>> "$LOG_FILE"; then log "${RED}Erreur: Échec de la suppression de la base de données${NC}" exit 1 fi if ! docker compose exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" db psql \ -U "$POSTGRES_USER" -d postgres \ -c "CREATE DATABASE $POSTGRES_DATABASE OWNER $POSTGRES_USER;" 2>> "$LOG_FILE"; then log "${RED}Erreur: Échec de la création de la base de données${NC}" exit 1 fi # Restaurer le dump if ! docker compose exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" db pg_restore \ -U "$POSTGRES_USER" \ -d "$POSTGRES_DATABASE" \ --clean \ --if-exists \ --no-owner \ --no-acl \ < "$BACKUP_DIR/database.dump" 2>> "$LOG_FILE"; then log "${RED}Erreur: Échec de la restauration de la base de données${NC}" log "${YELLOW}Le backup de sécurité est disponible dans: $SAFETY_BACKUP_DIR/current_db.dump${NC}" exit 1 fi log "${GREEN}✓ Base de données restaurée${NC}" log "${YELLOW}[5/6] Restauration des données Gitea...${NC}" # Arrêter Gitea s'il tourne docker compose stop gitea 2>/dev/null || true # Nettoyer les données existantes de manière sécurisée DATA_DIR="./data" if [ -d "$DATA_DIR" ]; then # Vérifier que c'est bien le bon répertoire if [ "$DATA_DIR" = "./data" ]; then log "Suppression des anciennes données..." # Utiliser find pour une suppression plus sûre find "$DATA_DIR" -mindepth 1 -delete 2>> "$LOG_FILE" || { log "${YELLOW}Avertissement: Échec du nettoyage partiel${NC}" } else log "${RED}Erreur: Répertoire de données invalide: $DATA_DIR${NC}" exit 1 fi fi # Créer le répertoire de données mkdir -p "$DATA_DIR" # Extraire les données depuis l'archive log "Extraction des données Gitea..." if ! tar xzf "$BACKUP_DIR/gitea_data.tar.gz" -C "$DATA_DIR" 2>> "$LOG_FILE"; then log "${RED}Erreur: Échec de l'extraction des données Gitea${NC}" exit 1 fi # Vérifier les permissions log "Vérification des permissions..." if [ -d "$DATA_DIR/gitea" ]; then log "${GREEN}✓ Données restaurées${NC}" else log "${YELLOW}Avertissement: Structure de données inattendue${NC}" fi log "${YELLOW}[6/6] Redémarrage de tous les services...${NC}" # Démarrer tous les services (db, redis, gitea) docker compose up -d # Attendre que les services soient prêts log "Attente du démarrage complet..." sleep 10 MAX_TRIES=30 COUNTER=0 until docker compose ps | grep -q 'gitea.*running'; do COUNTER=$((COUNTER + 1)) if [ $COUNTER -gt $MAX_TRIES ]; then log "${YELLOW}Avertissement: Gitea met du temps à démarrer${NC}" break fi log "En attente de Gitea... ($COUNTER/$MAX_TRIES)" sleep 2 done log "" log "${GREEN}=== Restauration terminée avec succès ===${NC}" log "Gitea devrait être accessible sur: http://localhost:3000" log "" log "Vérifications recommandées:" log " - Vérifier l'accès web" log " - Tester l'authentification" log " - Vérifier les dépôts" log " - Consulter les logs: docker compose logs -f gitea" log "" log "Log de restauration: $LOG_FILE" log "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')] Restauration terminée${NC}"