390 lines
12 KiB
Bash
Executable file
390 lines
12 KiB
Bash
Executable file
#!/bin/bash
|
||
|
||
# Django FBF Live Data Migration Script
|
||
# Migriert fbf-backup.sql vom Live-Server in den aktuellen Entwicklungsstand
|
||
#
|
||
# Verwendung: ./migrate_live_data.sh [backup-file]
|
||
# Standard: ./migrate_live_data.sh fbf-backup.sql
|
||
|
||
set -e # Stoppe bei Fehlern
|
||
|
||
# Farben für Output
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m' # No Color
|
||
|
||
# Logging Funktionen
|
||
log_info() {
|
||
echo -e "${BLUE}ℹ️ $1${NC}"
|
||
}
|
||
|
||
log_success() {
|
||
echo -e "${GREEN}✅ $1${NC}"
|
||
}
|
||
|
||
log_warning() {
|
||
echo -e "${YELLOW}⚠️ $1${NC}"
|
||
}
|
||
|
||
log_error() {
|
||
echo -e "${RED}❌ $1${NC}"
|
||
}
|
||
|
||
# Fortschrittsanzeige
|
||
show_progress() {
|
||
echo -e "${BLUE}🔄 $1${NC}"
|
||
}
|
||
|
||
# Banner
|
||
print_banner() {
|
||
echo -e "${BLUE}"
|
||
echo "╔══════════════════════════════════════════════════════════════╗"
|
||
echo "║ Django FBF Live Data Migration ║"
|
||
echo "║ Version 1.0 ║"
|
||
echo "╚══════════════════════════════════════════════════════════════╝"
|
||
echo -e "${NC}"
|
||
}
|
||
|
||
# Überprüfe Voraussetzungen
|
||
check_prerequisites() {
|
||
log_info "Überprüfe Voraussetzungen..."
|
||
|
||
# Docker und Docker Compose prüfen
|
||
if ! command -v docker &> /dev/null; then
|
||
log_error "Docker ist nicht installiert oder nicht im PATH"
|
||
exit 1
|
||
fi
|
||
|
||
if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then
|
||
log_error "Docker Compose ist nicht installiert oder nicht im PATH"
|
||
exit 1
|
||
fi
|
||
|
||
# docker-compose.yaml prüfen
|
||
if [ ! -f "docker-compose.yaml" ]; then
|
||
log_error "docker-compose.yaml nicht gefunden. Stelle sicher, dass du im Projektverzeichnis bist."
|
||
exit 1
|
||
fi
|
||
|
||
log_success "Alle Voraussetzungen erfüllt"
|
||
}
|
||
|
||
# Backup-Datei validieren
|
||
validate_backup_file() {
|
||
local backup_file="$1"
|
||
|
||
if [ ! -f "$backup_file" ]; then
|
||
log_error "Backup-Datei '$backup_file' nicht gefunden"
|
||
exit 1
|
||
fi
|
||
|
||
# Überprüfe ob es sich um ein PostgreSQL Dump handelt
|
||
if ! head -20 "$backup_file" | grep -q "PostgreSQL database dump"; then
|
||
log_error "Die Datei '$backup_file' scheint kein gültiges PostgreSQL Dump zu sein"
|
||
exit 1
|
||
fi
|
||
|
||
local file_size=$(stat -f%z "$backup_file" 2>/dev/null || stat -c%s "$backup_file" 2>/dev/null || echo "0")
|
||
local size_display=""
|
||
if command -v numfmt >/dev/null 2>&1; then
|
||
size_display=" ($(numfmt --to=iec $file_size))"
|
||
elif [ "$file_size" -gt 0 ]; then
|
||
size_display=" (${file_size} bytes)"
|
||
fi
|
||
log_info "Backup-Datei: $backup_file$size_display"
|
||
log_success "Backup-Datei validiert"
|
||
}
|
||
|
||
# Container Status prüfen
|
||
check_containers() {
|
||
log_info "Überprüfe Container Status..."
|
||
|
||
if ! docker-compose ps | grep -q "django_fbf_db_1.*Up"; then
|
||
log_warning "Datenbank-Container läuft nicht. Starte Services..."
|
||
docker-compose up -d db
|
||
|
||
# Warte auf Datenbank
|
||
local timeout=60
|
||
local count=0
|
||
while [ $count -lt $timeout ]; do
|
||
if docker-compose exec -T db pg_isready -U fbf -d postgres &>/dev/null; then
|
||
break
|
||
fi
|
||
echo -n "."
|
||
sleep 1
|
||
((count++))
|
||
done
|
||
|
||
if [ $count -ge $timeout ]; then
|
||
log_error "Datenbank-Container konnte nicht gestartet werden"
|
||
exit 1
|
||
fi
|
||
fi
|
||
|
||
log_success "Container sind bereit"
|
||
}
|
||
|
||
# Backup der aktuellen Datenbank erstellen
|
||
backup_current_db() {
|
||
local timestamp=$(date +"%Y%m%d_%H%M%S")
|
||
local backup_name="pre_migration_backup_${timestamp}.sql"
|
||
|
||
log_info "Erstelle Backup der aktuellen Datenbank..."
|
||
|
||
if docker-compose exec -T db pg_dump -U fbf -d db_fbf > "$backup_name" 2>/dev/null; then
|
||
log_success "Backup erstellt: $backup_name"
|
||
echo "$backup_name" > .last_backup_file
|
||
else
|
||
log_warning "Konnte kein Backup erstellen (vermutlich keine existierende Datenbank)"
|
||
fi
|
||
}
|
||
|
||
# Datenbank zurücksetzen
|
||
reset_database() {
|
||
log_info "Setze Datenbank zurück..."
|
||
|
||
# Stoppe Web-Container um Verbindungen zu vermeiden
|
||
show_progress "Stoppe Web-Container..."
|
||
docker-compose stop web 2>/dev/null || true
|
||
|
||
# Lösche und erstelle Datenbank neu
|
||
show_progress "Lösche bestehende Datenbank..."
|
||
docker-compose exec -T db psql -U fbf -d postgres -c "DROP DATABASE IF EXISTS db_fbf;" 2>/dev/null || true
|
||
|
||
show_progress "Erstelle neue Datenbank..."
|
||
docker-compose exec -T db psql -U fbf -d postgres -c "CREATE DATABASE db_fbf;" 2>/dev/null
|
||
|
||
log_success "Datenbank zurückgesetzt"
|
||
}
|
||
|
||
# Live-Daten importieren
|
||
import_live_data() {
|
||
local backup_file="$1"
|
||
|
||
log_info "Importiere Live-Daten..."
|
||
|
||
# Kopiere Backup in Container
|
||
show_progress "Kopiere Backup-Datei in Container..."
|
||
docker cp "$backup_file" django_fbf_db_1:/tmp/backup.sql
|
||
|
||
# Importiere SQL Backup
|
||
show_progress "Importiere SQL-Daten..."
|
||
if docker-compose exec -T db psql -U fbf -d db_fbf -f /tmp/backup.sql >/dev/null 2>&1; then
|
||
log_success "Live-Daten erfolgreich importiert"
|
||
else
|
||
log_error "Fehler beim Importieren der Live-Daten"
|
||
exit 1
|
||
fi
|
||
|
||
# Bereinige temporäre Datei
|
||
docker-compose exec -T db rm /tmp/backup.sql 2>/dev/null || true
|
||
}
|
||
|
||
# Django Migrationen anwenden
|
||
apply_django_migrations() {
|
||
log_info "Starte Web-Container und wende Django-Migrationen an..."
|
||
|
||
# Starte Web-Container
|
||
show_progress "Starte Web-Container..."
|
||
docker-compose up -d web
|
||
|
||
# Warte auf Web-Container
|
||
local timeout=120
|
||
local count=0
|
||
while [ $count -lt $timeout ]; do
|
||
if docker-compose exec -T web python manage.py check --database default &>/dev/null; then
|
||
break
|
||
fi
|
||
echo -n "."
|
||
sleep 1
|
||
((count++))
|
||
done
|
||
|
||
if [ $count -ge $timeout ]; then
|
||
log_error "Web-Container konnte nicht gestartet werden"
|
||
exit 1
|
||
fi
|
||
|
||
# Prüfe und wende Migrationen an
|
||
show_progress "Prüfe Migration Status..."
|
||
docker-compose exec -T web python manage.py showmigrations --plan > /tmp/migration_status.txt 2>/dev/null || true
|
||
|
||
show_progress "Wende fehlende Migrationen an..."
|
||
if docker-compose exec -T web python manage.py migrate >/dev/null 2>&1; then
|
||
log_success "Django-Migrationen erfolgreich angewendet"
|
||
else
|
||
log_warning "Einige Migrationen konnten nicht angewendet werden (möglicherweise bereits vorhanden)"
|
||
fi
|
||
}
|
||
|
||
# Datenintegrität prüfen
|
||
verify_data_integrity() {
|
||
log_info "Überprüfe Datenintegrität..."
|
||
|
||
# Zähle Datensätze in wichtigen Tabellen
|
||
local bird_count=$(docker-compose exec -T web python manage.py shell -c "
|
||
from bird.models import Bird
|
||
print(Bird.objects.count())
|
||
" 2>/dev/null | tail -1 | tr -d '\r')
|
||
|
||
local fallen_bird_count=$(docker-compose exec -T web python manage.py shell -c "
|
||
from bird.models import FallenBird
|
||
print(FallenBird.objects.count())
|
||
" 2>/dev/null | tail -1 | tr -d '\r')
|
||
|
||
local user_count=$(docker-compose exec -T web python manage.py shell -c "
|
||
from django.contrib.auth.models import User
|
||
print(User.objects.count())
|
||
" 2>/dev/null | tail -1 | tr -d '\r')
|
||
|
||
local contact_count=$(docker-compose exec -T web python manage.py shell -c "
|
||
from contact.models import Contact
|
||
print(Contact.objects.count())
|
||
" 2>/dev/null | tail -1 | tr -d '\r')
|
||
|
||
echo ""
|
||
log_success "Datenintegrität überprüft:"
|
||
echo " 📦 Vögel: $bird_count"
|
||
echo " 🐦 Fundvögel: $fallen_bird_count"
|
||
echo " 👤 Benutzer: $user_count"
|
||
echo " 📞 Kontakte: $contact_count"
|
||
}
|
||
|
||
# Admin-Benutzer konfigurieren
|
||
setup_admin_user() {
|
||
log_info "Konfiguriere Admin-Benutzer..."
|
||
|
||
docker-compose exec -T web python manage.py shell -c "
|
||
from django.contrib.auth.models import User
|
||
admin_user = User.objects.filter(is_superuser=True).first()
|
||
if admin_user:
|
||
admin_user.set_password('admin')
|
||
admin_user.save()
|
||
print(f'Admin-Passwort für \"{admin_user.username}\" wurde auf \"admin\" gesetzt')
|
||
else:
|
||
User.objects.create_superuser('admin', 'admin@example.com', 'admin')
|
||
print('Neuer Admin-Benutzer \"admin\" wurde erstellt')
|
||
" 2>/dev/null
|
||
|
||
log_success "Admin-Benutzer konfiguriert"
|
||
}
|
||
|
||
# Sammle Static Files
|
||
collect_static_files() {
|
||
log_info "Sammle Static Files..."
|
||
|
||
if docker-compose exec -T web python manage.py collectstatic --noinput >/dev/null 2>&1; then
|
||
log_success "Static Files gesammelt"
|
||
else
|
||
log_warning "Fehler beim Sammeln der Static Files"
|
||
fi
|
||
}
|
||
|
||
# Migration Zusammenfassung
|
||
show_migration_summary() {
|
||
echo ""
|
||
echo -e "${GREEN}╔══════════════════════════════════════════════════════════════╗"
|
||
echo -e "║ Migration erfolgreich! ║"
|
||
echo -e "╚══════════════════════════════════════════════════════════════╝${NC}"
|
||
echo ""
|
||
log_success "Die Live-Daten wurden erfolgreich in den aktuellen Entwicklungsstand migriert"
|
||
echo ""
|
||
echo -e "${BLUE}📋 Systemzugriff:${NC}"
|
||
echo " 🌐 Anwendung: http://localhost:8008"
|
||
echo " 🔧 Admin-Panel: http://localhost:8008/admin"
|
||
echo ""
|
||
echo -e "${BLUE}👤 Admin-Login:${NC}"
|
||
echo " Benutzername: admin"
|
||
echo " Passwort: admin"
|
||
echo ""
|
||
echo -e "${BLUE}📝 Nützliche Befehle:${NC}"
|
||
echo " docker-compose logs -f web # Logs anzeigen"
|
||
echo " docker-compose down # Projekt stoppen"
|
||
echo " docker-compose up -d # Projekt starten"
|
||
echo ""
|
||
|
||
if [ -f ".last_backup_file" ]; then
|
||
local backup_file=$(cat .last_backup_file)
|
||
echo -e "${YELLOW}💾 Backup der vorherigen Datenbank: $backup_file${NC}"
|
||
echo ""
|
||
fi
|
||
}
|
||
|
||
# Fehlerbehandlung für Rollback
|
||
rollback_migration() {
|
||
local backup_file="$1"
|
||
|
||
if [ -f ".last_backup_file" ]; then
|
||
local last_backup=$(cat .last_backup_file)
|
||
log_warning "Stelle vorherige Datenbank wieder her..."
|
||
|
||
docker-compose stop web 2>/dev/null || true
|
||
docker-compose exec -T db psql -U fbf -d postgres -c "DROP DATABASE IF EXISTS db_fbf;" 2>/dev/null || true
|
||
docker-compose exec -T db psql -U fbf -d postgres -c "CREATE DATABASE db_fbf;" 2>/dev/null
|
||
|
||
if [ -f "$last_backup" ]; then
|
||
docker cp "$last_backup" django_fbf_db_1:/tmp/restore.sql
|
||
docker-compose exec -T db psql -U fbf -d db_fbf -f /tmp/restore.sql >/dev/null 2>&1
|
||
docker-compose exec -T db rm /tmp/restore.sql 2>/dev/null || true
|
||
log_success "Vorherige Datenbank wiederhergestellt"
|
||
fi
|
||
|
||
rm .last_backup_file
|
||
fi
|
||
}
|
||
|
||
# Trap für Fehlerbehandlung
|
||
trap 'log_error "Migration fehlgeschlagen. Führe Rollback durch..."; rollback_migration' ERR
|
||
|
||
# Hauptfunktion
|
||
main() {
|
||
local backup_file="${1:-fbf-backup.sql}"
|
||
|
||
print_banner
|
||
|
||
check_prerequisites
|
||
validate_backup_file "$backup_file"
|
||
check_containers
|
||
backup_current_db
|
||
reset_database
|
||
import_live_data "$backup_file"
|
||
apply_django_migrations
|
||
verify_data_integrity
|
||
setup_admin_user
|
||
collect_static_files
|
||
show_migration_summary
|
||
|
||
# Bereinige temporäre Dateien
|
||
rm -f .last_backup_file /tmp/migration_status.txt
|
||
}
|
||
|
||
# Hilfe anzeigen
|
||
show_help() {
|
||
echo "Django FBF Live Data Migration Script"
|
||
echo ""
|
||
echo "Verwendung: $0 [OPTIONEN] [BACKUP-DATEI]"
|
||
echo ""
|
||
echo "OPTIONEN:"
|
||
echo " -h, --help Zeige diese Hilfe an"
|
||
echo ""
|
||
echo "BACKUP-DATEI:"
|
||
echo " Pfad zur fbf-backup.sql Datei (Standard: fbf-backup.sql)"
|
||
echo ""
|
||
echo "Beispiele:"
|
||
echo " $0 # Verwendet fbf-backup.sql"
|
||
echo " $0 /path/to/backup.sql # Verwendet spezifische Backup-Datei"
|
||
echo ""
|
||
}
|
||
|
||
# Kommandozeilen-Parameter verarbeiten
|
||
case "${1:-}" in
|
||
-h|--help)
|
||
show_help
|
||
exit 0
|
||
;;
|
||
*)
|
||
main "$@"
|
||
;;
|
||
esac
|