"feat: Integrate Raspberry Pi

This commit is contained in:
Till Tomczak 2025-05-29 15:56:53 +02:00
parent 5f2fa5af0c
commit bd45a34fa4
5 changed files with 972 additions and 181 deletions

View File

@ -4245,7 +4245,7 @@ if __name__ == "__main__":
# Scheduler stoppen falls aktiviert # Scheduler stoppen falls aktiviert
if SCHEDULER_ENABLED and scheduler: if SCHEDULER_ENABLED and scheduler:
try: try:
scheduler.shutdown(wait=False) scheduler.stop()
app_logger.info("Job-Scheduler gestoppt") app_logger.info("Job-Scheduler gestoppt")
except Exception as e: except Exception as e:
app_logger.error(f"Fehler beim Stoppen des Schedulers: {str(e)}") app_logger.error(f"Fehler beim Stoppen des Schedulers: {str(e)}")

View File

@ -3,6 +3,7 @@
# =================================================================== # ===================================================================
# Raspberry Pi Vollautomatische Installation - MYP Druckerverwaltung # Raspberry Pi Vollautomatische Installation - MYP Druckerverwaltung
# Für Debian 12 (Bookworm) auf Raspberry Pi # Für Debian 12 (Bookworm) auf Raspberry Pi
# Nach offizieller Raspberry Pi Kiosk-Anleitung optimiert
# =================================================================== # ===================================================================
set -e # Beende bei Fehlern set -e # Beende bei Fehlern
@ -38,25 +39,31 @@ if [ "$EUID" -ne 0 ]; then
fi fi
log "=== MYP Druckerverwaltung - Raspberry Pi Installation ===" log "=== MYP Druckerverwaltung - Raspberry Pi Installation ==="
log "Debian 12 (Bookworm) Vollautomatische Einrichtung" log "Debian 12 (Bookworm) Kiosk-Modus nach offizieller Anleitung"
echo echo
# Konfigurationsvariablen # Konfigurationsvariablen
APP_USER="myp" APP_USER="myp"
KIOSK_USER="kiosk"
APP_DIR="/opt/myp-druckerverwaltung" APP_DIR="/opt/myp-druckerverwaltung"
SERVICE_NAME="myp-druckerverwaltung" SERVICE_NAME="myp-druckerverwaltung"
KIOSK_SERVICE_NAME="myp-kiosk" KIOSK_SERVICE_NAME="myp-kiosk"
GITHUB_REPO="https://github.com/your-repo/myp-druckerverwaltung.git" # Anpassen! KIOSK_URL="http://localhost"
DISPLAY_PORT=":0"
# Bereinige unbenötigte Pakete (entsprechend offizieller Anleitung)
log "Entferne unbenötigte Pakete zur Speicheroptimierung..."
export DEBIAN_FRONTEND=noninteractive
apt-get purge -y wolfram-engine scratch scratch2 nuscratch sonic-pi idle3 smartsim java-common minecraft-pi libreoffice* || true
apt-get autoremove -y
apt-get clean
# Systemupdate # Systemupdate
log "Aktualisiere Paketlisten und System..." log "Aktualisiere Paketlisten und System..."
export DEBIAN_FRONTEND=noninteractive
apt-get update -y apt-get update -y
apt-get upgrade -y --allow-downgrades --allow-remove-essential --allow-change-held-packages apt-get upgrade -y --allow-downgrades --allow-remove-essential --allow-change-held-packages
# Installiere erforderliche Pakete # Installiere erforderliche Pakete (nach offizieller Anleitung)
log "Installiere Systempakete..." log "Installiere Systempakete für Kiosk-Modus..."
apt-get install -y \ apt-get install -y \
python3 \ python3 \
python3-pip \ python3-pip \
@ -74,7 +81,9 @@ apt-get install -y \
openbox \ openbox \
lightdm \ lightdm \
x11-xserver-utils \ x11-xserver-utils \
xdotool \
unclutter \ unclutter \
sed \
build-essential \ build-essential \
libssl-dev \ libssl-dev \
libffi-dev \ libffi-dev \
@ -82,7 +91,7 @@ apt-get install -y \
zlib1g-dev \ zlib1g-dev \
nodejs \ nodejs \
npm \ npm \
--break-system-packages || true --no-install-recommends || true
# Erstelle Anwendungsbenutzer # Erstelle Anwendungsbenutzer
log "Erstelle Anwendungsbenutzer '$APP_USER'..." log "Erstelle Anwendungsbenutzer '$APP_USER'..."
@ -94,15 +103,24 @@ else
log "Benutzer '$APP_USER' existiert bereits" log "Benutzer '$APP_USER' existiert bereits"
fi fi
# Erstelle Kiosk-Benutzer (nach offizieller Anleitung)
log "Erstelle Kiosk-Benutzer '$KIOSK_USER'..."
if ! id "$KIOSK_USER" &>/dev/null; then
useradd -m -s /bin/bash "$KIOSK_USER"
usermod -aG audio,video "$KIOSK_USER"
log "Benutzer '$KIOSK_USER' erstellt"
else
log "Benutzer '$KIOSK_USER' existiert bereits"
fi
# Erstelle Anwendungsverzeichnis # Erstelle Anwendungsverzeichnis
log "Erstelle Anwendungsverzeichnis..." log "Erstelle Anwendungsverzeichnis..."
mkdir -p "$APP_DIR" mkdir -p "$APP_DIR"
chown "$APP_USER:$APP_USER" "$APP_DIR" chown "$APP_USER:$APP_USER" "$APP_DIR"
# Klone Repository (falls nicht lokal vorhanden) # Kopiere Anwendung
if [ ! -d "$APP_DIR/.git" ]; then if [ ! -d "$APP_DIR/.git" ]; then
log "Klone Anwendung von Repository..." log "Kopiere Anwendung..."
# Für lokale Installation: kopiere aktuelles Verzeichnis
if [ -f "app.py" ]; then if [ -f "app.py" ]; then
log "Kopiere lokale Anwendung..." log "Kopiere lokale Anwendung..."
cp -r . "$APP_DIR/" cp -r . "$APP_DIR/"
@ -111,7 +129,7 @@ if [ ! -d "$APP_DIR/.git" ]; then
error "Anwendungsdateien nicht gefunden. Führe das Skript im Anwendungsverzeichnis aus." error "Anwendungsdateien nicht gefunden. Führe das Skript im Anwendungsverzeichnis aus."
fi fi
else else
log "Repository bereits vorhanden, aktualisiere..." log "Anwendung bereits vorhanden, aktualisiere..."
cd "$APP_DIR" cd "$APP_DIR"
sudo -u "$APP_USER" git pull sudo -u "$APP_USER" git pull
fi fi
@ -126,7 +144,7 @@ sudo -u "$APP_USER" ./venv/bin/pip install --upgrade pip
# Installiere Python-Abhängigkeiten # Installiere Python-Abhängigkeiten
log "Installiere Python-Abhängigkeiten..." log "Installiere Python-Abhängigkeiten..."
if [ -f "requirements.txt" ]; then if [ -f "requirements.txt" ]; then
sudo -u "$APP_USER" ./venv/bin/pip install -r requirements.txt --break-system-packages || true sudo -u "$APP_USER" ./venv/bin/pip install -r requirements.txt
else else
# Fallback: Installiere grundlegende Pakete # Fallback: Installiere grundlegende Pakete
sudo -u "$APP_USER" ./venv/bin/pip install \ sudo -u "$APP_USER" ./venv/bin/pip install \
@ -136,14 +154,13 @@ else
werkzeug \ werkzeug \
requests \ requests \
python-dotenv \ python-dotenv \
gunicorn \ gunicorn
--break-system-packages || true
fi fi
# Node.js Abhängigkeiten installieren (falls package.json vorhanden) # Node.js Abhängigkeiten installieren
if [ -f "package.json" ]; then if [ -f "package.json" ]; then
log "Installiere Node.js Abhängigkeiten..." log "Installiere Node.js Abhängigkeiten..."
sudo -u "$APP_USER" npm install --unsafe-perm=true --allow-root sudo -u "$APP_USER" npm install
# Baue Frontend-Assets # Baue Frontend-Assets
if [ -f "tailwind.config.js" ]; then if [ -f "tailwind.config.js" ]; then
@ -157,7 +174,6 @@ log "Initialisiere Datenbank..."
if [ -f "init_db.py" ]; then if [ -f "init_db.py" ]; then
sudo -u "$APP_USER" ./venv/bin/python init_db.py sudo -u "$APP_USER" ./venv/bin/python init_db.py
else else
# Erstelle einfache SQLite-Datenbank
sudo -u "$APP_USER" touch database.db sudo -u "$APP_USER" touch database.db
fi fi
@ -179,8 +195,96 @@ EOF
chown "$APP_USER:$APP_USER" "$APP_DIR/.env" chown "$APP_USER:$APP_USER" "$APP_DIR/.env"
# Erstelle Kiosk-Skript (nach offizieller Anleitung)
log "Erstelle Kiosk-Skript..."
cat > "/home/$KIOSK_USER/kiosk.sh" << EOF
#!/bin/bash
# Warte auf System-Bereitschaft
sleep 5
# Setze Display-Umgebung
export DISPLAY=:0
# Deaktiviere Bildschirmschoner (nach offizieller Anleitung)
xset s noblank
xset s off
xset -dpms
# Verstecke Mauszeiger
unclutter -idle 0.5 -root &
# Bereinige Chromium-Präferenzen (verhindert Crash-Warnungen)
sed -i 's/"exited_cleanly":false/"exited_cleanly":true/' /home/$KIOSK_USER/.config/chromium/Default/Preferences 2>/dev/null || true
sed -i 's/"exit_type":"Crashed"/"exit_type":"Normal"/' /home/$KIOSK_USER/.config/chromium/Default/Preferences 2>/dev/null || true
# Warte auf Netzwerk und Anwendung
while ! curl -s http://localhost > /dev/null; do
echo "Warte auf Anwendung..."
sleep 2
done
# Starte Chromium im Kiosk-Modus (nach offizieller Anleitung)
/usr/bin/chromium-browser \\
--noerrdialogs \\
--disable-infobars \\
--kiosk \\
--no-sandbox \\
--disable-web-security \\
--disable-features=TranslateUI \\
--disable-ipc-flooding-protection \\
--disable-renderer-backgrounding \\
--disable-backgrounding-occluded-windows \\
--disable-background-timer-throttling \\
--disable-background-networking \\
--disable-breakpad \\
--disable-component-extensions-with-background-pages \\
--disable-dev-shm-usage \\
--disable-extensions \\
--disable-features=TranslateUI,BlinkGenPropertyTrees \\
--disable-hang-monitor \\
--disable-popup-blocking \\
--disable-prompt-on-repost \\
--disable-sync \\
--disable-translate \\
--force-color-profile=srgb \\
--no-first-run \\
--autoplay-policy=no-user-gesture-required \\
--start-fullscreen \\
$KIOSK_URL &
# Optional: Automatische Tab-Rotation (auskommentiert)
# while true; do
# sleep 30
# xdotool keydown ctrl+r; xdotool keyup ctrl+r; # Seite neu laden
# done
EOF
chmod +x "/home/$KIOSK_USER/kiosk.sh"
chown "$KIOSK_USER:$KIOSK_USER" "/home/$KIOSK_USER/kiosk.sh"
# Konfiguriere LightDM für automatischen Login (nach offizieller Anleitung)
log "Konfiguriere automatischen Login..."
cat > "/etc/lightdm/lightdm.conf" << EOF
[Seat:*]
autologin-user=$KIOSK_USER
autologin-user-timeout=0
user-session=openbox
xserver-command=X -s 0 -dpms
EOF
# Erstelle Openbox-Konfiguration für Kiosk-Benutzer
log "Konfiguriere Openbox für Kiosk-Modus..."
mkdir -p "/home/$KIOSK_USER/.config/openbox"
cat > "/home/$KIOSK_USER/.config/openbox/autostart" << EOF
# Starte Kiosk-Anwendung automatisch
/home/$KIOSK_USER/kiosk.sh &
EOF
chown -R "$KIOSK_USER:$KIOSK_USER" "/home/$KIOSK_USER/.config"
# Erstelle Systemd-Service für die Anwendung # Erstelle Systemd-Service für die Anwendung
log "Erstelle Systemd-Service..." log "Erstelle Systemd-Service für Anwendung..."
cat > "/etc/systemd/system/$SERVICE_NAME.service" << EOF cat > "/etc/systemd/system/$SERVICE_NAME.service" << EOF
[Unit] [Unit]
Description=MYP Druckerverwaltung Flask Application Description=MYP Druckerverwaltung Flask Application
@ -202,6 +306,27 @@ StandardError=journal
WantedBy=multi-user.target WantedBy=multi-user.target
EOF EOF
# Erstelle Systemd-Service für Kiosk (nach offizieller Anleitung)
log "Erstelle Systemd-Service für Kiosk..."
cat > "/etc/systemd/system/$KIOSK_SERVICE_NAME.service" << EOF
[Unit]
Description=MYP Chromium Kiosk
Wants=graphical.target
After=graphical.target $SERVICE_NAME.service
[Service]
Environment=DISPLAY=:0.0
Environment=XAUTHORITY=/home/$KIOSK_USER/.Xauthority
Type=simple
ExecStart=/bin/bash /home/$KIOSK_USER/kiosk.sh
Restart=on-abort
User=$KIOSK_USER
Group=$KIOSK_USER
[Install]
WantedBy=graphical.target
EOF
# Nginx-Konfiguration # Nginx-Konfiguration
log "Konfiguriere Nginx..." log "Konfiguriere Nginx..."
cat > "/etc/nginx/sites-available/myp-druckerverwaltung" << EOF cat > "/etc/nginx/sites-available/myp-druckerverwaltung" << EOF
@ -237,112 +362,6 @@ EOF
rm -f /etc/nginx/sites-enabled/default rm -f /etc/nginx/sites-enabled/default
ln -sf /etc/nginx/sites-available/myp-druckerverwaltung /etc/nginx/sites-enabled/ ln -sf /etc/nginx/sites-available/myp-druckerverwaltung /etc/nginx/sites-enabled/
# Kiosk-Modus konfigurieren
log "Konfiguriere Kiosk-Modus..."
# Erstelle Kiosk-Benutzer
if ! id "kiosk" &>/dev/null; then
useradd -m -s /bin/bash kiosk
usermod -aG audio,video kiosk
fi
# Erstelle Kiosk-Startskript
cat > "/home/kiosk/start-kiosk.sh" << 'EOF'
#!/bin/bash
# Warte auf Netzwerk
sleep 10
# Setze Display
export DISPLAY=:0
# Deaktiviere Bildschirmschoner und Energiesparmodus
xset s off
xset -dpms
xset s noblank
# Verstecke Mauszeiger
unclutter -idle 0.5 -root &
# Starte Chromium im Kiosk-Modus
chromium-browser \
--kiosk \
--no-sandbox \
--disable-web-security \
--disable-features=TranslateUI \
--disable-ipc-flooding-protection \
--disable-renderer-backgrounding \
--disable-backgrounding-occluded-windows \
--disable-background-timer-throttling \
--disable-background-networking \
--disable-breakpad \
--disable-component-extensions-with-background-pages \
--disable-dev-shm-usage \
--disable-extensions \
--disable-features=TranslateUI,BlinkGenPropertyTrees \
--disable-hang-monitor \
--disable-ipc-flooding-protection \
--disable-popup-blocking \
--disable-prompt-on-repost \
--disable-renderer-backgrounding \
--disable-sync \
--disable-translate \
--disable-windows10-custom-titlebar \
--force-color-profile=srgb \
--metrics-recording-only \
--no-first-run \
--safebrowsing-disable-auto-update \
--enable-automation \
--password-store=basic \
--use-mock-keychain \
--autoplay-policy=no-user-gesture-required \
--start-fullscreen \
--window-position=0,0 \
--window-size=1920,1080 \
http://localhost/
EOF
chmod +x /home/kiosk/start-kiosk.sh
chown kiosk:kiosk /home/kiosk/start-kiosk.sh
# Erstelle Systemd-Service für Kiosk
cat > "/etc/systemd/system/$KIOSK_SERVICE_NAME.service" << EOF
[Unit]
Description=MYP Kiosk Mode
After=graphical-session.target network.target $SERVICE_NAME.service
Wants=graphical-session.target
[Service]
Type=simple
User=kiosk
Group=kiosk
Environment=DISPLAY=:0
ExecStart=/home/kiosk/start-kiosk.sh
Restart=always
RestartSec=10
[Install]
WantedBy=graphical.target
EOF
# Konfiguriere LightDM für automatischen Login
log "Konfiguriere automatischen Login..."
cat > "/etc/lightdm/lightdm.conf" << EOF
[Seat:*]
autologin-user=kiosk
autologin-user-timeout=0
user-session=openbox
EOF
# Erstelle Openbox-Konfiguration für Kiosk-Benutzer
mkdir -p /home/kiosk/.config/openbox
cat > "/home/kiosk/.config/openbox/autostart" << EOF
# Starte Kiosk-Anwendung
/home/kiosk/start-kiosk.sh &
EOF
chown -R kiosk:kiosk /home/kiosk/.config
# Erstelle Wartungsskript # Erstelle Wartungsskript
log "Erstelle Wartungsskript..." log "Erstelle Wartungsskript..."
cat > "/usr/local/bin/myp-maintenance" << 'EOF' cat > "/usr/local/bin/myp-maintenance" << 'EOF'
@ -383,15 +402,23 @@ case "$1" in
echo "=== Anwendungslogs ===" echo "=== Anwendungslogs ==="
journalctl -u myp-druckerverwaltung -f journalctl -u myp-druckerverwaltung -f
;; ;;
kiosk-logs)
echo "=== Kiosk-Logs ==="
journalctl -u myp-kiosk -f
;;
update) update)
echo "Aktualisiere MYP Druckerverwaltung..." echo "Aktualisiere MYP Druckerverwaltung..."
cd /opt/myp-druckerverwaltung cd /opt/myp-druckerverwaltung
sudo -u myp git pull sudo -u myp git pull
sudo -u myp ./venv/bin/pip install -r requirements.txt --break-system-packages sudo -u myp ./venv/bin/pip install -r requirements.txt
systemctl restart myp-druckerverwaltung systemctl restart myp-druckerverwaltung
;; ;;
kiosk-restart)
echo "Starte nur Kiosk neu..."
systemctl restart myp-kiosk
;;
*) *)
echo "Verwendung: $0 {start|stop|restart|status|logs|update}" echo "Verwendung: $0 {start|stop|restart|status|logs|kiosk-logs|update|kiosk-restart}"
exit 1 exit 1
;; ;;
esac esac
@ -399,57 +426,8 @@ EOF
chmod +x /usr/local/bin/myp-maintenance chmod +x /usr/local/bin/myp-maintenance
# Aktiviere und starte Services
log "Aktiviere und starte Services..."
systemctl daemon-reload
systemctl enable "$SERVICE_NAME"
systemctl enable nginx
systemctl enable "$KIOSK_SERVICE_NAME"
# Starte Services
systemctl start "$SERVICE_NAME"
systemctl start nginx
# Warte kurz und prüfe Status
sleep 5
if systemctl is-active --quiet "$SERVICE_NAME"; then
log "✅ MYP Druckerverwaltung Service läuft"
else
warning "⚠️ MYP Druckerverwaltung Service nicht aktiv"
fi
if systemctl is-active --quiet nginx; then
log "✅ Nginx läuft"
else
warning "⚠️ Nginx nicht aktiv"
fi
# Konfiguriere Firewall (falls ufw installiert)
if command -v ufw &> /dev/null; then
log "Konfiguriere Firewall..."
ufw --force enable
ufw allow 80/tcp
ufw allow 22/tcp
fi
# Erstelle Desktop-Verknüpfung für Wartung
cat > "/home/$APP_USER/Desktop/MYP-Wartung.desktop" << EOF
[Desktop Entry]
Version=1.0
Type=Application
Name=MYP Wartung
Comment=MYP Druckerverwaltung Wartung
Exec=gnome-terminal -- sudo myp-maintenance status
Icon=applications-system
Terminal=true
Categories=System;
EOF
chown "$APP_USER:$APP_USER" "/home/$APP_USER/Desktop/MYP-Wartung.desktop"
chmod +x "/home/$APP_USER/Desktop/MYP-Wartung.desktop"
# Erstelle Backup-Skript # Erstelle Backup-Skript
log "Erstelle Backup-Skript..."
cat > "/usr/local/bin/myp-backup" << 'EOF' cat > "/usr/local/bin/myp-backup" << 'EOF'
#!/bin/bash #!/bin/bash
@ -486,6 +464,40 @@ chmod +x /usr/local/bin/myp-backup
# Erstelle Cron-Job für automatische Backups # Erstelle Cron-Job für automatische Backups
echo "0 2 * * * root /usr/local/bin/myp-backup" > /etc/cron.d/myp-backup echo "0 2 * * * root /usr/local/bin/myp-backup" > /etc/cron.d/myp-backup
# Aktiviere und starte Services
log "Aktiviere und starte Services..."
systemctl daemon-reload
systemctl enable "$SERVICE_NAME"
systemctl enable nginx
systemctl enable "$KIOSK_SERVICE_NAME"
# Starte Services
systemctl start "$SERVICE_NAME"
systemctl start nginx
# Warte kurz und prüfe Status
sleep 5
if systemctl is-active --quiet "$SERVICE_NAME"; then
log "✅ MYP Druckerverwaltung Service läuft"
else
warning "⚠️ MYP Druckerverwaltung Service nicht aktiv"
fi
if systemctl is-active --quiet nginx; then
log "✅ Nginx läuft"
else
warning "⚠️ Nginx nicht aktiv"
fi
# Konfiguriere Firewall (falls ufw installiert)
if command -v ufw &> /dev/null; then
log "Konfiguriere Firewall..."
ufw --force enable
ufw allow 80/tcp
ufw allow 22/tcp
fi
# Abschlussmeldung # Abschlussmeldung
log "=== Installation abgeschlossen! ===" log "=== Installation abgeschlossen! ==="
echo echo
@ -495,6 +507,7 @@ info "📋 Wichtige Informationen:"
info " • Anwendung läuft auf: http://$(hostname -I | awk '{print $1}')" info " • Anwendung läuft auf: http://$(hostname -I | awk '{print $1}')"
info " • Anwendungsverzeichnis: $APP_DIR" info " • Anwendungsverzeichnis: $APP_DIR"
info " • Anwendungsbenutzer: $APP_USER" info " • Anwendungsbenutzer: $APP_USER"
info " • Kiosk-Benutzer: $KIOSK_USER"
info " • Service-Name: $SERVICE_NAME" info " • Service-Name: $SERVICE_NAME"
info " • Kiosk-Service: $KIOSK_SERVICE_NAME" info " • Kiosk-Service: $KIOSK_SERVICE_NAME"
echo echo
@ -502,13 +515,22 @@ info "🔧 Wartungskommandos:"
info " • Status prüfen: myp-maintenance status" info " • Status prüfen: myp-maintenance status"
info " • Neustart: myp-maintenance restart" info " • Neustart: myp-maintenance restart"
info " • Logs anzeigen: myp-maintenance logs" info " • Logs anzeigen: myp-maintenance logs"
info " • Kiosk-Logs: myp-maintenance kiosk-logs"
info " • Nur Kiosk neustarten: myp-maintenance kiosk-restart"
info " • Update: myp-maintenance update" info " • Update: myp-maintenance update"
info " • Backup erstellen: myp-backup" info " • Backup erstellen: myp-backup"
echo echo
info "🖥️ Kiosk-Modus:" info "🖥️ Kiosk-Modus (nach offizieller Raspberry Pi Anleitung):"
info " • Wird beim nächsten Neustart automatisch gestartet" info " • Automatischer Login als '$KIOSK_USER' Benutzer"
info " • Vollbild-Browser auf http://localhost" info " • Chromium-Browser im Vollbild-Kiosk-Modus"
info " • Automatischer Login als 'kiosk' Benutzer" info " • Automatische Anzeige von $KIOSK_URL"
info " • Versteckter Mauszeiger"
info " • Deaktivierte Bildschirmschoner"
echo
info "🔍 Zusätzliche Konfiguration:"
info " • Kiosk-Skript: /home/$KIOSK_USER/kiosk.sh"
info " • LightDM-Konfiguration: /etc/lightdm/lightdm.conf"
info " • Openbox-Autostart: /home/$KIOSK_USER/.config/openbox/autostart"
echo echo
warning "⚠️ Wichtiger Hinweis:" warning "⚠️ Wichtiger Hinweis:"
warning " Starte das System neu, um den Kiosk-Modus zu aktivieren:" warning " Starte das System neu, um den Kiosk-Modus zu aktivieren:"

