#!/bin/bash # Frontend-Deployment-Skript für MYP-Projekt # Bietet verschiedene Möglichkeiten, das Frontend zu deployen # Farbcodes für Ausgabe RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[0;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' NC='\033[0m' # No Color # Funktion zur Ausgabe mit Zeitstempel log() { echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1" } error_log() { echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] FEHLER:${NC} $1" >&2 } success_log() { echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] ERFOLG:${NC} $1" } header() { echo "" echo -e "${CYAN}===== $1 =====${NC}" echo "" } # Variablen definieren SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" FRONTEND_DIR="$SCRIPT_DIR/packages/reservation-platform" DOCKER_DIR="$FRONTEND_DIR/docker" DEFAULT_BACKEND_URL="http://192.168.0.105:5000" IMAGE_NAME="myp-rp:latest" CONTAINER_NAME="myp-rp" DB_VOLUME_DIR="/srv/MYP-DB" ENV_FILE_PATH="/srv/myp-env/github.env" # Prüfen, ob wir im Root des Projektverzeichnisses sind if [ ! -d "packages/reservation-platform" ]; then error_log "Dieses Skript muss im Root-Verzeichnis des MYP-Projekts ausgeführt werden." error_log "Bitte wechseln Sie in das Verzeichnis, das die 'packages/reservation-platform' enthält." exit 1 fi # Prüfen, ob Docker installiert ist if ! command -v docker &> /dev/null; then error_log "Docker ist nicht installiert. Bitte installieren Sie Docker zuerst." exit 1 fi # Prüfen, ob Docker läuft if ! docker info &> /dev/null; then error_log "Docker-Daemon läuft nicht. Bitte starten Sie Docker mit 'sudo systemctl start docker'." exit 1 fi # Prüfen, ob der Benutzer in der Docker-Gruppe ist if ! groups | grep -q '\bdocker\b'; then error_log "Aktueller Benutzer hat keine Docker-Berechtigungen." error_log "Bitte führen Sie das Skript mit 'sudo' aus oder fügen Sie den Benutzer zur Docker-Gruppe hinzu:" error_log "sudo usermod -aG docker $USER && newgrp docker" exit 1 fi # Erstelle Datenbank-Verzeichnis, falls nicht vorhanden if [ ! -d "$DB_VOLUME_DIR" ]; then log "Erstelle Datenbankverzeichnis: $DB_VOLUME_DIR" if ! mkdir -p "$DB_VOLUME_DIR"; then if ! sudo mkdir -p "$DB_VOLUME_DIR"; then error_log "Konnte Datenbankverzeichnis nicht erstellen. Bitte erstellen Sie es manuell:" error_log "sudo mkdir -p $DB_VOLUME_DIR && sudo chown $USER:$USER $DB_VOLUME_DIR" exit 1 fi sudo chown $USER:$USER "$DB_VOLUME_DIR" fi else log "Datenbankverzeichnis existiert bereits: $DB_VOLUME_DIR" fi # Funktion zum Laden der Umgebungsvariablen aus /srv/myp-env/github.env load_env_from_srv() { if [ -f "$ENV_FILE_PATH" ]; then log "Lade Umgebungsvariablen aus $ENV_FILE_PATH" # Versuche, die Variablen aus der Datei zu laden OAUTH_CLIENT_ID=$(grep -oP 'OAUTH_CLIENT_ID=\K.*' "$ENV_FILE_PATH" 2>/dev/null || echo "client_id") OAUTH_CLIENT_SECRET=$(grep -oP 'OAUTH_CLIENT_SECRET=\K.*' "$ENV_FILE_PATH" 2>/dev/null || echo "client_secret") # Prüfe, ob die Backend-URL in der Datei definiert ist BACKEND_URL_FROM_FILE=$(grep -oP 'NEXT_PUBLIC_API_URL=\K.*' "$ENV_FILE_PATH" 2>/dev/null) if [ -n "$BACKEND_URL_FROM_FILE" ]; then log "Backend-URL aus $ENV_FILE_PATH geladen: $BACKEND_URL_FROM_FILE" DEFAULT_BACKEND_URL="$BACKEND_URL_FROM_FILE" fi success_log "OAuth-Konfiguration aus $ENV_FILE_PATH geladen." else log "${YELLOW}Warnung: $ENV_FILE_PATH nicht gefunden. Verwende Standard-Konfiguration.${NC}" OAUTH_CLIENT_ID="client_id" OAUTH_CLIENT_SECRET="client_secret" fi } # Funktion zum Konfigurieren der Backend-URL configure_backend_url() { local backend_url="${1:-$DEFAULT_BACKEND_URL}" header "Backend-URL konfigurieren" log "Konfiguriere Backend-URL für Frontend: $backend_url" # Lade OAuth-Konfiguration aus /srv load_env_from_srv # Prüfen, ob setup-backend-url.sh existiert if [ -f "$FRONTEND_DIR/setup-backend-url.sh" ]; then chmod +x "$FRONTEND_DIR/setup-backend-url.sh" if ! "$FRONTEND_DIR/setup-backend-url.sh" "$backend_url"; then error_log "Fehler beim Konfigurieren der Backend-URL." return 1 fi else # Bestimme den Hostnamen für OAuth HOSTNAME=$(hostname) if [[ "$HOSTNAME" == *"m040tbaraspi001"* ]] || [[ "$HOSTNAME" == *"corpintra"* ]]; then FRONTEND_HOSTNAME="m040tbaraspi001.de040.corpintra.net" OAUTH_URL="http://m040tbaraspi001.de040.corpintra.net/auth/login/callback" log "Erkannt: Unternehmens-Hostname: $FRONTEND_HOSTNAME" else FRONTEND_HOSTNAME="$HOSTNAME" OAUTH_URL="http://$HOSTNAME:3000/auth/login/callback" log "Lokaler Hostname: $FRONTEND_HOSTNAME" fi # Erstelle .env.local-Datei manuell log "Erstelle .env.local-Datei manuell..." cat > "$FRONTEND_DIR/.env.local" << EOL # Backend API Konfiguration NEXT_PUBLIC_API_URL=${backend_url} # Frontend-URL für OAuth Callback NEXT_PUBLIC_FRONTEND_URL=http://${FRONTEND_HOSTNAME} # Explizite OAuth Callback URL für GitHub NEXT_PUBLIC_OAUTH_CALLBACK_URL=${OAUTH_URL} # OAuth Konfiguration aus /srv/myp-env/github.env OAUTH_CLIENT_ID=${OAUTH_CLIENT_ID} OAUTH_CLIENT_SECRET=${OAUTH_CLIENT_SECRET} EOL if [ ! -f "$FRONTEND_DIR/.env.local" ]; then error_log "Konnte .env.local-Datei nicht erstellen." return 1 fi chmod 600 "$FRONTEND_DIR/.env.local" fi success_log "Backend-URL erfolgreich konfiguriert: $backend_url" return 0 } # Funktion zum Bauen des Images build_image() { header "Docker-Image bauen" log "Baue Docker-Image: $IMAGE_NAME" if [ ! -f "$FRONTEND_DIR/Dockerfile" ]; then error_log "Dockerfile nicht gefunden in $FRONTEND_DIR" return 1 fi cd "$FRONTEND_DIR" || return 1 # Vorhandenes Image entfernen, falls gewünscht if docker image inspect "$IMAGE_NAME" &>/dev/null; then log "Image $IMAGE_NAME existiert bereits." read -p "Möchten Sie das existierende Image überschreiben? (j/n): " rebuild_choice if [[ "$rebuild_choice" == "j" ]]; then log "Entferne existierendes Image..." docker rmi "$IMAGE_NAME" &>/dev/null || true else log "Behalte existierendes Image." return 0 fi fi # Baue das Image log "${YELLOW}Baue Docker-Image... (Dies kann auf einem Raspberry Pi mehrere Minuten dauern)${NC}" if ! docker build -t "$IMAGE_NAME" .; then error_log "Fehler beim Bauen des Docker-Images." return 1 fi success_log "Docker-Image erfolgreich gebaut: $IMAGE_NAME" return 0 } # Funktion zum Speichern des Images save_image() { header "Docker-Image speichern" local save_dir="${1:-$DOCKER_DIR/images}" local save_file="$save_dir/myp-frontend.tar" # Prüfen, ob das Image existiert if ! docker image inspect "$IMAGE_NAME" &>/dev/null; then error_log "Image $IMAGE_NAME existiert nicht. Bitte bauen Sie es zuerst." return 1 fi # Verzeichnis erstellen, falls es nicht existiert mkdir -p "$save_dir" log "Speichere Docker-Image in: $save_file" log "${YELLOW}Dies kann einige Minuten dauern...${NC}" if ! docker save -o "$save_file" "$IMAGE_NAME"; then error_log "Fehler beim Speichern des Docker-Images." return 1 fi # Prüfe, ob die Datei erstellt wurde if [ ! -f "$save_file" ]; then error_log "Konnte Docker-Image nicht speichern." return 1 fi # Prüfe Dateigröße local filesize=$(stat -c%s "$save_file") if [ "$filesize" -lt 1000000 ]; then # Kleiner als 1MB ist verdächtig error_log "Gespeichertes Image ist ungewöhnlich klein ($filesize Bytes). Möglicherweise ist etwas schief gelaufen." return 1 fi success_log "Docker-Image erfolgreich gespeichert: $save_file (Größe: $(du -h "$save_file" | cut -f1))" return 0 } # Funktion zum Laden des Images load_image() { header "Docker-Image laden" local load_dir="${1:-$DOCKER_DIR/images}" local load_file="$load_dir/myp-frontend.tar" # Prüfen, ob die Datei existiert if [ ! -f "$load_file" ]; then error_log "Image-Datei nicht gefunden: $load_file" return 1 fi # Prüfe Dateigröße local filesize=$(stat -c%s "$load_file") if [ "$filesize" -lt 1000000 ]; then # Kleiner als 1MB ist verdächtig error_log "Image-Datei ist ungewöhnlich klein ($filesize Bytes). Möglicherweise ist sie beschädigt." return 1 fi log "Lade Docker-Image aus: $load_file" log "${YELLOW}Dies kann einige Minuten dauern...${NC}" if ! docker load -i "$load_file"; then error_log "Fehler beim Laden des Docker-Images. Die Datei könnte beschädigt sein." return 1 fi success_log "Docker-Image erfolgreich geladen." return 0 } # Funktion zum Starten des Containers mit Docker Compose start_container_compose() { header "Container mit Docker Compose starten" # Erstellen der vereinfachten docker-compose.yml-Datei local compose_file="$DOCKER_DIR/compose.simple.yml" # Lade OAuth-Konfiguration aus /srv, falls noch nicht geschehen if [ -z "$OAUTH_CLIENT_ID" ]; then load_env_from_srv fi log "Erstelle vereinfachte Docker-Compose-Datei: $compose_file" cat > "$compose_file" << EOL services: myp-rp: image: ${IMAGE_NAME} container_name: ${CONTAINER_NAME} environment: - NEXT_PUBLIC_API_URL=${BACKEND_URL} - NEXT_PUBLIC_FRONTEND_URL=http://${FRONTEND_HOSTNAME} - NEXT_PUBLIC_OAUTH_CALLBACK_URL=${OAUTH_URL} - OAUTH_CLIENT_ID=${OAUTH_CLIENT_ID} - OAUTH_CLIENT_SECRET=${OAUTH_CLIENT_SECRET} env_file: "${ENV_FILE_PATH}" ports: - "3000:3000" volumes: - ${DB_VOLUME_DIR}:/app/db restart: unless-stopped healthcheck: test: ["CMD", "wget", "--spider", "http://localhost:3000"] interval: 30s timeout: 10s retries: 3 start_period: 40s EOL # Prüfen, ob die Datei erstellt wurde if [ ! -f "$compose_file" ]; then error_log "Konnte Docker-Compose-Datei nicht erstellen." return 1 fi # Stoppen des vorhandenen Containers if docker ps -a | grep -q "$CONTAINER_NAME"; then log "Stoppe und entferne existierenden Container..." docker stop "$CONTAINER_NAME" &>/dev/null || true docker rm "$CONTAINER_NAME" &>/dev/null || true fi # Container starten log "Starte Container..." cd "$DOCKER_DIR" || return 1 if ! docker compose -f "$(basename "$compose_file")" up -d; then # Versuche mit docker-compose, falls docker compose nicht funktioniert if ! docker-compose -f "$(basename "$compose_file")" up -d; then error_log "Fehler beim Starten des Containers." return 1 fi fi success_log "Container erfolgreich gestartet: $CONTAINER_NAME" return 0 } # Funktion zum Starten des Containers mit Docker Run start_container_run() { header "Container direkt starten" # Stoppen des vorhandenen Containers if docker ps -a | grep -q "$CONTAINER_NAME"; then log "Stoppe und entferne existierenden Container..." docker stop "$CONTAINER_NAME" &>/dev/null || true docker rm "$CONTAINER_NAME" &>/dev/null || true fi # Lade OAuth-Konfiguration aus /srv, falls noch nicht geschehen if [ -z "$OAUTH_CLIENT_ID" ]; then load_env_from_srv fi # Container starten log "Starte Container mit 'docker run'..." if ! docker run -d --name "$CONTAINER_NAME" \ -p 3000:3000 \ -e "NEXT_PUBLIC_API_URL=$BACKEND_URL" \ -e "NEXT_PUBLIC_FRONTEND_URL=http://${FRONTEND_HOSTNAME}" \ -e "NEXT_PUBLIC_OAUTH_CALLBACK_URL=${OAUTH_URL}" \ -e "OAUTH_CLIENT_ID=${OAUTH_CLIENT_ID}" \ -e "OAUTH_CLIENT_SECRET=${OAUTH_CLIENT_SECRET}" \ --env-file "$ENV_FILE_PATH" \ -v "$DB_VOLUME_DIR:/app/db" \ --restart unless-stopped \ "$IMAGE_NAME"; then error_log "Fehler beim Starten des Containers." return 1 fi success_log "Container erfolgreich gestartet: $CONTAINER_NAME" return 0 } # Funktion zum Starten der Anwendung ohne Docker start_without_docker() { header "Anwendung ohne Docker starten" cd "$FRONTEND_DIR" || return 1 # Prüfen, ob Node.js und pnpm installiert sind if ! command -v node &> /dev/null; then error_log "Node.js ist nicht installiert. Bitte installieren Sie Node.js zuerst." return 1 fi if ! command -v pnpm &> /dev/null; then log "pnpm ist nicht installiert. Installiere pnpm..." npm install -g pnpm if [ $? -ne 0 ]; then error_log "Fehler beim Installieren von pnpm." return 1 fi fi # Installiere Abhängigkeiten log "Installiere Abhängigkeiten..." if ! pnpm install; then error_log "Fehler beim Installieren der Abhängigkeiten." return 1 fi # Lade OAuth-Konfiguration aus /srv und konfiguriere die Backend-URL load_env_from_srv if ! configure_backend_url "$BACKEND_URL"; then error_log "Fehler beim Konfigurieren der Backend-URL." return 1 fi # Baue und starte die Anwendung log "Baue und starte die Anwendung..." if ! pnpm build; then log "${YELLOW}Warnung: Build fehlgeschlagen. Versuche, im Dev-Modus zu starten...${NC}" fi # Starte im Screen-Session, damit die Anwendung im Hintergrund läuft if command -v screen &> /dev/null; then log "Starte Anwendung in Screen-Session..." screen -dmS myp-frontend bash -c "cd $FRONTEND_DIR && \ NEXT_PUBLIC_API_URL=$BACKEND_URL \ NEXT_PUBLIC_FRONTEND_URL=http://${FRONTEND_HOSTNAME} \ NEXT_PUBLIC_OAUTH_CALLBACK_URL=${OAUTH_URL} \ OAUTH_CLIENT_ID=${OAUTH_CLIENT_ID} \ OAUTH_CLIENT_SECRET=${OAUTH_CLIENT_SECRET} \ pnpm start || \ NEXT_PUBLIC_API_URL=$BACKEND_URL \ NEXT_PUBLIC_FRONTEND_URL=http://${FRONTEND_HOSTNAME} \ NEXT_PUBLIC_OAUTH_CALLBACK_URL=${OAUTH_URL} \ OAUTH_CLIENT_ID=${OAUTH_CLIENT_ID} \ OAUTH_CLIENT_SECRET=${OAUTH_CLIENT_SECRET} \ pnpm dev" success_log "Anwendung im Hintergrund gestartet. Verbinden mit: screen -r myp-frontend" else log "${YELLOW}Screen ist nicht installiert. Starte Anwendung im Vordergrund...${NC}" log "${YELLOW}Beenden mit Strg+C. Die Anwendung wird dann beendet.${NC}" sleep 3 export NEXT_PUBLIC_API_URL="$BACKEND_URL" export NEXT_PUBLIC_FRONTEND_URL="http://${FRONTEND_HOSTNAME}" export NEXT_PUBLIC_OAUTH_CALLBACK_URL="${OAUTH_URL}" export OAUTH_CLIENT_ID="${OAUTH_CLIENT_ID}" export OAUTH_CLIENT_SECRET="${OAUTH_CLIENT_SECRET}" pnpm start || pnpm dev fi return 0 } # Funktion für das Hauptmenü main_menu() { local choice header "MYP Frontend Deployment" echo "Bitte wählen Sie eine Option:" echo "" echo "1) Alles automatisch (Build, Deploy, Starten)" echo "2) Docker-Image bauen" echo "3) Docker-Image speichern" echo "4) Docker-Image laden" echo "5) Container mit Docker Compose starten" echo "6) Container direkt mit Docker Run starten" echo "7) Anwendung ohne Docker starten" echo "8) Nur Backend-URL konfigurieren" echo "9) Beenden" echo "" read -p "Ihre Wahl (1-9): " choice case $choice in 1) auto_deploy ;; 2) build_image ;; 3) save_image ;; 4) load_image ;; 5) configure_backend_url && start_container_compose ;; 6) configure_backend_url && start_container_run ;; 7) start_without_docker ;; 8) configure_backend_url ;; 9) log "Beende das Programm." && exit 0 ;; *) error_log "Ungültige Auswahl. Bitte versuchen Sie es erneut." && main_menu ;; esac # Zurück zum Hauptmenü, es sei denn, der Benutzer hat das Programm beendet if [ $choice -ne 9 ]; then read -p "Drücken Sie Enter, um zum Hauptmenü zurückzukehren..." main_menu fi } # Automatischer Deployment-Workflow auto_deploy() { header "Automatisches Deployment" log "Starte automatischen Deployment-Workflow..." # Konfiguriere Backend-URL if ! configure_backend_url; then error_log "Fehler beim Konfigurieren der Backend-URL." return 1 fi # Versuche zunächst, das Image zu laden local load_dir="$DOCKER_DIR/images" local load_file="$load_dir/myp-frontend.tar" if [ -f "$load_file" ]; then log "Image-Datei gefunden. Versuche zu laden..." if load_image; then log "Image erfolgreich geladen. Überspringe Bauen." else log "Konnte Image nicht laden. Versuche zu bauen..." if ! build_image; then error_log "Automatisches Deployment fehlgeschlagen beim Bauen des Images." return 1 fi fi else log "Keine Image-Datei gefunden. Baue neues Image..." if ! build_image; then error_log "Automatisches Deployment fehlgeschlagen beim Bauen des Images." return 1 fi fi # Speichere das Image für zukünftige Verwendung log "Speichere Image für zukünftige Verwendung..." save_image # Starte den Container log "Starte Container..." if ! start_container_compose; then error_log "Konnte Container nicht mit Docker Compose starten. Versuche direkten Start..." if ! start_container_run; then error_log "Automatisches Deployment fehlgeschlagen beim Starten des Containers." return 1 fi fi success_log "Automatisches Deployment erfolgreich abgeschlossen!" log "Frontend ist unter http://localhost:3000 erreichbar" log "API-Kommunikation mit Backend: $BACKEND_URL" return 0 } # Hauptanwendung # Zuerst nach Backend-URL fragen header "Backend-URL Konfiguration" log "Standard-Backend-URL: $DEFAULT_BACKEND_URL" read -p "Möchten Sie eine andere Backend-URL verwenden? (j/n): " change_url_choice if [[ "$change_url_choice" == "j" ]]; then read -p "Geben Sie die neue Backend-URL ein (z.B. http://192.168.0.105:5000): " custom_url if [ -n "$custom_url" ]; then BACKEND_URL="$custom_url" log "Verwende benutzerdefinierte Backend-URL: $BACKEND_URL" else BACKEND_URL="$DEFAULT_BACKEND_URL" log "Leere Eingabe. Verwende Standard-Backend-URL: $BACKEND_URL" fi else BACKEND_URL="$DEFAULT_BACKEND_URL" log "Verwende Standard-Backend-URL: $BACKEND_URL" fi # Anzeigen des Hauptmenüs main_menu