2208 lines
74 KiB
Bash
2208 lines
74 KiB
Bash
#!/bin/bash
|
|
|
|
# ===================================================================
|
|
# MYP Druckerverwaltung - KONSOLIDIERTES SETUP-SKRIPT
|
|
# Kombiniert alle Installationsfunktionen in einer einzigen Datei
|
|
# Optimiert für Debian/Linux (Raspberry Pi OS) - KEIN Windows-Support
|
|
# HTTPS auf Port 443 mit automatischer SSL-Zertifikat-Generierung
|
|
# Kiosk-Modus mit Chromium-Autostart ohne Desktop-Environment
|
|
# Version: 4.0.0
|
|
# ===================================================================
|
|
|
|
set -euo pipefail
|
|
|
|
# =========================== GLOBALE KONFIGURATION ===========================
|
|
readonly APP_NAME="MYP Druckerverwaltung"
|
|
readonly APP_VERSION="4.0.0"
|
|
readonly APP_DIR="/opt/myp"
|
|
readonly HTTPS_SERVICE_NAME="myp-https"
|
|
readonly KIOSK_SERVICE_NAME="myp-kiosk"
|
|
readonly WATCHDOG_SERVICE_NAME="kiosk-watchdog"
|
|
readonly WATCHDOG_PYTHON_SERVICE_NAME="kiosk-watchdog-python"
|
|
readonly FIREWALL_SERVICE_NAME="myp-firewall"
|
|
readonly KIOSK_USER="kiosk"
|
|
readonly CURRENT_DIR="$(pwd)"
|
|
readonly INSTALL_LOG="/var/log/myp-install.log"
|
|
readonly HTTPS_PORT="443"
|
|
readonly HTTPS_URL="https://localhost:${HTTPS_PORT}"
|
|
readonly SYSTEMD_DIR="$CURRENT_DIR/systemd"
|
|
readonly SYSTEM_SYSTEMD_DIR="/etc/systemd/system"
|
|
|
|
# Farben für Ausgabe
|
|
readonly RED='\033[0;31m'
|
|
readonly GREEN='\033[0;32m'
|
|
readonly YELLOW='\033[1;33m'
|
|
readonly BLUE='\033[0;34m'
|
|
readonly PURPLE='\033[0;35m'
|
|
readonly CYAN='\033[0;36m'
|
|
readonly NC='\033[0m'
|
|
|
|
# =========================== LOGGING-FUNKTIONEN ===========================
|
|
log() {
|
|
echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] $1${NC}" | tee -a "$INSTALL_LOG"
|
|
}
|
|
|
|
error() {
|
|
echo -e "${RED}[FEHLER] $1${NC}" | tee -a "$INSTALL_LOG"
|
|
exit 1
|
|
}
|
|
|
|
warning() {
|
|
echo -e "${YELLOW}[WARNUNG] $1${NC}" | tee -a "$INSTALL_LOG"
|
|
}
|
|
|
|
info() {
|
|
echo -e "${BLUE}[INFO] $1${NC}" | tee -a "$INSTALL_LOG"
|
|
}
|
|
|
|
progress() {
|
|
echo -e "${PURPLE}[FORTSCHRITT] $1${NC}" | tee -a "$INSTALL_LOG"
|
|
}
|
|
|
|
success() {
|
|
echo -e "${CYAN}[ERFOLG] $1${NC}" | tee -a "$INSTALL_LOG"
|
|
}
|
|
|
|
# =========================== SYSTEM-VALIDIERUNG ===========================
|
|
check_root() {
|
|
if [ "$EUID" -ne 0 ]; then
|
|
error "Dieses Skript muss als Root ausgeführt werden: sudo $0"
|
|
fi
|
|
export PATH="/usr/sbin:/sbin:/usr/bin:/bin:/usr/local/bin:$PATH"
|
|
log "✅ Root-Berechtigung bestätigt"
|
|
}
|
|
|
|
check_debian_system() {
|
|
if [ ! -f /etc/debian_version ]; then
|
|
error "Dieses Skript ist nur für Debian/Raspbian-Systeme geeignet!"
|
|
fi
|
|
|
|
local debian_version=$(cat /etc/debian_version 2>/dev/null || echo "Unbekannt")
|
|
log "✅ Debian/Raspbian-System erkannt (Version: $debian_version)"
|
|
|
|
# Prüfe auf Raspberry Pi
|
|
if [ -f /proc/device-tree/model ]; then
|
|
local pi_model=$(cat /proc/device-tree/model 2>/dev/null || echo "Unbekannt")
|
|
info "Raspberry Pi Modell: $pi_model"
|
|
fi
|
|
}
|
|
|
|
check_internet_connection() {
|
|
progress "Prüfe Internetverbindung..."
|
|
|
|
local test_urls=("8.8.8.8" "1.1.1.1" "google.com")
|
|
local connection_ok=false
|
|
|
|
for url in "${test_urls[@]}"; do
|
|
if ping -c 1 -W 3 "$url" >/dev/null 2>&1; then
|
|
connection_ok=true
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [ "$connection_ok" = true ]; then
|
|
log "✅ Internetverbindung verfügbar"
|
|
else
|
|
warning "⚠️ Keine Internetverbindung - Installation könnte fehlschlagen"
|
|
fi
|
|
}
|
|
|
|
# =========================== SYSTEM-VORBEREITUNG ===========================
|
|
update_system() {
|
|
log "=== SYSTEM-UPDATE ==="
|
|
|
|
progress "Aktualisiere Paketlisten..."
|
|
apt-get update -y || error "APT Update fehlgeschlagen"
|
|
|
|
progress "Führe System-Upgrade durch..."
|
|
apt-get upgrade -y || warning "System-Upgrade teilweise fehlgeschlagen"
|
|
|
|
progress "Installiere grundlegende System-Tools..."
|
|
apt-get install -y \
|
|
curl \
|
|
wget \
|
|
git \
|
|
nano \
|
|
htop \
|
|
rsync \
|
|
unzip \
|
|
sudo \
|
|
systemd \
|
|
ca-certificates \
|
|
gnupg \
|
|
lsb-release \
|
|
apt-transport-https \
|
|
software-properties-common \
|
|
bc \
|
|
|| error "Grundlegende Tools Installation fehlgeschlagen"
|
|
|
|
log "✅ System-Update abgeschlossen"
|
|
}
|
|
|
|
# =========================== NETZWERK-SICHERHEIT ===========================
|
|
configure_network_security() {
|
|
log "=== KONFIGURIERE ERWEITERTE NETZWERK-SICHERHEIT ==="
|
|
|
|
# IPv6 vollständig deaktivieren
|
|
progress "Deaktiviere IPv6..."
|
|
|
|
# IPv6 in GRUB deaktivieren
|
|
if [ -f /etc/default/grub ]; then
|
|
cp /etc/default/grub /etc/default/grub.backup
|
|
sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT="[^"]*/& ipv6.disable=1/' /etc/default/grub
|
|
sed -i 's/GRUB_CMDLINE_LINUX="[^"]*/& ipv6.disable=1/' /etc/default/grub
|
|
update-grub 2>/dev/null || true
|
|
fi
|
|
|
|
# IPv6 in sysctl deaktivieren
|
|
cat >> /etc/sysctl.conf << 'EOF'
|
|
|
|
# ===================================================================
|
|
# MYP Netzwerk-Sicherheitskonfiguration
|
|
# ===================================================================
|
|
|
|
# IPv6 vollständig deaktivieren
|
|
net.ipv6.conf.all.disable_ipv6 = 1
|
|
net.ipv6.conf.default.disable_ipv6 = 1
|
|
net.ipv6.conf.lo.disable_ipv6 = 1
|
|
|
|
# IP-Spoofing-Schutz aktivieren
|
|
net.ipv4.conf.all.rp_filter = 1
|
|
net.ipv4.conf.default.rp_filter = 1
|
|
|
|
# Source-Routing deaktivieren (verhindert IP-Spoofing)
|
|
net.ipv4.conf.all.accept_source_route = 0
|
|
net.ipv4.conf.default.accept_source_route = 0
|
|
|
|
# ICMP-Redirects ignorieren (verhindert Man-in-the-Middle)
|
|
net.ipv4.conf.all.accept_redirects = 0
|
|
net.ipv4.conf.default.accept_redirects = 0
|
|
net.ipv4.conf.all.send_redirects = 0
|
|
|
|
# Secure ICMP-Redirects ignorieren
|
|
net.ipv4.conf.all.secure_redirects = 0
|
|
net.ipv4.conf.default.secure_redirects = 0
|
|
|
|
# SYN-Flood-Schutz aktivieren
|
|
net.ipv4.tcp_syncookies = 1
|
|
net.ipv4.tcp_max_syn_backlog = 2048
|
|
net.ipv4.tcp_synack_retries = 2
|
|
net.ipv4.tcp_syn_retries = 5
|
|
|
|
# TCP-RFC-Compliance (verhindert aggressive Paketwiederholungen)
|
|
net.ipv4.tcp_retries1 = 3
|
|
net.ipv4.tcp_retries2 = 15
|
|
net.ipv4.tcp_orphan_retries = 3
|
|
|
|
# TCP-Window-Skalierung optimieren (geringere Netzwerkauslastung)
|
|
net.ipv4.tcp_window_scaling = 1
|
|
net.ipv4.tcp_rmem = 4096 65536 16777216
|
|
net.ipv4.tcp_wmem = 4096 65536 16777216
|
|
net.core.rmem_max = 16777216
|
|
net.core.wmem_max = 16777216
|
|
net.core.netdev_max_backlog = 5000
|
|
|
|
# Broadcast-Pings ignorieren (verhindert Smurf-Angriffe)
|
|
net.ipv4.icmp_echo_ignore_broadcasts = 1
|
|
|
|
# Bogus ICMP-Antworten ignorieren
|
|
net.ipv4.icmp_ignore_bogus_error_responses = 1
|
|
|
|
# Paketweiterleitung verhindern (Router-Funktionalität deaktivieren)
|
|
net.ipv4.ip_forward = 0
|
|
|
|
# Martian-Pakete loggen (verdächtige Pakete)
|
|
net.ipv4.conf.all.log_martians = 1
|
|
net.ipv4.conf.default.log_martians = 1
|
|
|
|
# TCP-Timestamps deaktivieren (verhindert Fingerprinting)
|
|
net.ipv4.tcp_timestamps = 0
|
|
|
|
# TCP-SACK deaktivieren (verhindert bestimmte Angriffe)
|
|
net.ipv4.tcp_sack = 0
|
|
|
|
# TCP-Keepalive optimieren
|
|
net.ipv4.tcp_keepalive_time = 7200
|
|
net.ipv4.tcp_keepalive_probes = 9
|
|
net.ipv4.tcp_keepalive_intvl = 75
|
|
|
|
# Memory-Limits für Netzwerk-Buffers
|
|
net.core.rmem_default = 262144
|
|
net.core.wmem_default = 262144
|
|
|
|
# Schutz vor Time-Wait-Assassination
|
|
net.ipv4.tcp_rfc1337 = 1
|
|
|
|
# ===================================================================
|
|
# RASPBERRY PI PERFORMANCE-OPTIMIERUNGEN FÜR WEBAPP
|
|
# ===================================================================
|
|
|
|
# Memory Management für schwache Hardware optimieren
|
|
vm.swappiness = 10
|
|
vm.dirty_ratio = 15
|
|
vm.dirty_background_ratio = 5
|
|
vm.vfs_cache_pressure = 50
|
|
vm.min_free_kbytes = 8192
|
|
vm.overcommit_memory = 1
|
|
|
|
# CPU Scheduler für bessere Responsivität
|
|
kernel.sched_min_granularity_ns = 10000000
|
|
kernel.sched_wakeup_granularity_ns = 15000000
|
|
kernel.sched_migration_cost_ns = 5000000
|
|
|
|
# Filesystem Performance
|
|
vm.dirty_expire_centisecs = 500
|
|
vm.dirty_writeback_centisecs = 100
|
|
|
|
# Memory Compaction für bessere Speichernutzung
|
|
vm.compact_memory = 1
|
|
|
|
# OOM Killer weniger aggressiv
|
|
vm.oom_kill_allocating_task = 0
|
|
vm.panic_on_oom = 0
|
|
|
|
# Kernel Preemption für bessere Interaktivität
|
|
kernel.sched_rt_runtime_us = 950000
|
|
kernel.sched_rt_period_us = 1000000
|
|
|
|
# I/O Scheduler Optimierungen für SD-Karte
|
|
# (wird später per udev-Regel angewendet)
|
|
|
|
EOF
|
|
|
|
# Sysctl-Einstellungen sofort anwenden
|
|
sysctl -p || warning "Einige sysctl-Einstellungen konnten nicht angewendet werden"
|
|
|
|
# IPv6 in Netzwerk-Interfaces deaktivieren
|
|
progress "Deaktiviere IPv6 in Netzwerk-Interfaces..."
|
|
|
|
# Für systemd-networkd
|
|
if systemctl is-enabled systemd-networkd 2>/dev/null; then
|
|
mkdir -p /etc/systemd/network
|
|
cat > /etc/systemd/network/99-disable-ipv6.network << 'EOF'
|
|
[Match]
|
|
Name=*
|
|
|
|
[Network]
|
|
IPv6AcceptRA=no
|
|
LinkLocalAddressing=no
|
|
EOF
|
|
systemctl restart systemd-networkd 2>/dev/null || true
|
|
fi
|
|
|
|
# Für NetworkManager
|
|
if systemctl is-enabled NetworkManager 2>/dev/null; then
|
|
cat > /etc/NetworkManager/conf.d/99-disable-ipv6.conf << 'EOF'
|
|
[main]
|
|
plugins=keyfile
|
|
|
|
[keyfile]
|
|
unmanaged-devices=none
|
|
|
|
[connection]
|
|
ipv6.method=ignore
|
|
EOF
|
|
systemctl restart NetworkManager 2>/dev/null || true
|
|
fi
|
|
|
|
# IPv6 in /etc/hosts auskommentieren
|
|
sed -i 's/^::1/#::1/' /etc/hosts 2>/dev/null || true
|
|
|
|
log "✅ Erweiterte Netzwerk-Sicherheit konfiguriert:"
|
|
log " 🚫 IPv6 vollständig deaktiviert"
|
|
log " 🛡️ IP-Spoofing-Schutz aktiviert"
|
|
log " 🔒 SYN-Flood-Schutz aktiviert"
|
|
log " 📝 Verdächtige Pakete werden geloggt"
|
|
log " 🚫 Paketweiterleitung deaktiviert"
|
|
log " ⚡ TCP-Performance optimiert"
|
|
log " 🔐 RFC-Compliance für TCP aktiviert"
|
|
}
|
|
|
|
# =========================== DESKTOP-ENVIRONMENT ENTFERNUNG ===========================
|
|
remove_desktop_environments() {
|
|
log "=== ENTFERNE DESKTOP ENVIRONMENTS FÜR KIOSK-MODUS ==="
|
|
|
|
progress "Stoppe alle Desktop-Services..."
|
|
local desktop_services=("lightdm" "gdm3" "sddm" "xdm" "nodm")
|
|
|
|
for service in "${desktop_services[@]}"; do
|
|
systemctl stop "$service" 2>/dev/null || true
|
|
systemctl disable "$service" 2>/dev/null || true
|
|
done
|
|
|
|
progress "Entferne Desktop-Pakete vollständig..."
|
|
|
|
# Raspberry Pi OS Desktop-Pakete
|
|
apt-get remove --purge -y \
|
|
raspberrypi-ui-mods \
|
|
pi-package \
|
|
desktop-base \
|
|
lxde* \
|
|
xfce4* \
|
|
gnome* \
|
|
kde* \
|
|
mate* \
|
|
cinnamon* \
|
|
openbox \
|
|
pcmanfm \
|
|
file-manager* \
|
|
task-lxde-desktop \
|
|
task-xfce-desktop \
|
|
task-gnome-desktop \
|
|
task-kde-desktop \
|
|
2>/dev/null || true
|
|
|
|
# Display Manager entfernen
|
|
apt-get remove --purge -y \
|
|
lightdm* \
|
|
gdm3* \
|
|
sddm* \
|
|
xdm* \
|
|
nodm* \
|
|
2>/dev/null || true
|
|
|
|
# Unnötige Anwendungen entfernen
|
|
apt-get remove --purge -y \
|
|
libreoffice* \
|
|
thunderbird* \
|
|
firefox* \
|
|
vlc* \
|
|
gimp* \
|
|
scratch* \
|
|
minecraft-pi \
|
|
sonic-pi \
|
|
2>/dev/null || true
|
|
|
|
# Aufräumen
|
|
apt-get autoremove --purge -y
|
|
apt-get autoclean
|
|
|
|
log "✅ Desktop Environments vollständig entfernt"
|
|
}
|
|
|
|
# =========================== MINIMALE X11-UMGEBUNG ===========================
|
|
install_minimal_x11() {
|
|
log "=== INSTALLIERE MINIMALE X11-UMGEBUNG FÜR KIOSK ==="
|
|
|
|
progress "Installiere minimale X11-Pakete..."
|
|
apt-get install -y \
|
|
xserver-xorg-core \
|
|
xserver-xorg-input-all \
|
|
xserver-xorg-video-fbdev \
|
|
xserver-xorg-video-vesa \
|
|
xinit \
|
|
x11-xserver-utils \
|
|
xdotool \
|
|
unclutter \
|
|
openbox \
|
|
|| error "X11 Installation fehlgeschlagen"
|
|
|
|
# Browser-Installation mit Fallback-Mechanismus
|
|
progress "Installiere Browser für Kiosk-Modus..."
|
|
local browser_installed=false
|
|
|
|
# Versuche Chromium zu installieren
|
|
if apt-get install -y chromium 2>/dev/null; then
|
|
log "✅ Chromium erfolgreich installiert"
|
|
browser_installed=true
|
|
elif apt-get install -y chromium-browser 2>/dev/null; then
|
|
log "✅ Chromium-Browser erfolgreich installiert"
|
|
browser_installed=true
|
|
elif apt-get install -y firefox-esr 2>/dev/null; then
|
|
warning "⚠️ Chromium nicht verfügbar - Firefox ESR als Fallback installiert"
|
|
browser_installed=true
|
|
fi
|
|
|
|
if [ "$browser_installed" = false ]; then
|
|
error "❌ Kein Browser verfügbar (chromium, chromium-browser, firefox-esr)"
|
|
fi
|
|
|
|
log "✅ Minimale X11-Umgebung installiert"
|
|
}
|
|
|
|
# =========================== KIOSK-BENUTZER MANAGEMENT ===========================
|
|
create_kiosk_user() {
|
|
log "=== KIOSK-BENUTZER SETUP ==="
|
|
|
|
if ! id "$KIOSK_USER" &>/dev/null; then
|
|
progress "Erstelle Kiosk-Benutzer: $KIOSK_USER"
|
|
useradd -m -s /bin/bash "$KIOSK_USER" || error "Kann Kiosk-Benutzer nicht erstellen"
|
|
|
|
# Gruppen hinzufügen
|
|
usermod -aG audio,video,input,dialout,plugdev,users "$KIOSK_USER" 2>/dev/null || true
|
|
else
|
|
info "Kiosk-Benutzer $KIOSK_USER existiert bereits"
|
|
fi
|
|
|
|
# Passwort entfernen für automatischen Login
|
|
passwd -d "$KIOSK_USER" || warning "Konnte Passwort nicht entfernen"
|
|
|
|
log "✅ Kiosk-Benutzer konfiguriert: $KIOSK_USER"
|
|
}
|
|
|
|
configure_autologin() {
|
|
log "=== KONFIGURIERE AUTOLOGIN FÜR KIOSK-BENUTZER ==="
|
|
|
|
# Getty-Service für automatischen Login konfigurieren
|
|
progress "Konfiguriere automatischen Login auf tty1..."
|
|
|
|
local getty_override_dir="/etc/systemd/system/getty@tty1.service.d"
|
|
mkdir -p "$getty_override_dir"
|
|
|
|
cat > "$getty_override_dir/override.conf" << EOF
|
|
[Service]
|
|
ExecStart=
|
|
ExecStart=-/sbin/agetty --autologin $KIOSK_USER --noclear %I \$TERM
|
|
EOF
|
|
|
|
# Systemd-Konfiguration neu laden
|
|
systemctl daemon-reload
|
|
systemctl enable getty@tty1.service
|
|
|
|
log "✅ Autologin für $KIOSK_USER konfiguriert"
|
|
}
|
|
|
|
configure_kiosk_autostart() {
|
|
log "=== KONFIGURIERE AUTOMATISCHEN KIOSK-START ==="
|
|
|
|
# Erstelle .bashrc für automatischen X-Server und Browser-Start
|
|
progress "Konfiguriere automatischen Kiosk-Start für $KIOSK_USER..."
|
|
|
|
local kiosk_home="/home/$KIOSK_USER"
|
|
|
|
# .bashrc für automatischen Start erstellen
|
|
cat > "$kiosk_home/.bashrc" << 'EOF'
|
|
# Automatischer Kiosk-Start beim Login
|
|
if [ -z "$DISPLAY" ] && [ "$XDG_VTNR" = "1" ]; then
|
|
echo "Starte Kiosk-Modus..."
|
|
|
|
# X-Server im Hintergrund starten
|
|
startx /home/kiosk/.xinitrc -- :0 vt1 &
|
|
|
|
# Warte bis X-Server bereit ist
|
|
sleep 5
|
|
|
|
# Setze DISPLAY-Variable
|
|
export DISPLAY=:0
|
|
|
|
# Warte auf HTTPS-Backend
|
|
echo "Warte auf HTTPS-Backend..."
|
|
for i in {1..60}; do
|
|
if curl -k -s https://localhost:443 >/dev/null 2>&1; then
|
|
echo "HTTPS-Backend erreichbar"
|
|
break
|
|
fi
|
|
echo "Warte... ($i/60)"
|
|
sleep 2
|
|
done
|
|
|
|
# Bildschirmschoner deaktivieren
|
|
xset s off
|
|
xset s noblank
|
|
xset -dpms
|
|
|
|
# Mauszeiger verstecken
|
|
unclutter -idle 0.1 -root -noevents &
|
|
|
|
# Browser im Kiosk-Modus starten
|
|
if command -v chromium >/dev/null 2>&1; then
|
|
BROWSER="chromium"
|
|
elif command -v chromium-browser >/dev/null 2>&1; then
|
|
BROWSER="chromium-browser"
|
|
else
|
|
BROWSER="firefox-esr"
|
|
fi
|
|
|
|
echo "Starte $BROWSER im Kiosk-Modus..."
|
|
|
|
if [[ "$BROWSER" == "chromium"* ]]; then
|
|
exec $BROWSER \
|
|
--kiosk \
|
|
--no-sandbox \
|
|
--disable-infobars \
|
|
--disable-session-crashed-bubble \
|
|
--disable-restore-session-state \
|
|
--disable-features=TranslateUI \
|
|
--disable-extensions \
|
|
--disable-plugins \
|
|
--disable-popup-blocking \
|
|
--disable-prompt-on-repost \
|
|
--disable-sync \
|
|
--disable-translate \
|
|
--noerrdialogs \
|
|
--no-first-run \
|
|
--no-default-browser-check \
|
|
--autoplay-policy=no-user-gesture-required \
|
|
--start-fullscreen \
|
|
--start-maximized \
|
|
--user-data-dir=/home/kiosk/.chromium-kiosk \
|
|
--disable-background-mode \
|
|
--force-device-scale-factor=1.0 \
|
|
--disable-pinch \
|
|
--overscroll-history-navigation=0 \
|
|
--disable-dev-shm-usage \
|
|
--memory-pressure-off \
|
|
--max_old_space_size=512 \
|
|
--disable-background-timer-throttling \
|
|
--disable-backgrounding-occluded-windows \
|
|
--disable-renderer-backgrounding \
|
|
--disable-features=VizDisplayCompositor \
|
|
--enable-features=OverlayScrollbar \
|
|
--hide-scrollbars \
|
|
--ignore-certificate-errors \
|
|
--ignore-ssl-errors \
|
|
--ignore-certificate-errors-spki-list \
|
|
--disable-web-security \
|
|
--allow-running-insecure-content \
|
|
--unsafely-treat-insecure-origin-as-secure=https://localhost:443 \
|
|
https://localhost:443
|
|
else
|
|
exec firefox-esr \
|
|
--kiosk \
|
|
https://localhost:443
|
|
fi
|
|
fi
|
|
EOF
|
|
|
|
# .xinitrc für X-Server-Konfiguration erstellen
|
|
cat > "$kiosk_home/.xinitrc" << 'EOF'
|
|
#!/bin/bash
|
|
# Minimale X-Session für Kiosk-Modus
|
|
exec openbox-session
|
|
EOF
|
|
|
|
# Berechtigungen setzen
|
|
chown "$KIOSK_USER:$KIOSK_USER" "$kiosk_home/.bashrc"
|
|
chown "$KIOSK_USER:$KIOSK_USER" "$kiosk_home/.xinitrc"
|
|
chmod +x "$kiosk_home/.xinitrc"
|
|
|
|
# Erstelle Kiosk-Verzeichnisse
|
|
mkdir -p "$kiosk_home/.chromium-kiosk"
|
|
chown -R "$KIOSK_USER:$KIOSK_USER" "$kiosk_home/.chromium-kiosk"
|
|
|
|
log "✅ Automatischer Kiosk-Start konfiguriert"
|
|
info "Der Kiosk-Modus startet automatisch beim Login des $KIOSK_USER"
|
|
}
|
|
|
|
# =========================== PYTHON & NODE.JS INSTALLATION ===========================
|
|
install_python_dependencies() {
|
|
log "=== PYTHON-ABHÄNGIGKEITEN INSTALLATION ==="
|
|
|
|
progress "Installiere Python 3 und Entwicklungstools..."
|
|
apt-get install -y \
|
|
python3 \
|
|
python3-pip \
|
|
python3-dev \
|
|
python3-setuptools \
|
|
python3-venv \
|
|
build-essential \
|
|
libssl-dev \
|
|
libffi-dev \
|
|
sqlite3 \
|
|
|| error "Python Installation fehlgeschlagen"
|
|
|
|
# pip auf neueste Version aktualisieren
|
|
progress "Aktualisiere pip..."
|
|
python3 -m pip install --upgrade pip --break-system-packages || warning "pip Update fehlgeschlagen"
|
|
|
|
# SSL-Konfiguration für pip
|
|
mkdir -p /root/.pip
|
|
cat > /root/.pip/pip.conf << EOF
|
|
[global]
|
|
trusted-host = pypi.org
|
|
pypi.python.org
|
|
files.pythonhosted.org
|
|
cert = /etc/ssl/certs/ca-certificates.crt
|
|
timeout = 60
|
|
retries = 3
|
|
no-cache-dir = true
|
|
|
|
[install]
|
|
trusted-host = pypi.org
|
|
pypi.python.org
|
|
files.pythonhosted.org
|
|
no-warn-script-location = true
|
|
EOF
|
|
|
|
log "✅ Python-Umgebung vorbereitet"
|
|
}
|
|
|
|
install_nodejs_npm() {
|
|
log "=== NODE.JS UND NPM INSTALLATION ==="
|
|
|
|
# Alte Node.js-Installationen entfernen
|
|
progress "Entferne alte Node.js-Installationen..."
|
|
apt-get remove --purge -y nodejs npm 2>/dev/null || true
|
|
apt-get autoremove -y 2>/dev/null || true
|
|
|
|
# NodeSource Repository für Node.js LTS hinzufügen
|
|
progress "Installiere Node.js LTS..."
|
|
|
|
if curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - 2>/dev/null; then
|
|
apt-get update -y
|
|
apt-get install -y nodejs || error "Node.js Installation fehlgeschlagen"
|
|
else
|
|
warning "NodeSource Repository nicht verfügbar - verwende Debian-Repository"
|
|
apt-get install -y nodejs npm || error "Node.js Fallback Installation fehlgeschlagen"
|
|
fi
|
|
|
|
# Versionen prüfen
|
|
if command -v node >/dev/null 2>&1; then
|
|
local node_version=$(node --version)
|
|
log "✅ Node.js installiert: $node_version"
|
|
else
|
|
error "❌ Node.js Installation fehlgeschlagen"
|
|
fi
|
|
|
|
if command -v npm >/dev/null 2>&1; then
|
|
local npm_version=$(npm --version)
|
|
log "✅ npm installiert: $npm_version"
|
|
|
|
# npm-Konfiguration optimieren
|
|
npm config set fund false 2>/dev/null || true
|
|
npm config set audit-level moderate 2>/dev/null || true
|
|
else
|
|
error "❌ npm Installation fehlgeschlagen"
|
|
fi
|
|
|
|
log "✅ Node.js und npm erfolgreich installiert"
|
|
}
|
|
|
|
install_python_packages() {
|
|
log "=== PYTHON-PAKETE INSTALLATION ==="
|
|
|
|
local pip_opts="--break-system-packages --trusted-host pypi.org --trusted-host pypi.python.org --trusted-host files.pythonhosted.org --timeout 120 --retries 5 --no-cache-dir"
|
|
|
|
progress "Installiere requirements.txt direkt..."
|
|
if [ -f "$CURRENT_DIR/requirements.txt" ]; then
|
|
# Kopiere requirements.txt zum App-Verzeichnis
|
|
cp "$CURRENT_DIR/requirements.txt" "$APP_DIR/" 2>/dev/null || true
|
|
|
|
# Installiere direkt aus requirements.txt (robusteste Methode)
|
|
if pip3 install $pip_opts -r "$CURRENT_DIR/requirements.txt"; then
|
|
success "✅ Requirements.txt erfolgreich installiert"
|
|
|
|
# Kurze Validierung der Installation
|
|
progress "Prüfe pip-Installation..."
|
|
pip3 list | grep -E "(Flask|Werkzeug|SQLAlchemy|bcrypt)" || warning "Einige Core-Pakete möglicherweise nicht sichtbar"
|
|
else
|
|
warning "⚠️ Requirements.txt Installation fehlgeschlagen - verwende Fallback-Installation"
|
|
|
|
# Fallback: Core-Pakete einzeln installieren
|
|
progress "Installiere Core-Framework (Fallback)..."
|
|
pip3 install $pip_opts Flask==3.1.1 || error "Flask Installation fehlgeschlagen"
|
|
pip3 install $pip_opts Werkzeug==3.1.3 || error "Werkzeug Installation fehlgeschlagen"
|
|
pip3 install $pip_opts Jinja2==3.1.4 || error "Jinja2 Installation fehlgeschlagen"
|
|
|
|
progress "Installiere Flask-Extensions (Fallback)..."
|
|
pip3 install $pip_opts Flask-Login==0.6.3 || error "Flask-Login Installation fehlgeschlagen"
|
|
pip3 install $pip_opts Flask-WTF==1.2.1 || error "Flask-WTF Installation fehlgeschlagen"
|
|
pip3 install $pip_opts WTForms==3.1.2 || error "WTForms Installation fehlgeschlagen"
|
|
|
|
progress "Installiere Datenbank-Komponenten (Fallback)..."
|
|
pip3 install $pip_opts SQLAlchemy==2.0.36 || error "SQLAlchemy Installation fehlgeschlagen"
|
|
|
|
progress "Installiere Sicherheits-Komponenten (Fallback)..."
|
|
pip3 install $pip_opts cryptography==44.0.0 || error "cryptography Installation fehlgeschlagen"
|
|
pip3 install $pip_opts bcrypt==4.2.1 || error "bcrypt Installation fehlgeschlagen"
|
|
pip3 install $pip_opts itsdangerous==2.2.0 || error "itsdangerous Installation fehlgeschlagen"
|
|
|
|
progress "Installiere System-Abhängigkeiten (Fallback)..."
|
|
pip3 install $pip_opts requests==2.32.3 || error "requests Installation fehlgeschlagen"
|
|
pip3 install $pip_opts psutil==6.1.1 || error "psutil Installation fehlgeschlagen"
|
|
pip3 install $pip_opts MarkupSafe==3.0.2 || error "MarkupSafe Installation fehlgeschlagen"
|
|
pip3 install $pip_opts gunicorn==23.0.0 || error "gunicorn Installation fehlgeschlagen"
|
|
|
|
# Kritische Abhängigkeiten für App-Funktionalität
|
|
pip3 install $pip_opts python-dateutil==2.9.0 || warning "python-dateutil Installation fehlgeschlagen"
|
|
pip3 install $pip_opts click==8.1.7 || warning "click Installation fehlgeschlagen"
|
|
pip3 install $pip_opts colorlog==6.9.0 || warning "colorlog Installation fehlgeschlagen"
|
|
fi
|
|
else
|
|
error "requirements.txt nicht gefunden: $CURRENT_DIR/requirements.txt"
|
|
fi
|
|
|
|
# Validiere kritische Imports nach Installation
|
|
progress "Validiere kritische Python-Abhängigkeiten..."
|
|
local validation_errors=0
|
|
|
|
# Robuste Validierung mit detailliertem Logging
|
|
progress "Teste Flask-Import..."
|
|
if python3 -c "
|
|
import sys
|
|
try:
|
|
import flask
|
|
try:
|
|
version = flask.__version__
|
|
except AttributeError:
|
|
version = 'Version unbekannt'
|
|
print('✅ Flask ' + version + ' verfügbar')
|
|
sys.exit(0)
|
|
except ImportError as e:
|
|
print('❌ Flask-Import fehlgeschlagen: ' + str(e))
|
|
sys.exit(1)
|
|
except Exception as e:
|
|
print('❌ Flask-Test-Fehler: ' + str(e))
|
|
sys.exit(1)
|
|
" 2>&1; then
|
|
success "✅ Flask erfolgreich validiert"
|
|
else
|
|
warning "⚠️ Flask-Import problematisch"
|
|
((validation_errors++))
|
|
fi
|
|
|
|
progress "Teste Werkzeug-Import..."
|
|
if python3 -c "
|
|
import sys
|
|
try:
|
|
import werkzeug
|
|
# Robuste Versions-Erkennung für Werkzeug
|
|
version = 'Version unbekannt'
|
|
try:
|
|
version = werkzeug.__version__
|
|
except AttributeError:
|
|
try:
|
|
from werkzeug import __version__ as wz_version
|
|
version = wz_version
|
|
except ImportError:
|
|
try:
|
|
import pkg_resources
|
|
version = pkg_resources.get_distribution('werkzeug').version
|
|
except:
|
|
version = 'verfügbar'
|
|
print('✅ Werkzeug ' + version + ' verfügbar')
|
|
sys.exit(0)
|
|
except ImportError as e:
|
|
print('❌ Werkzeug-Import fehlgeschlagen: ' + str(e))
|
|
sys.exit(1)
|
|
except Exception as e:
|
|
print('❌ Werkzeug-Test-Fehler: ' + str(e))
|
|
sys.exit(1)
|
|
" 2>&1; then
|
|
success "✅ Werkzeug erfolgreich validiert"
|
|
else
|
|
warning "⚠️ Werkzeug-Import problematisch"
|
|
((validation_errors++))
|
|
fi
|
|
|
|
progress "Teste SQLAlchemy-Import..."
|
|
if python3 -c "
|
|
import sys
|
|
try:
|
|
import sqlalchemy
|
|
try:
|
|
version = sqlalchemy.__version__
|
|
except AttributeError:
|
|
version = 'Version unbekannt'
|
|
print('✅ SQLAlchemy ' + version + ' verfügbar')
|
|
sys.exit(0)
|
|
except ImportError as e:
|
|
print('❌ SQLAlchemy-Import fehlgeschlagen: ' + str(e))
|
|
sys.exit(1)
|
|
except Exception as e:
|
|
print('❌ SQLAlchemy-Test-Fehler: ' + str(e))
|
|
sys.exit(1)
|
|
" 2>&1; then
|
|
success "✅ SQLAlchemy erfolgreich validiert"
|
|
else
|
|
warning "⚠️ SQLAlchemy-Import problematisch"
|
|
((validation_errors++))
|
|
fi
|
|
|
|
progress "Teste bcrypt-Import..."
|
|
if python3 -c "
|
|
import sys
|
|
try:
|
|
import bcrypt
|
|
print('✅ bcrypt verfügbar')
|
|
sys.exit(0)
|
|
except ImportError as e:
|
|
print('❌ bcrypt-Import fehlgeschlagen: ' + str(e))
|
|
sys.exit(1)
|
|
except Exception as e:
|
|
print('❌ bcrypt-Test-Fehler: ' + str(e))
|
|
sys.exit(1)
|
|
" 2>&1; then
|
|
success "✅ bcrypt erfolgreich validiert"
|
|
else
|
|
warning "⚠️ bcrypt-Import problematisch"
|
|
((validation_errors++))
|
|
fi
|
|
|
|
# Zusammenfassung und Entscheidung
|
|
if [ $validation_errors -eq 0 ]; then
|
|
success "✅ Alle kritischen Python-Abhängigkeiten erfolgreich validiert"
|
|
elif [ $validation_errors -le 2 ]; then
|
|
warning "⚠️ $validation_errors Abhängigkeiten haben Probleme - System sollte trotzdem funktionieren"
|
|
info "→ Möglicherweise sind nur optionale Features betroffen"
|
|
else
|
|
error "❌ $validation_errors kritische Python-Abhängigkeiten fehlen!"
|
|
fi
|
|
|
|
log "✅ Python-Pakete erfolgreich installiert und validiert"
|
|
}
|
|
|
|
# =========================== SSL-ZERTIFIKATE ===========================
|
|
install_ssl_certificates() {
|
|
log "=== SSL-ZERTIFIKATE KONFIGURATION ==="
|
|
|
|
progress "Aktualisiere CA-Zertifikate..."
|
|
apt-get install -y ca-certificates openssl || error "CA-Zertifikate Installation fehlgeschlagen"
|
|
update-ca-certificates || warning "CA-Zertifikate Update fehlgeschlagen"
|
|
|
|
# Mercedes Corporate Zertifikate (falls vorhanden)
|
|
if [ -d "$CURRENT_DIR/certs/mercedes" ] && [ "$(ls -A $CURRENT_DIR/certs/mercedes 2>/dev/null)" ]; then
|
|
progress "Installiere Mercedes Corporate Zertifikate..."
|
|
|
|
find "$CURRENT_DIR/certs/mercedes" -type f \( -name "*.crt" -o -name "*.pem" -o -name "*.cer" \) | while read cert_file; do
|
|
local cert_basename=$(basename "$cert_file")
|
|
local cert_name="${cert_basename%.*}"
|
|
|
|
progress "Verarbeite Mercedes-Zertifikat: $cert_basename"
|
|
|
|
# Zertifikat validieren und installieren
|
|
if openssl x509 -in "$cert_file" -text -noout >/dev/null 2>&1; then
|
|
cp "$cert_file" "/usr/local/share/ca-certificates/${cert_name}.crt"
|
|
log "✅ Zertifikat installiert: ${cert_name}.crt"
|
|
elif openssl x509 -in "$cert_file" -inform DER -text -noout >/dev/null 2>&1; then
|
|
openssl x509 -in "$cert_file" -inform DER -out "/usr/local/share/ca-certificates/${cert_name}.crt" -outform PEM
|
|
log "✅ DER-Zertifikat konvertiert und installiert: ${cert_name}.crt"
|
|
else
|
|
warning "⚠️ Ungültiges Zertifikat übersprungen: $cert_file"
|
|
fi
|
|
done
|
|
|
|
update-ca-certificates || warning "Mercedes Zertifikate Update fehlgeschlagen"
|
|
fi
|
|
|
|
# SSL-Umgebungsvariablen setzen
|
|
export SSL_CERT_FILE="/etc/ssl/certs/ca-certificates.crt"
|
|
export REQUESTS_CA_BUNDLE="/etc/ssl/certs/ca-certificates.crt"
|
|
export CURL_CA_BUNDLE="/etc/ssl/certs/ca-certificates.crt"
|
|
|
|
log "✅ SSL-Zertifikate konfiguriert"
|
|
}
|
|
|
|
# =========================== ANWENDUNGS-DEPLOYMENT ===========================
|
|
deploy_application() {
|
|
log "=== ANWENDUNGS-DEPLOYMENT ==="
|
|
|
|
progress "Erstelle Zielverzeichnis: $APP_DIR"
|
|
mkdir -p "$APP_DIR" || error "Konnte Zielverzeichnis nicht erstellen"
|
|
|
|
progress "Kopiere Anwendungsdateien..."
|
|
|
|
# Liste der zu kopierenden Dateien/Ordner
|
|
local copy_items=(
|
|
"app.py"
|
|
"models.py"
|
|
"requirements.txt"
|
|
"blueprints/"
|
|
"config/"
|
|
"database/"
|
|
"static/"
|
|
"templates/"
|
|
"uploads/"
|
|
"utils/"
|
|
"logs/"
|
|
"certs/"
|
|
)
|
|
|
|
# Sichere selektive Kopie
|
|
for item in "${copy_items[@]}"; do
|
|
if [ -e "$CURRENT_DIR/$item" ]; then
|
|
progress "Kopiere: $item"
|
|
cp -r "$CURRENT_DIR/$item" "$APP_DIR/" || warning "Fehler beim Kopieren von $item"
|
|
else
|
|
info "Überspringe nicht vorhandenes Element: $item"
|
|
fi
|
|
done
|
|
|
|
# Spezielle Dateien
|
|
for file in "package.json" "package-lock.json" "tailwind.config.js" "postcss.config.js"; do
|
|
if [ -f "$CURRENT_DIR/$file" ]; then
|
|
cp "$CURRENT_DIR/$file" "$APP_DIR/" || warning "Fehler beim Kopieren von $file"
|
|
fi
|
|
done
|
|
|
|
# Erstelle notwendige Verzeichnisse
|
|
mkdir -p "$APP_DIR"/{database/backups,logs/{app,auth,errors},uploads/temp,certs/localhost}
|
|
|
|
# Berechtigungen setzen
|
|
chown -R root:root "$APP_DIR"
|
|
chmod -R 755 "$APP_DIR"
|
|
chmod 750 "$APP_DIR"/{database,logs,certs}
|
|
chmod +x "$APP_DIR/app.py"
|
|
|
|
# Python-Pfad-Konfiguration erstellen
|
|
progress "Konfiguriere Python-Umgebung..."
|
|
|
|
# .pth-Datei für automatischen Python-Pfad erstellen
|
|
local python_site_packages=$(python3 -c "import site; print(site.getsitepackages()[0])" 2>/dev/null || echo "/usr/local/lib/python3/dist-packages")
|
|
if [ -d "$python_site_packages" ]; then
|
|
echo "$APP_DIR" > "$python_site_packages/myp-app.pth"
|
|
log "✅ Python-Pfad konfiguriert: $python_site_packages/myp-app.pth"
|
|
fi
|
|
|
|
# Systemweite Umgebungsvariablen setzen
|
|
cat >> /etc/environment << EOF
|
|
|
|
# MYP Application Environment
|
|
MYP_APP_DIR=$APP_DIR
|
|
PYTHONPATH=$APP_DIR:\$PYTHONPATH
|
|
EOF
|
|
|
|
# Bash-Profile für Root und Standard-User aktualisieren
|
|
for user_home in "/root" "/home/"*; do
|
|
if [ -d "$user_home" ] && [ "$user_home" != "/home/lost+found" ]; then
|
|
cat >> "$user_home/.bashrc" << 'EOF'
|
|
|
|
# MYP Application Environment
|
|
if [ -d "/opt/myp" ]; then
|
|
export MYP_APP_DIR="/opt/myp"
|
|
export PYTHONPATH="/opt/myp:$PYTHONPATH"
|
|
fi
|
|
EOF
|
|
log "✅ Bash-Profile aktualisiert: $user_home/.bashrc"
|
|
fi
|
|
done
|
|
|
|
log "✅ Anwendung erfolgreich deployed mit korrekter Python-Umgebung"
|
|
}
|
|
|
|
install_npm_dependencies() {
|
|
log "=== NPM-ABHÄNGIGKEITEN INSTALLATION ==="
|
|
|
|
if [ -f "$APP_DIR/package.json" ]; then
|
|
progress "Installiere npm-Abhängigkeiten..."
|
|
|
|
cd "$APP_DIR"
|
|
|
|
# npm install mit verschiedenen Fallback-Strategien
|
|
if npm install --no-optional --no-audit --no-fund 2>/dev/null; then
|
|
log "✅ npm install erfolgreich (Standard)"
|
|
elif npm install --legacy-peer-deps --no-optional 2>/dev/null; then
|
|
log "✅ npm install erfolgreich (Legacy-Modus)"
|
|
elif npm install --force 2>/dev/null; then
|
|
log "✅ npm install erfolgreich (Force-Modus)"
|
|
else
|
|
warning "⚠️ npm install fehlgeschlagen - überspringe"
|
|
fi
|
|
|
|
cd "$CURRENT_DIR"
|
|
else
|
|
info "Keine package.json gefunden - überspringe npm-Installation"
|
|
fi
|
|
|
|
log "✅ NPM-Abhängigkeiten verarbeitet"
|
|
}
|
|
|
|
# =========================== SSL-ZERTIFIKAT GENERIERUNG ===========================
|
|
generate_ssl_certificate() {
|
|
log "=== SSL-ZERTIFIKAT GENERIERUNG ==="
|
|
|
|
progress "Generiere selbstsigniertes SSL-Zertifikat für localhost..."
|
|
|
|
local cert_dir="$APP_DIR/certs/localhost"
|
|
mkdir -p "$cert_dir"
|
|
|
|
# Generiere privaten Schlüssel
|
|
openssl genrsa -out "$cert_dir/localhost.key" 2048 || error "Fehler beim Generieren des privaten Schlüssels"
|
|
|
|
# Generiere Zertifikat
|
|
openssl req -new -x509 -key "$cert_dir/localhost.key" -out "$cert_dir/localhost.crt" -days 365 \
|
|
-subj "/C=DE/ST=Baden-Wuerttemberg/L=Stuttgart/O=Mercedes-Benz/OU=IT/CN=localhost" \
|
|
|| error "Fehler beim Generieren des SSL-Zertifikats"
|
|
|
|
# Berechtigungen setzen
|
|
chmod 600 "$cert_dir/localhost.key"
|
|
chmod 644 "$cert_dir/localhost.crt"
|
|
|
|
log "✅ SSL-Zertifikat erfolgreich generiert"
|
|
}
|
|
|
|
# =========================== SYSTEMD-SERVICES ===========================
|
|
install_systemd_services() {
|
|
log "=== SYSTEMD-SERVICES INSTALLATION ==="
|
|
|
|
# Prüfe ob systemd-Verzeichnis existiert
|
|
if [ ! -d "$SYSTEMD_DIR" ]; then
|
|
error "systemd-Verzeichnis nicht gefunden: $SYSTEMD_DIR"
|
|
fi
|
|
|
|
progress "Kopiere Service-Dateien nach $SYSTEM_SYSTEMD_DIR..."
|
|
|
|
# Kopiere alle Service-Dateien
|
|
local service_files=(
|
|
"$HTTPS_SERVICE_NAME.service"
|
|
"$KIOSK_SERVICE_NAME.service"
|
|
"$WATCHDOG_SERVICE_NAME.service"
|
|
"$WATCHDOG_PYTHON_SERVICE_NAME.service"
|
|
"$FIREWALL_SERVICE_NAME.service"
|
|
)
|
|
|
|
for service_file in "${service_files[@]}"; do
|
|
if [ -f "$SYSTEMD_DIR/$service_file" ]; then
|
|
progress "Kopiere Service: $service_file"
|
|
cp "$SYSTEMD_DIR/$service_file" "$SYSTEM_SYSTEMD_DIR/" || error "Fehler beim Kopieren von $service_file"
|
|
else
|
|
warning "Service-Datei nicht gefunden: $service_file"
|
|
fi
|
|
done
|
|
|
|
# Systemd-Konfiguration neu laden
|
|
progress "Lade systemd-Konfiguration neu..."
|
|
systemctl daemon-reload || error "systemctl daemon-reload fehlgeschlagen"
|
|
|
|
log "✅ Systemd-Services installiert"
|
|
}
|
|
|
|
enable_and_start_services() {
|
|
log "=== SERVICES AKTIVIEREN UND STARTEN ==="
|
|
|
|
# HTTPS-Service aktivieren und starten
|
|
progress "Aktiviere und starte HTTPS-Service..."
|
|
systemctl enable "$HTTPS_SERVICE_NAME" || error "Fehler beim Aktivieren des HTTPS-Service"
|
|
systemctl start "$HTTPS_SERVICE_NAME" || error "Fehler beim Starten des HTTPS-Service"
|
|
|
|
# Warte kurz und prüfe Status
|
|
sleep 5
|
|
if systemctl is-active --quiet "$HTTPS_SERVICE_NAME"; then
|
|
log "✅ HTTPS-Service läuft erfolgreich"
|
|
else
|
|
error "❌ HTTPS-Service konnte nicht gestartet werden"
|
|
fi
|
|
|
|
# Kiosk-Service aktivieren (aber nicht starten - wird beim nächsten Boot gestartet)
|
|
progress "Aktiviere Kiosk-Service..."
|
|
systemctl enable "$KIOSK_SERVICE_NAME" || warning "Fehler beim Aktivieren des Kiosk-Service"
|
|
|
|
# Watchdog-Service aktivieren und starten
|
|
progress "Aktiviere und starte Watchdog-Service..."
|
|
systemctl enable "$WATCHDOG_SERVICE_NAME" || warning "Fehler beim Aktivieren des Watchdog-Service"
|
|
systemctl start "$WATCHDOG_SERVICE_NAME" || warning "Fehler beim Starten des Watchdog-Service"
|
|
|
|
# Firewall-Service aktivieren (falls vorhanden)
|
|
if [ -f "$SYSTEM_SYSTEMD_DIR/$FIREWALL_SERVICE_NAME.service" ]; then
|
|
progress "Aktiviere Firewall-Service..."
|
|
systemctl enable "$FIREWALL_SERVICE_NAME" || warning "Fehler beim Aktivieren des Firewall-Service"
|
|
fi
|
|
|
|
log "✅ Services erfolgreich konfiguriert"
|
|
}
|
|
|
|
# =========================== SYSTEM-TEST ===========================
|
|
test_application() {
|
|
log "=== SYSTEM-TEST ==="
|
|
|
|
progress "Teste HTTPS-Verbindung..."
|
|
|
|
# Warte auf Service-Start
|
|
local max_attempts=30
|
|
local attempt=1
|
|
|
|
while [ $attempt -le $max_attempts ]; do
|
|
if curl -k -s --connect-timeout 5 "$HTTPS_URL" >/dev/null 2>&1; then
|
|
success "✅ HTTPS-Backend erreichbar unter $HTTPS_URL"
|
|
break
|
|
fi
|
|
|
|
progress "Warte auf HTTPS-Backend... ($attempt/$max_attempts)"
|
|
sleep 2
|
|
((attempt++))
|
|
done
|
|
|
|
if [ $attempt -gt $max_attempts ]; then
|
|
error "❌ HTTPS-Backend nicht erreichbar nach $max_attempts Versuchen"
|
|
fi
|
|
|
|
# Teste SSL-Zertifikat
|
|
progress "Teste SSL-Zertifikat..."
|
|
if openssl s_client -connect localhost:443 -servername localhost </dev/null 2>/dev/null | openssl x509 -noout -text >/dev/null 2>&1; then
|
|
success "✅ SSL-Zertifikat gültig"
|
|
else
|
|
warning "⚠️ SSL-Zertifikat-Test fehlgeschlagen"
|
|
fi
|
|
|
|
log "✅ System-Test abgeschlossen"
|
|
}
|
|
|
|
# =========================== AUFRÄUMEN ===========================
|
|
cleanup_old_files() {
|
|
log "=== AUFRÄUMEN ALTE DATEIEN ==="
|
|
|
|
progress "Entferne alte Shell-Skripte..."
|
|
|
|
# Entferne alte Skripte (falls vorhanden)
|
|
local old_scripts=("combined.sh" "installer.sh")
|
|
|
|
for script in "${old_scripts[@]}"; do
|
|
if [ -f "$CURRENT_DIR/$script" ]; then
|
|
progress "Entferne: $script"
|
|
rm -f "$CURRENT_DIR/$script" || warning "Fehler beim Entfernen von $script"
|
|
fi
|
|
done
|
|
|
|
log "✅ Aufräumen abgeschlossen"
|
|
}
|
|
|
|
# =========================== HAUPTMENÜ ===========================
|
|
show_menu() {
|
|
clear
|
|
echo -e "${CYAN}=================================================================${NC}"
|
|
echo -e "${CYAN} $APP_NAME - Setup-Skript v$APP_VERSION${NC}"
|
|
echo -e "${CYAN}=================================================================${NC}"
|
|
echo ""
|
|
echo -e "${YELLOW}Bitte wählen Sie eine Option:${NC}"
|
|
echo ""
|
|
echo -e "${GREEN}1)${NC} Abhängigkeiten installieren und System für manuelles Testen vorbereiten"
|
|
echo -e " ${BLUE}→ Python, Node.js, SSL-Zertifikate, Anwendung deployed, minimaler Test${NC}"
|
|
echo -e " ${BLUE}→ System bereit für manuelle Tests und Entwicklung${NC}"
|
|
echo ""
|
|
echo -e "${GREEN}2)${NC} Vollständige Kiosk-Installation mit Remote-Zugang"
|
|
echo -e " ${BLUE}→ Komplette Produktionsinstallation mit automatischem Kiosk-Start${NC}"
|
|
echo -e " ${BLUE}→ RDP (root:744563017196A), SSH (user:raspberry), Firewall${NC}"
|
|
echo -e " ${BLUE}→ Automatischer Login und Kiosk-Modus beim Boot${NC}"
|
|
echo ""
|
|
echo -e "${GREEN}3)${NC} Beenden"
|
|
echo ""
|
|
echo -e "${CYAN}=================================================================${NC}"
|
|
echo -n "Ihre Wahl [1-3]: "
|
|
}
|
|
|
|
# =========================== INSTALLATIONS-MODI ===========================
|
|
install_dependencies_only() {
|
|
log "=== MODUS: ABHÄNGIGKEITEN INSTALLIEREN FÜR MANUELLES TESTEN ==="
|
|
|
|
check_root
|
|
check_debian_system
|
|
check_internet_connection
|
|
|
|
configure_hostname
|
|
update_system
|
|
configure_network_security
|
|
install_python_dependencies
|
|
install_nodejs_npm
|
|
install_ssl_certificates
|
|
install_python_packages
|
|
deploy_application
|
|
install_npm_dependencies
|
|
generate_ssl_certificate
|
|
|
|
# Performance-Optimierungen auch für manuelles Testen
|
|
optimize_webapp_performance
|
|
optimize_static_assets
|
|
|
|
# Erweiterter Systemtest mit robuster Fehlerbehandlung
|
|
progress "Starte erweiterten Systemtest..."
|
|
|
|
# 1. Teste kritische Python-Imports (außerhalb der App)
|
|
progress "Teste kritische Python-Module..."
|
|
local import_test_errors=0
|
|
|
|
# Core-Framework-Tests (robustere Version)
|
|
if python3 -c "
|
|
try:
|
|
import flask, werkzeug, jinja2
|
|
print('✅ Core-Framework verfügbar')
|
|
except ImportError as e:
|
|
print('⚠️ Core-Framework Import-Problem: ' + str(e))
|
|
exit(1)
|
|
" 2>&1; then
|
|
success "✅ Core-Framework erfolgreich getestet"
|
|
else
|
|
warning "⚠️ Core-Framework Import-Problem"
|
|
((import_test_errors++))
|
|
fi
|
|
|
|
# Datenbank-Tests (robustere Version)
|
|
if python3 -c "
|
|
try:
|
|
import sqlalchemy
|
|
print('✅ Datenbank-Module verfügbar')
|
|
except ImportError as e:
|
|
print('⚠️ Datenbank-Module Import-Problem: ' + str(e))
|
|
exit(1)
|
|
" 2>&1; then
|
|
success "✅ Datenbank-Module erfolgreich getestet"
|
|
else
|
|
warning "⚠️ Datenbank-Module Import-Problem"
|
|
((import_test_errors++))
|
|
fi
|
|
|
|
# Security-Tests (robustere Version)
|
|
if python3 -c "
|
|
try:
|
|
import bcrypt, cryptography
|
|
print('✅ Security-Module verfügbar')
|
|
except ImportError as e:
|
|
print('⚠️ Security-Module Import-Problem: ' + str(e))
|
|
exit(1)
|
|
" 2>&1; then
|
|
success "✅ Security-Module erfolgreich getestet"
|
|
else
|
|
warning "⚠️ Security-Module Import-Problem"
|
|
((import_test_errors++))
|
|
fi
|
|
|
|
# 2. Teste App-Struktur
|
|
progress "Validiere App-Struktur..."
|
|
local structure_errors=0
|
|
|
|
required_files=(
|
|
"$APP_DIR/app.py"
|
|
"$APP_DIR/models.py"
|
|
"$APP_DIR/requirements.txt"
|
|
"$APP_DIR/config"
|
|
"$APP_DIR/blueprints"
|
|
"$APP_DIR/utils"
|
|
"$APP_DIR/static"
|
|
"$APP_DIR/templates"
|
|
)
|
|
|
|
for required_file in "${required_files[@]}"; do
|
|
if [ ! -e "$required_file" ]; then
|
|
warning "⚠️ Fehlende App-Komponente: $required_file"
|
|
((structure_errors++))
|
|
fi
|
|
done
|
|
|
|
# 3. Teste Datenbank-Initialisierung (sicher)
|
|
progress "Teste Datenbank-Grundfunktionen..."
|
|
|
|
cd "$APP_DIR"
|
|
if python3 -c "
|
|
import sys
|
|
import os
|
|
sys.path.insert(0, os.getcwd())
|
|
|
|
try:
|
|
# Sichere Datenbank-Initialisierung testen
|
|
from models import init_database
|
|
print('✅ Datenbank-Initialisierung verfügbar')
|
|
|
|
# Test SQLite-Verbindung
|
|
import sqlite3
|
|
import tempfile
|
|
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
|
|
conn = sqlite3.connect(tmp.name)
|
|
conn.execute('CREATE TABLE test (id INTEGER)')
|
|
conn.close()
|
|
print('✅ SQLite-Funktionalität verfügbar')
|
|
|
|
except Exception as e:
|
|
print(f'⚠️ Datenbank-Test-Problem: {str(e)}')
|
|
exit(1)
|
|
" 2>/dev/null; then
|
|
log "✅ Datenbank-Grundfunktionen funktionieren"
|
|
else
|
|
warning "⚠️ Datenbank-Grundfunktionen haben Probleme"
|
|
((import_test_errors++))
|
|
fi
|
|
|
|
# 4. Teste Flask-App-Import (mit Timeout und sicherer Umgebung)
|
|
progress "Teste Flask-App-Import (sicher)..."
|
|
|
|
# Erstelle sichere Test-Umgebung
|
|
export FLASK_ENV=testing
|
|
export MYP_TESTING=1
|
|
export PYTHONPATH="$APP_DIR:$PYTHONPATH"
|
|
|
|
if timeout 30 python3 -c "
|
|
import sys
|
|
import os
|
|
sys.path.insert(0, os.getcwd())
|
|
|
|
# Setze Test-Umgebung
|
|
os.environ['FLASK_ENV'] = 'testing'
|
|
os.environ['MYP_TESTING'] = '1'
|
|
|
|
try:
|
|
# Minimaler Import-Test
|
|
import app
|
|
print('✅ App-Modul erfolgreich importiert')
|
|
|
|
# Test ob Flask-App-Objekt verfügbar ist
|
|
if hasattr(app, 'app'):
|
|
print('✅ Flask-App-Objekt verfügbar')
|
|
else:
|
|
print('⚠️ Flask-App-Objekt nicht gefunden')
|
|
exit(1)
|
|
|
|
except ImportError as ie:
|
|
print(f'❌ Import-Fehler: {str(ie)}')
|
|
exit(1)
|
|
except Exception as e:
|
|
print(f'⚠️ App-Import-Problem: {str(e)}')
|
|
exit(1)
|
|
" 2>/dev/null; then
|
|
success "✅ Flask-App kann erfolgreich importiert werden"
|
|
else
|
|
warning "⚠️ Flask-App-Import hat Probleme - möglicherweise fehlende optionale Abhängigkeiten"
|
|
warning " → Das ist normal für minimale Installation - App sollte trotzdem funktionieren"
|
|
fi
|
|
|
|
cd "$CURRENT_DIR"
|
|
|
|
# 5. Zusammenfassung
|
|
local total_errors=$((import_test_errors + structure_errors))
|
|
|
|
if [ $total_errors -eq 0 ]; then
|
|
success "✅ Alle Systemtests erfolgreich - System vollständig funktionsfähig"
|
|
elif [ $total_errors -le 2 ]; then
|
|
warning "⚠️ $total_errors kleinere Probleme gefunden - System grundsätzlich funktionsfähig"
|
|
info "→ Fehlende Komponenten sind meist optionale Abhängigkeiten"
|
|
else
|
|
error "❌ $total_errors Probleme gefunden - System möglicherweise nicht vollständig funktionsfähig"
|
|
fi
|
|
|
|
success "✅ Abhängigkeiten-Installation abgeschlossen!"
|
|
info "Das System ist bereit für manuelle Tests und Entwicklung"
|
|
info "Hostname wurde auf 'raspberrypi' gesetzt"
|
|
info "HTTPS-Backend kann manuell gestartet werden mit:"
|
|
info " cd /opt/myp && python3 app.py"
|
|
}
|
|
|
|
install_full_production_system() {
|
|
log "=== MODUS: VOLLSTÄNDIGE KIOSK-INSTALLATION MIT REMOTE-ZUGANG ==="
|
|
|
|
check_root
|
|
check_debian_system
|
|
check_internet_connection
|
|
|
|
# Hostname zuerst setzen
|
|
configure_hostname
|
|
|
|
# Führe zuerst Abhängigkeiten-Installation durch (falls noch nicht geschehen)
|
|
if [ ! -d "$APP_DIR" ] || [ ! -f "$APP_DIR/app.py" ]; then
|
|
warning "Anwendung noch nicht deployed - führe Abhängigkeiten-Installation durch..."
|
|
update_system
|
|
configure_network_security
|
|
install_python_dependencies
|
|
install_nodejs_npm
|
|
install_ssl_certificates
|
|
install_python_packages
|
|
deploy_application
|
|
install_npm_dependencies
|
|
generate_ssl_certificate
|
|
else
|
|
# Netzwerk-Sicherheit auch bei bestehender Installation konfigurieren
|
|
configure_network_security
|
|
fi
|
|
|
|
# Desktop-Environments entfernen und minimale X11 installieren
|
|
remove_desktop_environments
|
|
install_minimal_x11
|
|
|
|
# Performance-Optimierungen für Raspberry Pi Webapp
|
|
optimize_webapp_performance
|
|
optimize_static_assets
|
|
|
|
# Remote-Zugang konfigurieren
|
|
install_remote_access
|
|
configure_firewall
|
|
|
|
# Kiosk-Benutzer und Autologin konfigurieren
|
|
create_kiosk_user
|
|
configure_autologin
|
|
configure_kiosk_autostart
|
|
|
|
# Services installieren und aktivieren
|
|
install_systemd_services
|
|
enable_and_start_services
|
|
|
|
# System-Test
|
|
test_application
|
|
test_remote_access
|
|
|
|
# Aufräumen
|
|
cleanup_old_files
|
|
|
|
success "✅ Vollständige Kiosk-Installation abgeschlossen!"
|
|
info "Das System ist vollständig konfiguriert:"
|
|
info " 🖥️ Hostname: raspberrypi"
|
|
info " 🖥️ Automatischer Kiosk-Modus beim Boot"
|
|
info " 📡 SSH: ssh user@<ip> (Passwort: raspberry)"
|
|
info " 🖥️ RDP: <ip>:3389 (Benutzer: root, Passwort: 744563017196A)"
|
|
info " 🔒 Firewall: 192.168.0.0/16 + localhost + raspberrypi + m040tbaraspi001"
|
|
warning "⚠️ Neustart erforderlich für automatischen Kiosk-Start: sudo reboot"
|
|
}
|
|
|
|
# =========================== RDP & SSH ZUGANG ===========================
|
|
install_remote_access() {
|
|
log "=== INSTALLIERE REMOTE-ZUGANG (RDP & SSH) ==="
|
|
|
|
# SSH-Server installieren und konfigurieren
|
|
progress "Installiere und konfiguriere SSH-Server..."
|
|
apt-get install -y openssh-server || error "SSH-Server Installation fehlgeschlagen"
|
|
|
|
# SSH-Service aktivieren
|
|
systemctl enable ssh
|
|
systemctl start ssh
|
|
|
|
# SSH-Benutzer 'user' erstellen (falls nicht vorhanden)
|
|
if ! id "user" &>/dev/null; then
|
|
progress "Erstelle SSH-Benutzer: user"
|
|
useradd -m -s /bin/bash user || error "Kann SSH-Benutzer nicht erstellen"
|
|
echo "user:raspberry" | chpasswd || error "Kann Passwort für SSH-Benutzer nicht setzen"
|
|
usermod -aG sudo user 2>/dev/null || true
|
|
log "✅ SSH-Benutzer 'user' erstellt mit Passwort 'raspberry'"
|
|
else
|
|
info "SSH-Benutzer 'user' existiert bereits"
|
|
echo "user:raspberry" | chpasswd || warning "Konnte Passwort für SSH-Benutzer nicht aktualisieren"
|
|
fi
|
|
|
|
# RDP-Server (xrdp) installieren - vereinfachter Ansatz
|
|
progress "Installiere RDP-Server (xrdp) - vereinfachte Installation..."
|
|
|
|
# Alle bestehenden xrdp-Installationen entfernen
|
|
progress "Entferne vorherige xrdp-Installationen..."
|
|
systemctl stop xrdp xrdp-sesman 2>/dev/null || true
|
|
systemctl disable xrdp xrdp-sesman 2>/dev/null || true
|
|
apt-get remove --purge -y xrdp 2>/dev/null || true
|
|
rm -rf /etc/xrdp /var/log/xrdp* 2>/dev/null || true
|
|
|
|
# XFCE Desktop installieren (minimal)
|
|
progress "Installiere minimale XFCE-Umgebung..."
|
|
if ! apt-get install -y xfce4-session xfce4-panel xfce4-terminal xfce4-settings xfdesktop4 dbus-x11; then
|
|
warning "Minimale XFCE-Installation fehlgeschlagen - verwende Fallback..."
|
|
apt-get install -y xfce4 dbus-x11 || error "XFCE Installation fehlgeschlagen"
|
|
fi
|
|
|
|
# xrdp neu installieren
|
|
progress "Installiere xrdp neu..."
|
|
apt-get update
|
|
apt-get install -y xrdp || error "xrdp Installation fehlgeschlagen"
|
|
|
|
# Benutzer zur xrdp-Gruppe hinzufügen
|
|
usermod -aG xrdp root 2>/dev/null || true
|
|
if id "user" &>/dev/null; then
|
|
usermod -aG xrdp user 2>/dev/null || true
|
|
fi
|
|
|
|
# Erstelle minimale xrdp-Konfiguration
|
|
progress "Erstelle minimale xrdp-Konfiguration..."
|
|
|
|
# Backup der Original-Konfiguration
|
|
cp /etc/xrdp/xrdp.ini /etc/xrdp/xrdp.ini.original 2>/dev/null || true
|
|
|
|
# Sehr einfache xrdp.ini
|
|
cat > /etc/xrdp/xrdp.ini << 'EOF'
|
|
[Globals]
|
|
ini_version=1
|
|
fork=true
|
|
port=3389
|
|
tcp_nodelay=true
|
|
tcp_keepalive=true
|
|
security_layer=rdp
|
|
autorun=
|
|
allow_channels=true
|
|
allow_multimon=false
|
|
bitmap_cache=true
|
|
bitmap_compression=true
|
|
bulk_compression=false
|
|
max_bpp=24
|
|
new_cursors=true
|
|
use_fastpath=both
|
|
require_credentials=true
|
|
ask_for_reconnect_reason=false
|
|
enable_token_login=false
|
|
|
|
[Xorg]
|
|
name=Xorg
|
|
lib=libxup.so
|
|
username=ask
|
|
password=ask
|
|
ip=127.0.0.1
|
|
port=-1
|
|
code=20
|
|
EOF
|
|
|
|
# Einfache sesman.ini
|
|
cp /etc/xrdp/sesman.ini /etc/xrdp/sesman.ini.original 2>/dev/null || true
|
|
|
|
cat > /etc/xrdp/sesman.ini << 'EOF'
|
|
[Globals]
|
|
ListenAddress=127.0.0.1
|
|
ListenPort=3350
|
|
EnableUserWindowManager=true
|
|
UserWindowManager=startxfce4
|
|
DefaultWindowManager=startxfce4
|
|
|
|
[Security]
|
|
AllowRootLogin=true
|
|
MaxLoginRetry=4
|
|
AlwaysGroupCheck=false
|
|
|
|
[Sessions]
|
|
X11DisplayOffset=10
|
|
MaxSessions=10
|
|
KillDisconnected=false
|
|
IdleTimeLimit=0
|
|
DisconnectedTimeLimit=0
|
|
|
|
[Logging]
|
|
LogFile=xrdp-sesman.log
|
|
LogLevel=INFO
|
|
EnableSyslog=true
|
|
SyslogLevel=INFO
|
|
|
|
[Xorg]
|
|
param1=-bs
|
|
param2=-nolisten
|
|
param3=tcp
|
|
param4=-dpi
|
|
param5=96
|
|
EOF
|
|
|
|
# Erstelle .xsession für XFCE
|
|
progress "Konfiguriere XFCE-Sessions..."
|
|
|
|
# Root .xsession
|
|
cat > /root/.xsession << 'EOF'
|
|
#!/bin/bash
|
|
export XDG_SESSION_DESKTOP=xfce
|
|
export XDG_DATA_DIRS=/usr/share/xfce4:/usr/local/share:/usr/share
|
|
export XDG_CONFIG_DIRS=/etc/xdg/xdg-xfce:/etc/xdg
|
|
exec startxfce4
|
|
EOF
|
|
chmod +x /root/.xsession
|
|
|
|
# User .xsession (falls user existiert)
|
|
if id "user" &>/dev/null; then
|
|
cat > /home/user/.xsession << 'EOF'
|
|
#!/bin/bash
|
|
export XDG_SESSION_DESKTOP=xfce
|
|
export XDG_DATA_DIRS=/usr/share/xfce4:/usr/local/share:/usr/share
|
|
export XDG_CONFIG_DIRS=/etc/xdg/xdg-xfce:/etc/xdg
|
|
exec startxfce4
|
|
EOF
|
|
chown user:user /home/user/.xsession
|
|
chmod +x /home/user/.xsession
|
|
fi
|
|
|
|
# Root-Passwort setzen
|
|
progress "Setze Root-Passwort für RDP..."
|
|
echo "root:744563017196A" | chpasswd || error "Kann Root-Passwort nicht setzen"
|
|
|
|
# Log-Verzeichnisse erstellen mit korrekten Berechtigungen
|
|
progress "Erstelle Log-Verzeichnisse..."
|
|
mkdir -p /var/log
|
|
touch /var/log/xrdp.log /var/log/xrdp-sesman.log 2>/dev/null || true
|
|
chown xrdp:xrdp /var/log/xrdp*.log 2>/dev/null || true
|
|
chmod 644 /var/log/xrdp*.log 2>/dev/null || true
|
|
|
|
# Erstelle xrdp-Konfigurationsverzeichnisse
|
|
mkdir -p /etc/xrdp/cert /var/run/xrdp
|
|
chown xrdp:xrdp /etc/xrdp/cert /var/run/xrdp 2>/dev/null || true
|
|
|
|
# Services aktivieren und starten
|
|
progress "Starte xrdp-Services..."
|
|
|
|
# systemd daemon reload
|
|
systemctl daemon-reload
|
|
|
|
# Services aktivieren
|
|
systemctl enable xrdp-sesman
|
|
systemctl enable xrdp
|
|
|
|
# Services starten (sesman zuerst)
|
|
systemctl start xrdp-sesman
|
|
sleep 3
|
|
|
|
# Prüfe sesman-Status
|
|
if systemctl is-active --quiet xrdp-sesman; then
|
|
success "✅ xrdp-sesman erfolgreich gestartet"
|
|
|
|
# Jetzt xrdp starten
|
|
systemctl start xrdp
|
|
sleep 3
|
|
|
|
if systemctl is-active --quiet xrdp; then
|
|
success "✅ xrdp erfolgreich gestartet"
|
|
else
|
|
warning "⚠️ xrdp konnte nicht gestartet werden"
|
|
journalctl -u xrdp --no-pager -l | tail -10
|
|
fi
|
|
else
|
|
warning "⚠️ xrdp-sesman konnte nicht gestartet werden"
|
|
journalctl -u xrdp-sesman --no-pager -l | tail -10
|
|
fi
|
|
|
|
cd "$CURRENT_DIR"
|
|
|
|
# Finaler Status-Check
|
|
if systemctl is-active --quiet xrdp && systemctl is-active --quiet xrdp-sesman; then
|
|
log "✅ Remote-Zugang vollständig konfiguriert:"
|
|
log " 📡 SSH: user:raspberry (Port 22)"
|
|
log " 🖥️ RDP: root:744563017196A (Port 3389)"
|
|
log " 🖥️ RDP: user:raspberry (Port 3389)"
|
|
elif systemctl is-active --quiet ssh; then
|
|
log "✅ SSH-Zugang konfiguriert:"
|
|
log " 📡 SSH: user:raspberry (Port 22)"
|
|
warning "⚠️ RDP-Installation unvollständig"
|
|
info "Manuelle Überprüfung erforderlich:"
|
|
info " systemctl status xrdp"
|
|
info " systemctl status xrdp-sesman"
|
|
info " journalctl -u xrdp -f"
|
|
else
|
|
error "❌ Weder SSH noch RDP konnten konfiguriert werden"
|
|
fi
|
|
}
|
|
|
|
# =========================== FIREWALL KONFIGURATION ===========================
|
|
configure_firewall() {
|
|
log "=== KONFIGURIERE FIREWALL (firewalld) ==="
|
|
|
|
# firewalld installieren
|
|
progress "Installiere firewalld..."
|
|
apt-get install -y firewalld || error "firewalld Installation fehlgeschlagen"
|
|
|
|
# firewalld aktivieren und starten
|
|
systemctl enable firewalld
|
|
systemctl start firewalld
|
|
|
|
# Warte kurz bis firewalld vollständig gestartet ist
|
|
sleep 5
|
|
|
|
progress "Konfiguriere firewalld-Zonen und -Regeln..."
|
|
|
|
# Firewall-Status prüfen
|
|
if ! firewall-cmd --state >/dev/null 2>&1; then
|
|
error "firewalld ist nicht aktiv oder reagiert nicht"
|
|
fi
|
|
|
|
# Bestehende Zone entfernen falls vorhanden
|
|
progress "Entferne bestehende myp-backend Zone falls vorhanden..."
|
|
if firewall-cmd --permanent --get-zones | grep -q "myp-backend"; then
|
|
log "Entferne bestehende myp-backend Zone..."
|
|
firewall-cmd --permanent --delete-zone=myp-backend 2>/dev/null || true
|
|
firewall-cmd --reload
|
|
sleep 2
|
|
fi
|
|
|
|
# Zone neu erstellen
|
|
progress "Erstelle neue myp-backend Zone..."
|
|
if ! firewall-cmd --permanent --new-zone=myp-backend; then
|
|
error "Fehler beim Erstellen der myp-backend Zone"
|
|
fi
|
|
|
|
# Konfiguration neu laden
|
|
firewall-cmd --reload
|
|
sleep 2
|
|
|
|
# Erweiterte Netzwerk-Quellen definieren (nur IPv4)
|
|
progress "Füge Netzwerk-Quellen hinzu..."
|
|
firewall-cmd --permanent --zone=myp-backend --add-source=192.168.0.0/16 || error "Fehler beim Hinzufügen des 192.168.0.0/16 Netzwerks"
|
|
firewall-cmd --permanent --zone=myp-backend --add-source=127.0.0.1/32 || error "Fehler beim Hinzufügen von localhost"
|
|
|
|
# Lokaler Hostname "raspberrypi" hinzufügen
|
|
local local_hostname="raspberrypi"
|
|
progress "Füge lokalen Hostname hinzu: $local_hostname"
|
|
local local_ip=$(getent hosts "$local_hostname" | awk '{print $1}' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -1 2>/dev/null || true)
|
|
if [ -n "$local_ip" ]; then
|
|
firewall-cmd --permanent --zone=myp-backend --add-source="$local_ip/32" 2>/dev/null || warning "Konnte lokalen Hostname nicht hinzufügen"
|
|
log "✅ Lokaler Hostname $local_hostname hinzugefügt: $local_ip"
|
|
else
|
|
info "Lokaler Hostname $local_hostname nicht auflösbar - wird beim nächsten Boot verfügbar sein"
|
|
fi
|
|
|
|
# Frontend-Server m040tbaraspi001 hinzufügen (falls auflösbar)
|
|
progress "Füge Frontend-Server hinzu: m040tbaraspi001"
|
|
local frontend_ip=$(getent hosts "m040tbaraspi001" | awk '{print $1}' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -1 2>/dev/null || true)
|
|
if [ -n "$frontend_ip" ]; then
|
|
firewall-cmd --permanent --zone=myp-backend --add-source="$frontend_ip/32" 2>/dev/null || warning "Konnte Frontend-Server IP nicht hinzufügen"
|
|
log "✅ Frontend-Server m040tbaraspi001 hinzugefügt: $frontend_ip"
|
|
else
|
|
# Versuche auch mit FQDN
|
|
local frontend_fqdn_ip=$(getent hosts "m040tbaraspi001.de040.corpintra.net" | awk '{print $1}' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -1 2>/dev/null || true)
|
|
if [ -n "$frontend_fqdn_ip" ]; then
|
|
firewall-cmd --permanent --zone=myp-backend --add-source="$frontend_fqdn_ip/32" 2>/dev/null || warning "Konnte Frontend-Server FQDN nicht hinzufügen"
|
|
log "✅ Frontend-Server m040tbaraspi001.de040.corpintra.net hinzugefügt: $frontend_fqdn_ip"
|
|
else
|
|
info "Frontend-Server m040tbaraspi001 nicht auflösbar - überspringe"
|
|
fi
|
|
fi
|
|
|
|
# Ports und Services hinzufügen
|
|
progress "Konfiguriere Ports und Services..."
|
|
|
|
# HTTPS für API & Kiosk zulassen
|
|
firewall-cmd --permanent --zone=myp-backend --add-port=443/tcp || error "Fehler beim Hinzufügen von Port 443"
|
|
|
|
# SSH für Wartung
|
|
firewall-cmd --permanent --zone=myp-backend --add-service=ssh || error "Fehler beim Hinzufügen des SSH-Service"
|
|
|
|
# RDP für Remote-Desktop
|
|
firewall-cmd --permanent --zone=myp-backend --add-port=3389/tcp || error "Fehler beim Hinzufügen von Port 3389"
|
|
|
|
# IPv6 in firewalld deaktivieren
|
|
progress "Deaktiviere IPv6 in firewalld..."
|
|
firewall-cmd --permanent --set-target=DROP --zone=public --family=ipv6 2>/dev/null || warning "IPv6 konnte nicht in public Zone deaktiviert werden"
|
|
firewall-cmd --permanent --set-target=DROP --zone=myp-backend --family=ipv6 2>/dev/null || warning "IPv6 konnte nicht in myp-backend Zone deaktiviert werden"
|
|
|
|
# Default-Zone setzen
|
|
progress "Setze Default-Zone..."
|
|
firewall-cmd --set-default-zone=myp-backend || error "Fehler beim Setzen der Default-Zone"
|
|
|
|
# Änderungen übernehmen
|
|
progress "Lade Firewall-Konfiguration neu..."
|
|
firewall-cmd --reload || error "Fehler beim Neuladen der Firewall-Konfiguration"
|
|
|
|
# Kurz warten und Status prüfen
|
|
sleep 3
|
|
|
|
# Firewall-Status anzeigen
|
|
progress "Firewall-Konfiguration:"
|
|
if firewall-cmd --list-all --zone=myp-backend 2>/dev/null; then
|
|
log "✅ Firewall-Konfiguration erfolgreich angezeigt"
|
|
else
|
|
warning "⚠️ Firewall-Status konnte nicht angezeigt werden"
|
|
fi
|
|
|
|
# Finale Validierung
|
|
progress "Validiere Firewall-Konfiguration..."
|
|
local validation_errors=0
|
|
|
|
# Prüfe ob Zone existiert
|
|
if ! firewall-cmd --get-zones | grep -q "myp-backend"; then
|
|
error "Zone myp-backend wurde nicht korrekt erstellt"
|
|
((validation_errors++))
|
|
fi
|
|
|
|
# Prüfe Default-Zone
|
|
if [ "$(firewall-cmd --get-default-zone)" != "myp-backend" ]; then
|
|
warning "Default-Zone ist nicht myp-backend"
|
|
((validation_errors++))
|
|
fi
|
|
|
|
# Prüfe Ports
|
|
if ! firewall-cmd --zone=myp-backend --query-port=443/tcp 2>/dev/null; then
|
|
warning "Port 443 nicht korrekt konfiguriert"
|
|
((validation_errors++))
|
|
fi
|
|
|
|
if ! firewall-cmd --zone=myp-backend --query-port=3389/tcp 2>/dev/null; then
|
|
warning "Port 3389 nicht korrekt konfiguriert"
|
|
((validation_errors++))
|
|
fi
|
|
|
|
if ! firewall-cmd --zone=myp-backend --query-service=ssh 2>/dev/null; then
|
|
warning "SSH-Service nicht korrekt konfiguriert"
|
|
((validation_errors++))
|
|
fi
|
|
|
|
if [ $validation_errors -eq 0 ]; then
|
|
log "✅ Firewall konfiguriert und validiert:"
|
|
log " 🔒 Zone: myp-backend (als Default gesetzt)"
|
|
log " 🌐 Netzwerk: 192.168.0.0/16 (nur IPv4)"
|
|
log " 🏠 Localhost: 127.0.0.1"
|
|
log " 🖥️ Lokaler Host: raspberrypi"
|
|
log " 📡 Frontend-Server: m040tbaraspi001"
|
|
log " 🔌 Ports: 443/tcp (HTTPS), 22/tcp (SSH), 3389/tcp (RDP)"
|
|
log " 🚫 IPv6 vollständig blockiert"
|
|
else
|
|
warning "⚠️ Firewall-Konfiguration mit $validation_errors Fehlern abgeschlossen"
|
|
info "System funktioniert möglicherweise trotzdem - manuelle Überprüfung empfohlen"
|
|
fi
|
|
}
|
|
|
|
# =========================== REMOTE-ZUGANG TESTEN ===========================
|
|
test_remote_access() {
|
|
log "=== TESTE REMOTE-ZUGANG ==="
|
|
|
|
# SSH-Service testen
|
|
progress "Teste SSH-Service..."
|
|
if systemctl is-active --quiet ssh; then
|
|
success "✅ SSH-Service läuft"
|
|
|
|
# SSH-Port testen
|
|
if ss -tlnp | grep -q ":22 "; then
|
|
success "✅ SSH-Port 22 ist offen"
|
|
else
|
|
warning "⚠️ SSH-Port 22 nicht erreichbar"
|
|
fi
|
|
else
|
|
warning "⚠️ SSH-Service läuft nicht"
|
|
fi
|
|
|
|
# RDP-Services testen
|
|
progress "Teste RDP-Services..."
|
|
|
|
local xrdp_sesman_status="❌"
|
|
local xrdp_status="❌"
|
|
|
|
if systemctl is-active --quiet xrdp-sesman; then
|
|
xrdp_sesman_status="✅"
|
|
success "✅ xrdp-sesman läuft"
|
|
else
|
|
warning "⚠️ xrdp-sesman läuft nicht"
|
|
fi
|
|
|
|
if systemctl is-active --quiet xrdp; then
|
|
xrdp_status="✅"
|
|
success "✅ xrdp läuft"
|
|
|
|
# RDP-Port testen
|
|
if ss -tlnp | grep -q ":3389 "; then
|
|
success "✅ RDP-Port 3389 ist offen"
|
|
else
|
|
warning "⚠️ RDP-Port 3389 nicht erreichbar"
|
|
fi
|
|
else
|
|
warning "⚠️ xrdp läuft nicht"
|
|
fi
|
|
|
|
# Firewall-Status testen
|
|
progress "Teste Firewall-Status..."
|
|
if systemctl is-active --quiet firewalld; then
|
|
success "✅ Firewall läuft"
|
|
|
|
# Prüfe ob Ports offen sind
|
|
if firewall-cmd --zone=myp-backend --query-port=22/tcp 2>/dev/null; then
|
|
success "✅ SSH-Port in Firewall freigegeben"
|
|
else
|
|
warning "⚠️ SSH-Port nicht in Firewall freigegeben"
|
|
fi
|
|
|
|
if firewall-cmd --zone=myp-backend --query-port=3389/tcp 2>/dev/null; then
|
|
success "✅ RDP-Port in Firewall freigegeben"
|
|
else
|
|
warning "⚠️ RDP-Port nicht in Firewall freigegeben"
|
|
fi
|
|
else
|
|
warning "⚠️ Firewall läuft nicht"
|
|
fi
|
|
|
|
# Netzwerk-Interface testen
|
|
progress "Teste Netzwerk-Konfiguration..."
|
|
local ip_address=$(ip route get 1.1.1.1 2>/dev/null | awk '{print $7}' | head -1 || echo "Unbekannt")
|
|
if [ "$ip_address" != "Unbekannt" ]; then
|
|
success "✅ Netzwerk-Interface aktiv: $ip_address"
|
|
info "Zugang-URLs:"
|
|
info " SSH: ssh user@$ip_address"
|
|
if [ "$xrdp_status" = "✅" ]; then
|
|
info " RDP: $ip_address:3389 (root:744563017196A oder user:raspberry)"
|
|
fi
|
|
else
|
|
warning "⚠️ Keine Netzwerk-IP ermittelt"
|
|
fi
|
|
|
|
# Status-Zusammenfassung
|
|
log "📊 Service-Status:"
|
|
log " SSH: $(systemctl is-active --quiet ssh && echo "✅ Aktiv" || echo "❌ Inaktiv")"
|
|
log " xrdp-sesman: $xrdp_sesman_status $(systemctl is-active --quiet xrdp-sesman && echo "Aktiv" || echo "Inaktiv")"
|
|
log " xrdp: $xrdp_status $(systemctl is-active --quiet xrdp && echo "Aktiv" || echo "Inaktiv")"
|
|
|
|
log "✅ Remote-Zugang-Test abgeschlossen"
|
|
}
|
|
|
|
# =========================== HOSTNAME KONFIGURATION ===========================
|
|
configure_hostname() {
|
|
log "=== KONFIGURIERE HOSTNAME ==="
|
|
|
|
local target_hostname="raspberrypi"
|
|
local current_hostname=$(hostname)
|
|
|
|
if [ "$current_hostname" != "$target_hostname" ]; then
|
|
progress "Setze Hostname von '$current_hostname' auf '$target_hostname'..."
|
|
|
|
# Hostname sofort setzen
|
|
hostnamectl set-hostname "$target_hostname" || error "Fehler beim Setzen des Hostnames"
|
|
|
|
# /etc/hostname aktualisieren
|
|
echo "$target_hostname" > /etc/hostname
|
|
|
|
# /etc/hosts aktualisieren
|
|
cp /etc/hosts /etc/hosts.backup
|
|
sed -i "s/127.0.1.1.*/127.0.1.1\t$target_hostname/" /etc/hosts
|
|
|
|
# Falls kein 127.0.1.1 Eintrag existiert, hinzufügen
|
|
if ! grep -q "127.0.1.1" /etc/hosts; then
|
|
echo "127.0.1.1 $target_hostname" >> /etc/hosts
|
|
fi
|
|
|
|
log "✅ Hostname erfolgreich auf '$target_hostname' gesetzt"
|
|
else
|
|
log "✅ Hostname bereits korrekt: '$target_hostname'"
|
|
fi
|
|
|
|
# Hostname-Auflösung testen
|
|
if getent hosts "$target_hostname" >/dev/null 2>&1; then
|
|
local resolved_ip=$(getent hosts "$target_hostname" | awk '{print $1}' | head -1)
|
|
log "✅ Hostname-Auflösung funktioniert: $target_hostname -> $resolved_ip"
|
|
else
|
|
warning "⚠️ Hostname-Auflösung für '$target_hostname' fehlgeschlagen"
|
|
fi
|
|
}
|
|
|
|
# =========================== WEBAPP PERFORMANCE-OPTIMIERUNG ===========================
|
|
optimize_webapp_performance() {
|
|
log "=== WEBAPP PERFORMANCE-OPTIMIERUNG FÜR RASPBERRY PI ==="
|
|
|
|
# Python/Flask spezifische Optimierungen
|
|
progress "Konfiguriere Python-Performance-Optimierungen..."
|
|
|
|
# Python Bytecode Optimierung aktivieren
|
|
cat > /etc/environment << 'EOF'
|
|
# Python Performance Optimierungen
|
|
PYTHONOPTIMIZE=2
|
|
PYTHONDONTWRITEBYTECODE=1
|
|
PYTHONUNBUFFERED=1
|
|
PYTHONHASHSEED=random
|
|
|
|
# Flask/SQLite Optimierungen
|
|
FLASK_ENV=production
|
|
FLASK_DEBUG=0
|
|
SQLITE_TMPDIR=/tmp
|
|
|
|
# Memory Optimierungen
|
|
MALLOC_ARENA_MAX=2
|
|
MALLOC_MMAP_THRESHOLD=131072
|
|
MALLOC_TRIM_THRESHOLD=131072
|
|
|
|
EOF
|
|
|
|
# Systemd Service-Optimierungen
|
|
progress "Optimiere Systemd-Services für bessere Performance..."
|
|
|
|
# Stoppe unnötige Services
|
|
local unnecessary_services=(
|
|
"bluetooth.service"
|
|
"hciuart.service"
|
|
"avahi-daemon.service"
|
|
"cups.service"
|
|
"cups-browsed.service"
|
|
"ModemManager.service"
|
|
"wpa_supplicant.service"
|
|
)
|
|
|
|
for service in "${unnecessary_services[@]}"; do
|
|
if systemctl is-enabled "$service" 2>/dev/null; then
|
|
systemctl disable "$service" 2>/dev/null || true
|
|
systemctl stop "$service" 2>/dev/null || true
|
|
log "✅ Service deaktiviert: $service"
|
|
fi
|
|
done
|
|
|
|
# Tmpfs für temporäre Dateien
|
|
progress "Konfiguriere tmpfs für bessere I/O Performance..."
|
|
|
|
cat >> /etc/fstab << 'EOF'
|
|
|
|
# MYP Performance Optimierungen - tmpfs für temporäre Dateien
|
|
tmpfs /tmp tmpfs defaults,noatime,nosuid,size=256m 0 0
|
|
tmpfs /var/tmp tmpfs defaults,noatime,nosuid,size=128m 0 0
|
|
tmpfs /var/log tmpfs defaults,noatime,nosuid,size=64m 0 0
|
|
|
|
EOF
|
|
|
|
# Logrotate für tmpfs-Logs konfigurieren
|
|
cat > /etc/logrotate.d/myp-tmpfs << 'EOF'
|
|
/var/log/*.log {
|
|
daily
|
|
missingok
|
|
rotate 2
|
|
compress
|
|
notifempty
|
|
create 0644 root root
|
|
copytruncate
|
|
}
|
|
EOF
|
|
|
|
# Systemd Journal Einstellungen optimieren
|
|
progress "Optimiere systemd Journal für bessere Performance..."
|
|
|
|
mkdir -p /etc/systemd/journald.conf.d
|
|
cat > /etc/systemd/journald.conf.d/myp-performance.conf << 'EOF'
|
|
[Journal]
|
|
# Journal Optimierungen für Raspberry Pi
|
|
Storage=volatile
|
|
RuntimeMaxUse=32M
|
|
RuntimeKeepFree=16M
|
|
RuntimeMaxFileSize=8M
|
|
RuntimeMaxFiles=4
|
|
MaxRetentionSec=1day
|
|
MaxFileSec=1hour
|
|
ForwardToSyslog=no
|
|
ForwardToKMsg=no
|
|
ForwardToConsole=no
|
|
ForwardToWall=no
|
|
EOF
|
|
|
|
# Crontab für regelmäßige Cache-Bereinigung
|
|
progress "Installiere automatische Cache-Bereinigung..."
|
|
|
|
cat > /etc/cron.d/myp-cache-cleanup << 'EOF'
|
|
# MYP Cache und Memory Cleanup
|
|
# Alle 6 Stunden Cache bereinigen
|
|
0 */6 * * * root /bin/echo 3 > /proc/sys/vm/drop_caches
|
|
# Täglich um 3 Uhr temporäre Dateien bereinigen
|
|
0 3 * * * root /usr/bin/find /tmp -type f -atime +1 -delete 2>/dev/null
|
|
# Wöchentlich Python Cache bereinigen
|
|
0 2 * * 0 root /usr/bin/find /opt/myp -name "*.pyc" -delete 2>/dev/null
|
|
0 2 * * 0 root /usr/bin/find /opt/myp -name "__pycache__" -type d -exec rm -rf {} + 2>/dev/null
|
|
EOF
|
|
|
|
# Limits für bessere Ressourcen-Verwaltung
|
|
progress "Konfiguriere System-Limits..."
|
|
|
|
cat >> /etc/security/limits.conf << 'EOF'
|
|
|
|
# MYP Performance Limits
|
|
* soft nofile 65536
|
|
* hard nofile 65536
|
|
* soft nproc 32768
|
|
* hard nproc 32768
|
|
root soft nofile 65536
|
|
root hard nofile 65536
|
|
|
|
EOF
|
|
|
|
# Apache/Nginx entfernen falls vorhanden (Konflikt mit Flask)
|
|
progress "Entferne konfliktbehaftete Webserver..."
|
|
|
|
local webservers=("apache2" "nginx" "lighttpd")
|
|
for webserver in "${webservers[@]}"; do
|
|
if systemctl is-enabled "$webserver" 2>/dev/null; then
|
|
systemctl stop "$webserver" 2>/dev/null || true
|
|
systemctl disable "$webserver" 2>/dev/null || true
|
|
apt-get remove --purge -y "$webserver" 2>/dev/null || true
|
|
log "✅ Webserver entfernt: $webserver"
|
|
fi
|
|
done
|
|
|
|
log "✅ Webapp Performance-Optimierung abgeschlossen:"
|
|
log " 🚀 Python Bytecode-Optimierung aktiviert"
|
|
log " 💾 tmpfs für temporäre Dateien konfiguriert"
|
|
log " 📝 Journal-Logging optimiert"
|
|
log " 🧹 Automatische Cache-Bereinigung installiert"
|
|
log " ⚡ Unnötige Services deaktiviert"
|
|
log " 📊 System-Limits für bessere Performance gesetzt"
|
|
}
|
|
|
|
# =========================== CSS/JS OPTIMIERUNG ===========================
|
|
optimize_static_assets() {
|
|
log "=== STATISCHE DATEIEN OPTIMIERUNG ==="
|
|
|
|
if [ ! -d "$APP_DIR/static" ]; then
|
|
warning "Static-Ordner nicht gefunden - überspringe Asset-Optimierung"
|
|
return
|
|
fi
|
|
|
|
progress "Analysiere und optimiere CSS/JS Dateien..."
|
|
|
|
cd "$APP_DIR/static"
|
|
|
|
# Erstelle optimierte CSS-Datei durch Kombination kritischer Styles
|
|
progress "Erstelle optimierte CSS-Kombination..."
|
|
|
|
cat > css/critical.min.css << 'EOF'
|
|
/* Kritische Styles für ersten Seitenaufbau - Inline-optimiert */
|
|
*{box-sizing:border-box}body{margin:0;font-family:system-ui,-apple-system,sans-serif;line-height:1.5}
|
|
.container{max-width:1200px;margin:0 auto;padding:0 1rem}
|
|
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}
|
|
.btn{display:inline-flex;align-items:center;padding:0.5rem 1rem;border:none;border-radius:0.375rem;font-weight:500;text-decoration:none;cursor:pointer;transition:all 0.15s}
|
|
.btn-primary{background:#3b82f6;color:white}.btn-primary:hover{background:#2563eb}
|
|
.card{background:white;border-radius:0.5rem;padding:1.5rem;box-shadow:0 1px 3px rgba(0,0,0,0.1)}
|
|
.flex{display:flex}.items-center{align-items:center}.justify-between{justify-content:space-between}
|
|
.hidden{display:none}.block{display:block}.inline-block{display:inline-block}
|
|
.text-sm{font-size:0.875rem}.text-lg{font-size:1.125rem}
|
|
.font-medium{font-weight:500}.font-bold{font-weight:700}
|
|
.text-gray-600{color:#4b5563}.text-gray-900{color:#111827}
|
|
.mb-4{margin-bottom:1rem}.mt-6{margin-top:1.5rem}.p-4{padding:1rem}
|
|
.w-full{width:100%}.h-full{height:100%}
|
|
@media(max-width:768px){.container{padding:0 0.5rem}.card{padding:1rem}}
|
|
EOF
|
|
|
|
# Erstelle minimale JavaScript-Loader
|
|
progress "Erstelle optimierten JavaScript-Loader..."
|
|
|
|
cat > js/loader.min.js << 'EOF'
|
|
/*Minimaler Async Loader für bessere Performance*/
|
|
(function(){var d=document,w=window;function loadCSS(href){var l=d.createElement('link');l.rel='stylesheet';l.href=href;l.media='print';l.onload=function(){this.media='all'};d.head.appendChild(l)}function loadJS(src,cb){var s=d.createElement('script');s.async=true;s.src=src;if(cb)s.onload=cb;d.head.appendChild(s)}w.loadAssets=function(){if(w.assetsLoaded)return;w.assetsLoaded=true;loadCSS('/static/css/tailwind.min.css');loadJS('/static/js/app.min.js')};if(d.readyState==='loading'){d.addEventListener('DOMContentLoaded',w.loadAssets)}else{w.loadAssets()}})();
|
|
EOF
|
|
|
|
# Service Worker für besseres Caching
|
|
progress "Erstelle optimierten Service Worker..."
|
|
|
|
cat > sw-optimized.js << 'EOF'
|
|
const CACHE_NAME = 'myp-webapp-v1';
|
|
const ASSETS_TO_CACHE = [
|
|
'/',
|
|
'/static/css/critical.min.css',
|
|
'/static/js/loader.min.js',
|
|
'/static/favicon.svg'
|
|
];
|
|
|
|
self.addEventListener('install', event => {
|
|
event.waitUntil(
|
|
caches.open(CACHE_NAME)
|
|
.then(cache => cache.addAll(ASSETS_TO_CACHE))
|
|
);
|
|
});
|
|
|
|
self.addEventListener('fetch', event => {
|
|
if (event.request.destination === 'image' ||
|
|
event.request.url.includes('/static/')) {
|
|
event.respondWith(
|
|
caches.match(event.request)
|
|
.then(response => response || fetch(event.request))
|
|
);
|
|
}
|
|
});
|
|
EOF
|
|
|
|
# Gzip-Kompression für statische Dateien
|
|
progress "Komprimiere statische Dateien..."
|
|
|
|
find . -name "*.css" -o -name "*.js" -o -name "*.html" | while read file; do
|
|
if [ -f "$file" ] && [ ! -f "$file.gz" ]; then
|
|
gzip -c "$file" > "$file.gz" 2>/dev/null || true
|
|
fi
|
|
done
|
|
|
|
cd "$CURRENT_DIR"
|
|
|
|
log "✅ Statische Dateien optimiert:"
|
|
log " 📦 Kritische CSS-Styles kombiniert"
|
|
log " ⚡ Asynchroner Asset-Loader erstellt"
|
|
log " 🗜️ Gzip-Kompression angewendet"
|
|
log " 🔄 Service Worker für Caching installiert"
|
|
}
|
|
|
|
# =========================== HAUPTPROGRAMM ===========================
|
|
main() {
|
|
# Erstelle Log-Datei
|
|
touch "$INSTALL_LOG" || true
|
|
|
|
while true; do
|
|
show_menu
|
|
read -r choice
|
|
|
|
case $choice in
|
|
1)
|
|
install_dependencies_only
|
|
echo ""
|
|
echo -n "Drücken Sie Enter um fortzufahren..."
|
|
read -r
|
|
;;
|
|
2)
|
|
install_full_production_system
|
|
echo ""
|
|
echo -n "Drücken Sie Enter um fortzufahren..."
|
|
read -r
|
|
;;
|
|
3)
|
|
log "Setup-Skript beendet"
|
|
exit 0
|
|
;;
|
|
*)
|
|
error "Ungültige Auswahl. Bitte wählen Sie 1-3."
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# Skript starten
|
|
main "$@" |