View File

@ -0,0 +1,526 @@
/**
* Mercedes-Benz MYP Platform - Responsive Verbesserungen & Problembehebung
* Entfernt eckige Hintergründe und verbessert Responsivität
*/
/* Globale Überschreibungen für problematische Hintergründe */
* {
background-color: transparent !important;
}
/* Erlaubte Hintergründe explizit definieren */
body,
html {
background: #f8fafc !important;
}
.dark body,
.dark html {
background: #000000 !important;
}
/* Mercedes Professional Overrides */
.bg-professional,
.professional-hero,
.professional-container,
.mb-glass,
.card-professional,
.stat-card {
background: #f8fafc !important;
border-radius: 2rem !important;
}
.dark .bg-professional,
.dark .professional-hero,
.dark .professional-container,
.dark .mb-glass,
.dark .card-professional,
.dark .stat-card {
background: #111111 !important;
border-color: #333333 !important;
}
/* Entferne alle eckigen Hintergründe mit problematischen Farben */
[style*="#182031"],
[style*="#d5d7d8"],
[style*="rgb(24, 32, 49)"],
[style*="rgb(213, 215, 216)"] {
background: transparent !important;
background-color: transparent !important;
}
/* Navbar und Navigation - Responsive */
.navbar {
background: rgba(255, 255, 255, 0.95) !important;
backdrop-filter: blur(20px) !important;
border-bottom: 1px solid rgba(0, 0, 0, 0.1) !important;
}
.dark .navbar {
background: rgba(0, 0, 0, 0.95) !important;
border-bottom: 1px solid rgba(255, 255, 255, 0.1) !important;
}
/* Responsive Typography */
@media (max-width: 640px) {
.title-professional {
font-size: 2.5rem !important;
line-height: 1.1 !important;
}
.subtitle-professional {
font-size: 1.125rem !important;
line-height: 1.5 !important;
}
.btn-professional {
padding: 0.75rem 1.5rem !important;
font-size: 0.875rem !important;
}
.professional-container,
.mb-glass,
.card-professional {
padding: 1.5rem !important;
margin: 1rem !important;
border-radius: 1rem !important;
}
}
@media (min-width: 641px) and (max-width: 768px) {
.title-professional {
font-size: 3.5rem !important;
line-height: 1.1 !important;
}
.subtitle-professional {
font-size: 1.25rem !important;
line-height: 1.6 !important;
}
.btn-professional {
padding: 1rem 2rem !important;
font-size: 1rem !important;
}
.professional-container,
.mb-glass,
.card-professional {
padding: 2rem !important;
margin: 1.5rem !important;
border-radius: 1.5rem !important;
}
}
@media (min-width: 769px) and (max-width: 1024px) {
.title-professional {
font-size: 4.5rem !important;
line-height: 1.1 !important;
}
.subtitle-professional {
font-size: 1.5rem !important;
line-height: 1.6 !important;
}
.btn-professional {
padding: 1.25rem 2.5rem !important;
font-size: 1.125rem !important;
}
.professional-container,
.mb-glass,
.card-professional {
padding: 2.5rem !important;
margin: 2rem !important;
border-radius: 2rem !important;
}
}
@media (min-width: 1025px) {
.title-professional {
font-size: 6rem !important;
line-height: 1.1 !important;
}
.subtitle-professional {
font-size: 2rem !important;
line-height: 1.6 !important;
}
.btn-professional {
padding: 1.5rem 3rem !important;
font-size: 1.25rem !important;
}
.professional-container,
.mb-glass,
.card-professional {
padding: 3rem !important;
margin: 2rem !important;
border-radius: 2rem !important;
}
}
/* Responsive Grid Improvements */
@media (max-width: 640px) {
.grid {
grid-template-columns: 1fr !important;
gap: 1rem !important;
}
.grid-cols-2 {
grid-template-columns: 1fr !important;
}
.grid-cols-3 {
grid-template-columns: 1fr !important;
}
.grid-cols-4 {
grid-template-columns: 1fr !important;
}
}
@media (min-width: 641px) and (max-width: 768px) {
.grid-cols-3 {
grid-template-columns: repeat(2, 1fr) !important;
}
.grid-cols-4 {
grid-template-columns: repeat(2, 1fr) !important;
}
}
/* Responsive Flex Improvements */
.flex-col-mobile {
flex-direction: column !important;
}
@media (min-width: 768px) {
.flex-col-mobile {
flex-direction: row !important;
}
}
/* Responsive Spacing */
@media (max-width: 640px) {
.space-y-6 > * + * {
margin-top: 1rem !important;
}
.space-y-8 > * + * {
margin-top: 1.5rem !important;
}
.space-y-12 > * + * {
margin-top: 2rem !important;
}
.space-x-6 > * + * {
margin-left: 1rem !important;
}
.space-x-8 > * + * {
margin-left: 1.5rem !important;
}
}
/* Input Field Responsivität */
.input-professional {
background: #ffffff !important;
border: 2px solid #e2e8f0 !important;
border-radius: 1rem !important;
padding: 1rem !important;
font-size: 1rem !important;
width: 100% !important;
}
.dark .input-professional {
background: #1a1a1a !important;
border-color: #333333 !important;
color: #ffffff !important;
}
.input-professional:focus {
border-color: #3b82f6 !important;
box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.1) !important;
background: #ffffff !important;
}
.dark .input-professional:focus {
background: #222222 !important;
}
@media (max-width: 640px) {
.input-professional {
padding: 0.875rem !important;
font-size: 0.875rem !important;
border-radius: 0.75rem !important;
}
}
/* Alert und Modal Responsivität */
.alert-professional {
border-radius: 1.5rem !important;
padding: 2rem !important;
margin-bottom: 2rem !important;
}
@media (max-width: 640px) {
.alert-professional {
padding: 1.5rem !important;
margin-bottom: 1.5rem !important;
border-radius: 1rem !important;
}
}
/* Status Card Responsivität */
.status-professional {
display: inline-flex !important;
align-items: center !important;
gap: 0.5rem !important;
padding: 0.75rem 1rem !important;
border-radius: 2rem !important;
font-size: 0.875rem !important;
font-weight: 700 !important;
border: 1px solid transparent !important;
}
@media (max-width: 640px) {
.status-professional {
padding: 0.5rem 0.75rem !important;
font-size: 0.75rem !important;
border-radius: 1rem !important;
}
}
/* Button Responsivität */
.btn-professional {
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%) !important;
color: white !important;
border: none !important;
border-radius: 1rem !important;
font-weight: 700 !important;
text-decoration: none !important;
transition: all 0.3s ease !important;
box-shadow: 0 4px 15px rgba(59, 130, 246, 0.3) !important;
}
.btn-professional:hover {
background: linear-gradient(135deg, #1d4ed8 0%, #1e40af 100%) !important;
transform: translateY(-2px) !important;
box-shadow: 0 8px 25px rgba(59, 130, 246, 0.4) !important;
color: white !important;
}
@media (max-width: 640px) {
.btn-professional {
width: 100% !important;
justify-content: center !important;
padding: 1rem 2rem !important;
font-size: 1rem !important;
}
}
/* Hero Header Responsivität */
.professional-hero {
position: relative !important;
overflow: hidden !important;
background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%) !important;
border: 1px solid #e2e8f0 !important;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1) !important;
}
.dark .professional-hero {
background: linear-gradient(135deg, #000000 0%, #1a1a1a 100%) !important;
border-color: #333333 !important;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5) !important;
}
/* Mobile Navigation Verbesserungen */
@media (max-width: 1024px) {
.navbar-menu-new {
display: none !important;
}
.mobile-menu-new {
background: rgba(255, 255, 255, 0.95) !important;
backdrop-filter: blur(20px) !important;
border-top: 1px solid rgba(0, 0, 0, 0.1) !important;
}
.dark .mobile-menu-new {
background: rgba(0, 0, 0, 0.95) !important;
border-top: 1px solid rgba(255, 255, 255, 0.1) !important;
}
.mobile-nav-item {
display: flex !important;
align-items: center !important;
padding: 1rem !important;
border-radius: 0.75rem !important;
color: #64748b !important;
text-decoration: none !important;
transition: all 0.3s ease !important;
}
.dark .mobile-nav-item {
color: #94a3b8 !important;
}
.mobile-nav-item:hover,
.mobile-nav-item.active {
background: rgba(59, 130, 246, 0.1) !important;
color: #3b82f6 !important;
}
}
/* Container Max-Width Responsivität */
.max-w-7xl {
max-width: 100% !important;
padding-left: 1rem !important;
padding-right: 1rem !important;
}
@media (min-width: 640px) {
.max-w-7xl {
padding-left: 1.5rem !important;
padding-right: 1.5rem !important;
}
}
@media (min-width: 768px) {
.max-w-7xl {
padding-left: 2rem !important;
padding-right: 2rem !important;
}
}
@media (min-width: 1024px) {
.max-w-7xl {
max-width: 80rem !important;
padding-left: 2rem !important;
padding-right: 2rem !important;
}
}
/* Entferne alle potentiellen eckigen Standard-Hintergründe */
div, section, article, aside, header, footer, main, nav {
background-color: transparent !important;
}
/* Glasmorphism Verbesserungen */
.mb-glass {
background: rgba(255, 255, 255, 0.9) !important;
backdrop-filter: blur(20px) !important;
border: 1px solid rgba(255, 255, 255, 0.2) !important;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1) !important;
}
.dark .mb-glass {
background: rgba(17, 17, 17, 0.95) !important;
border: 1px solid rgba(255, 255, 255, 0.1) !important;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3) !important;
}
/* Entfernt alle Standardhintergründe von Browsern */
input, textarea, select, button {
background-color: transparent !important;
}
/* Erlaubte Input-Hintergründe */
.input-professional,
input[type="text"],
input[type="email"],
input[type="password"],
input[type="number"],
input[type="datetime-local"],
textarea,
select {
background: #ffffff !important;
border: 2px solid #e2e8f0 !important;
}
.dark .input-professional,
.dark input[type="text"],
.dark input[type="email"],
.dark input[type="password"],
.dark input[type="number"],
.dark input[type="datetime-local"],
.dark textarea,
.dark select {
background: #1a1a1a !important;
border-color: #333333 !important;
color: #ffffff !important;
}
/* Animation Verbesserungen */
@keyframes fadeInResponsive {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-fade-in {
animation: fadeInResponsive 0.6s ease-out !important;
}
.animate-slide-up {
animation: fadeInResponsive 0.8s ease-out !important;
}
.animate-scale-in {
animation: fadeInResponsive 0.5s ease-out !important;
}
/* Smooth Scrolling */
html {
scroll-behavior: smooth !important;
}
/* Performance Optimierungen */
* {
box-sizing: border-box !important;
}
img {
max-width: 100% !important;
height: auto !important;
}
/* Accessibility Verbesserungen */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* Focus Indicators */
button:focus,
input:focus,
textarea:focus,
select:focus,
a:focus {
outline: 2px solid #3b82f6 !important;
outline-offset: 2px !important;
}
/* Entfernt problematische Farben aus allen Elementen */
[style*="background-color: #182031"],
[style*="background: #182031"],
[style*="background-color: #d5d7d8"],
[style*="background: #d5d7d8"] {
background: transparent !important;
background-color: transparent !important;
}

View File

@ -0,0 +1,241 @@
/**
* Mercedes-Benz MYP Platform - Background Fix
* Entfernt problematische eckige Hintergründe zur Laufzeit
*/
(function() {
'use strict';
// Problematische Farben die entfernt werden sollen
const problematicColors = [
'#182031',
'#d5d7d8',
'rgb(24, 32, 49)',
'rgb(213, 215, 216)',
'rgba(24, 32, 49, 1)',
'rgba(213, 215, 216, 1)'
];
// Funktion zum Entfernen problematischer Hintergründe
function removeProblematicBackgrounds() {
const allElements = document.querySelectorAll('*');
allElements.forEach(element => {
const computedStyle = window.getComputedStyle(element);
const backgroundColor = computedStyle.backgroundColor;
const backgroundImage = computedStyle.backgroundImage;
// Prüfe auf problematische Hintergrundfarben
problematicColors.forEach(color => {
if (backgroundColor.includes(color.replace('#', '')) ||
backgroundColor === color ||
backgroundImage.includes(color)) {
console.log('Entferne problematischen Hintergrund:', color, 'von Element:', element);
element.style.background = 'transparent';
element.style.backgroundColor = 'transparent';
element.style.backgroundImage = 'none';
}
});
// Spezielle Checks für inline styles
const inlineStyle = element.getAttribute('style');
if (inlineStyle) {
problematicColors.forEach(color => {
if (inlineStyle.includes(color)) {
console.log('Entferne problematischen Inline-Style:', color, 'von Element:', element);
// Entferne problematische Farbe aus inline style
let newStyle = inlineStyle
.replace(new RegExp(`background-color:\\s*${color.replace('#', '\\#')}[^;]*;?`, 'gi'), '')
.replace(new RegExp(`background:\\s*${color.replace('#', '\\#')}[^;]*;?`, 'gi'), '')
.replace(/background-color:\s*transparent\s*!important\s*;?\s*/gi, '')
.trim();
if (newStyle !== inlineStyle) {
element.setAttribute('style', newStyle);
}
}
});
}
});
}
// Funktion zum Erzwingen des Mercedes-Designs
function enforceDesign() {
const isDark = document.documentElement.classList.contains('dark');
// Haupthintergrund erzwingen
document.body.style.background = isDark ? '#000000' : '#f8fafc';
document.documentElement.style.background = isDark ? '#000000' : '#f8fafc';
// Professionelle Container sicherstellen
const containers = document.querySelectorAll(
'.professional-container, .mb-glass, .card-professional, .bg-professional, .professional-hero'
);
containers.forEach(container => {
if (isDark) {
container.style.background = '#111111';
container.style.borderColor = '#333333';
} else {
container.style.background = '#f8fafc';
container.style.borderColor = '#e2e8f0';
}
container.style.borderRadius = '2rem';
});
// Input-Felder sicherstellen
const inputs = document.querySelectorAll(
'.input-professional, input[type="text"], input[type="email"], input[type="password"], input[type="number"], textarea, select'
);
inputs.forEach(input => {
if (isDark) {
input.style.background = '#1a1a1a';
input.style.borderColor = '#333333';
input.style.color = '#ffffff';
} else {
input.style.background = '#ffffff';
input.style.borderColor = '#e2e8f0';
input.style.color = '#0f172a';
}
input.style.borderWidth = '2px';
input.style.borderRadius = '1rem';
});
}
// Funktion zum Überwachen von DOM-Änderungen
function observeChanges() {
const observer = new MutationObserver(function(mutations) {
let shouldCheck = false;
mutations.forEach(function(mutation) {
if (mutation.type === 'childList' || mutation.type === 'attributes') {
shouldCheck = true;
}
});
if (shouldCheck) {
// Kleine Verzögerung um Layout-Thrashing zu vermeiden
setTimeout(() => {
removeProblematicBackgrounds();
enforceDesign();
}, 10);
}
});
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['style', 'class']
});
}
// Dark Mode Toggle überwachen
function setupDarkModeObserver() {
const darkModeObserver = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
const target = mutation.target;
if (target === document.documentElement &&
(target.classList.contains('dark') || !target.classList.contains('dark'))) {
// Warte kurz und wende dann das Design neu an
setTimeout(() => {
enforceDesign();
}, 50);
}
}
});
});
darkModeObserver.observe(document.documentElement, {
attributes: true,
attributeFilter: ['class']
});
}
// Entferne CSS-Klassen die problematische Hintergründe verursachen könnten
function removeProblematicClasses() {
const elements = document.querySelectorAll('*');
const problematicClassPatterns = [
/bg-slate-\d+/,
/bg-gray-\d+/,
/bg-zinc-\d+/
];
elements.forEach(element => {
const classList = Array.from(element.classList);
let classesRemoved = false;
classList.forEach(className => {
problematicClassPatterns.forEach(pattern => {
if (pattern.test(className)) {
console.log('Entferne problematische CSS-Klasse:', className, 'von Element:', element);
element.classList.remove(className);
classesRemoved = true;
}
});
});
if (classesRemoved) {
// Füge professional classes hinzu
if (!element.classList.contains('bg-professional') &&
!element.classList.contains('professional-container') &&
!element.classList.contains('mb-glass')) {
// Nur bei bestimmten Elementen
if (element.tagName === 'DIV' || element.tagName === 'SECTION') {
element.classList.add('bg-professional');
}
}
}
});
}
// Initialisierung
function init() {
console.log('Mercedes-Benz Background Fix - Initialisierung');
// Sofort ausführen
removeProblematicBackgrounds();
removeProblematicClasses();
enforceDesign();
// Observer einrichten
observeChanges();
setupDarkModeObserver();
// Regelmäßige Überprüfung alle 5 Sekunden
setInterval(() => {
removeProblematicBackgrounds();
enforceDesign();
}, 5000);
console.log('Mercedes-Benz Background Fix - Aktiv');
}
// Starten wenn DOM bereit ist
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
// Auch beim Laden von Ressourcen
window.addEventListener('load', () => {
setTimeout(() => {
removeProblematicBackgrounds();
enforceDesign();
}, 100);
});
// Expose für Debug-Zwecke
window.mercedesBackgroundFix = {
removeProblematicBackgrounds,
enforceDesign,
removeProblematicClasses
};
})();

View File

@ -29,6 +29,7 @@
<link href="{{ url_for('static', filename='css/tailwind.min.css') }}" rel="stylesheet"> <link href="{{ url_for('static', filename='css/tailwind.min.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/components.css') }}" rel="stylesheet"> <link href="{{ url_for('static', filename='css/components.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/professional-theme.css') }}" rel="stylesheet"> <link href="{{ url_for('static', filename='css/professional-theme.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/responsive-improvements.css') }}" rel="stylesheet">
<!-- Preload critical resources --> <!-- Preload critical resources -->
<link rel="preload" href="{{ url_for('static', filename='js/ui-components.js') }}" as="script"> <link rel="preload" href="{{ url_for('static', filename='js/ui-components.js') }}" as="script">
@ -488,6 +489,7 @@
</footer> </footer>
<!-- JavaScript --> <!-- JavaScript -->
<script src="{{ url_for('static', filename='js/background-fix.js') }}"></script>
<script src="{{ url_for('static', filename='js/ui-components.js') }}"></script> <script src="{{ url_for('static', filename='js/ui-components.js') }}"></script>
<script src="{{ url_for('static', filename='js/dark-mode-fix.js') }}"></script> <script src="{{ url_for('static', filename='js/dark-mode-fix.js') }}"></script>
{% if current_user.is_authenticated %} {% if current_user.is_authenticated %}