This commit is contained in:
2025-06-11 12:20:16 +02:00
parent 9fdf7b7d35
commit d219098bc8
4 changed files with 778 additions and 555 deletions

View File

@ -1 +1,344 @@
# 🚀 MYP Frontend Build System
## Air-Gapped Build System für Mercedes-Benz TBA Marienfelde
Dieses Build-System ermöglicht die lokale Kompilierung und Optimierung aller Frontend-Assets für den **air-gapped Betrieb** (ohne Internetverbindung).
---
## 📋 **Schnellstart**
### **1. Initial Setup (einmalig mit Internet)**
```bash
# Im backend-Verzeichnis
cd backend
# Dependencies installieren
npm install
```
### **2. Build ausführen**
```bash
# Vollständiger Build
npm run build
# Entwicklungsmodus (Watch)
npm run dev
```
### **3. Air-Gapped Installation**
```bash
# Für Systeme ohne Internet
npm run install:air-gapped
```
---
## 🛠️ **Verfügbare Befehle**
| Befehl | Beschreibung |
|--------|-------------|
| `npm run build` | Vollständiger Production-Build |
| `npm run build:tailwind` | Nur Tailwind CSS kompilieren |
| `npm run build:css` | Alias für build:tailwind |
| `npm run build:js` | JavaScript minifizieren |
| `npm run watch` | Watch-Modus für Entwicklung |
| `npm run dev` | Entwicklungsmodus starten |
| `npm run clean` | Build-Artefakte löschen |
| `npm run optimize` | Build + Komprimierung |
| `npm run compress` | Assets mit gzip komprimieren |
| `npm run analyze` | CSS-Analyse und Debug-Output |
---
## 📁 **Dateistruktur**
```
backend/
├── package.json # Node.js Dependencies
├── tailwind.config.js # Tailwind CSS Konfiguration
├── postcss.config.js # PostCSS Setup
├── scripts/
│ └── compress-assets.js # Asset-Komprimierung
├── static/
│ ├── css/
│ │ ├── input.css # Tailwind Source
│ │ ├── tailwind.min.css # Kompilierte Ausgabe
│ │ └── *.css.gz # Komprimierte Versionen
│ └── js/
│ ├── *.js # JavaScript Source
│ └── *.min.js # Minifizierte Versionen
└── templates/ # HTML Templates
```
---
## 🎨 **Tailwind CSS Konfiguration**
### **Mercedes-Benz Design System**
Das Build-System enthält vordefinierte Mercedes-Benz Farben und Komponenten:
```css
/* Verfügbare CSS-Klassen */
.btn-mercedes /* Mercedes-Benz Button */
.mercedes-form-input /* Formulareingaben */
.dashboard-card /* Dashboard-Karten */
.glassmorphism /* Glassmorphism-Effekt */
```
### **Custom Farben**
```css
/* Mercedes-Benz Corporate Colors */
bg-mercedes-black /* #000000 */
bg-mercedes-blue /* #0073ce */
bg-mercedes-silver /* #aaa9ad */
bg-mercedes-gray /* #5e5e5e */
```
---
## 🔧 **Air-Gapped Setup**
### **Schritt 1: Online-Vorbereitung**
Auf einem System **mit Internet**:
```bash
# 1. Repository klonen
git clone <repository-url>
cd backend
# 2. Dependencies installieren
npm install
# 3. Initialen Build erstellen
npm run build
# 4. node_modules packen für Offline-Transfer
tar -czf node_modules_backup.tar.gz node_modules/
```
### **Schritt 2: Offline-Installation**
Auf dem **air-gapped System**:
```bash
# 1. Repository-Dateien übertragen
# 2. node_modules entpacken
tar -xzf node_modules_backup.tar.gz
# 3. Lokale Installation versuchen
npm run install:air-gapped
# 4. Build ausführen
npm run build
```
---
## 📊 **Performance-Optimierung**
### **Asset-Komprimierung**
```bash
# Komprimiert alle CSS/JS-Dateien mit gzip
npm run compress
```
**Ergebnis:**
- `tailwind.min.css` (221KB) → `tailwind.min.css.gz` (28KB)
- Typische Einsparung: **70-85%**
### **Production-Build**
```bash
# Für Production-Deployment
NODE_ENV=production npm run build
```
**Optimierungen:**
- CSS Purging (entfernt ungenutzte Klassen)
- Minimierung und Komprimierung
- Autoprefixer für Browser-Kompatibilität
- Critical CSS Extraction
---
## 🚨 **Troubleshooting**
### **Problem: npm install schlägt fehl**
```bash
# Lösung 1: Cache leeren
npm cache clean --force
# Lösung 2: Offline-Modus verwenden
npm install --offline --no-optional
# Lösung 3: Registry auf lokal setzen
npm config set registry http://localhost:4873
```
### **Problem: Tailwind CSS wird nicht kompiliert**
```bash
# 1. Input-Datei prüfen
ls -la static/css/input.css
# 2. Tailwind Config validieren
npx tailwindcss --help
# 3. Manueller Build
npx tailwindcss -i ./static/css/input.css -o ./static/css/tailwind.min.css --minify
```
### **Problem: Assets werden nicht komprimiert**
```bash
# 1. Node.js Version prüfen
node --version # Sollte >= 18.0.0 sein
# 2. Skript-Berechtigungen setzen
chmod +x scripts/compress-assets.js
# 3. Manuell ausführen
node scripts/compress-assets.js
```
---
## 🔄 **Update-Prozess**
### **1. Dependencies aktualisieren (mit Internet)**
```bash
# Prüfe auf Updates
npm outdated
# Update alle Dependencies
npm update
# Oder spezifische Pakete
npm install tailwindcss@latest
```
### **2. Tailwind CSS erweitern**
**Neue Komponenten hinzufügen:**
```javascript
// tailwind.config.js
module.exports = {
// ...
plugins: [
function({ addComponents }) {
addComponents({
'.neue-komponente': {
// CSS-Eigenschaften
}
})
}
]
}
```
### **3. Custom CSS erweitern**
```css
/* static/css/input.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* Custom Styles */
.meine-klasse {
/* Eigene CSS-Regeln */
}
```
---
## 📈 **Monitoring & Analytics**
### **Build-Größe überwachen**
```bash
# CSS-Größe analysieren
npm run analyze
# Detaillierte Bundle-Analyse
npx bundlesize
```
### **Performance-Metriken**
```bash
# Komprimierungsstatistiken
npm run compress
# Dateigröße vor/nach
ls -lh static/css/tailwind.min.css*
```
---
## 🛡️ **Sicherheit**
### **Dependency-Scanning**
```bash
# Security-Audit
npm audit
# Automatische Fixes
npm audit fix
```
### **Air-Gapped Best Practices**
1. **Regelmäßige Updates** der node_modules auf Online-System
2. **Signatur-Verifizierung** der übertragenen Pakete
3. **Isolierte Build-Umgebung** verwenden
4. **Backup** der funktionierenden node_modules
---
## 📞 **Support**
### **Häufige Fragen**
**Q: Kann ich das System ohne Node.js verwenden?**
A: Nein, aber die kompilierten Assets funktionieren ohne Node.js.
**Q: Wie oft sollte ich das Build-System aktualisieren?**
A: Alle 3-6 Monate oder bei kritischen Sicherheitsupdates.
**Q: Funktioniert es auf Windows?**
A: Ja, vollständig Windows-kompatibel.
### **Technische Spezifikationen**
- **Node.js**: ≥18.0.0
- **npm**: ≥9.0.0
- **Tailwind CSS**: 3.4.4
- **PostCSS**: 8.4.38
- **Komprimierung**: gzip Level 9
---
## 🎯 **Nächste Schritte**
1. ✅ Führen Sie `npm install` aus
2. ✅ Testen Sie `npm run build`
3. ✅ Prüfen Sie die generierten Assets
4. ✅ Konfigurieren Sie Ihren Webserver für `.gz`-Dateien
5. ✅ Implementieren Sie das Build-System in Ihren CI/CD-Pipeline
---
**Mercedes-Benz TBA Marienfelde - Frontend Build System v1.0.0**

View File

@ -1,553 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
MYP Druckerverwaltung - OPTIMIERTE PRODUKTIONS-VERSION
=====================================================
Standalone Flask App für Raspberry Pi Produktionsbetrieb:
- Nur HTTPS Port 443 (kein HTTP Port 5000)
- Browser-kompatible SSL-Zertifikate
- Optimierte Performance für Kiosk-Modus
- Minimale Firewall-Exposition
- Keine Proxy-Dependencies
Version: 5.0.0 Production
"""
import os
import sys
import ssl
import logging
import platform
from datetime import datetime, timedelta
# Füge App-Verzeichnis zum Python-Pfad hinzu
sys.path.insert(0, '/opt/myp')
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
# Import der Haupt-App
from app import app, app_logger
# Flask-Imports für Request-Handling
from flask import request, redirect
# SSL und Sicherheits-Imports
from utils.ssl_config import ensure_ssl_certificates, get_ssl_context
# =========================== PRODUKTIONS-KONFIGURATION ===========================
class ProductionConfig:
"""Optimierte Produktions-Konfiguration für Raspberry Pi"""
# HTTPS-Only Konfiguration
FORCE_HTTPS = True
SSL_REQUIRED = True
HTTPS_PORT = 443
HTTP_DISABLED = True
# Performance-Optimierungen
DEBUG = False
TESTING = False
OPTIMIZED_MODE = True
USE_MINIFIED_ASSETS = True
DISABLE_ANIMATIONS = True
# Sicherheits-Einstellungen
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = 'Strict'
WTF_CSRF_ENABLED = True
# SSL-Konfiguration
SSL_CERT_PATH = '/opt/myp/ssl/cert.pem'
SSL_KEY_PATH = '/opt/myp/ssl/key.pem'
# Firewall-freundliche Konfiguration
SINGLE_PORT_MODE = True
NO_ADDITIONAL_PORTS = True
# Wende Produktions-Konfiguration an
app.config.from_object(ProductionConfig)
# =========================== SSL-SETUP ===========================
def setup_production_ssl():
"""Stelle sicher, dass browser-kompatible SSL-Zertifikate vorhanden sind"""
# Plattform-spezifische SSL-Pfade
if platform.system() == 'Windows':
ssl_dir = os.path.join(os.path.dirname(__file__), 'ssl')
else:
ssl_dir = '/opt/myp/ssl'
cert_file = f'{ssl_dir}/cert.pem'
key_file = f'{ssl_dir}/key.pem'
app_logger.info("🔐 Prüfe SSL-Zertifikate für Produktionsbetrieb...")
# Erstelle SSL-Verzeichnis
os.makedirs(ssl_dir, exist_ok=True)
# Prüfe ob Zertifikate existieren und gültig sind
cert_valid = False
if os.path.exists(cert_file) and os.path.exists(key_file):
try:
# Prüfe Zertifikat-Gültigkeit
import subprocess
result = subprocess.run([
'openssl', 'x509', '-in', cert_file, '-noout', '-checkend', '86400'
], capture_output=True, text=True)
if result.returncode == 0:
# Prüfe Browser-Kompatibilität
cert_info = subprocess.run([
'openssl', 'x509', '-in', cert_file, '-noout', '-text'
], capture_output=True, text=True)
if ('Digital Signature' in cert_info.stdout and
'Key Encipherment' in cert_info.stdout and
'TLS Web Server Authentication' in cert_info.stdout and
'Subject Alternative Name' in cert_info.stdout):
cert_valid = True
app_logger.info("✅ Browser-kompatible SSL-Zertifikate gefunden")
else:
app_logger.warning("⚠️ SSL-Zertifikate nicht browser-kompatibel")
else:
app_logger.warning("⚠️ SSL-Zertifikate abgelaufen")
except Exception as e:
app_logger.warning(f"⚠️ SSL-Zertifikat-Prüfung fehlgeschlagen: {e}")
# Erstelle neue browser-kompatible Zertifikate falls nötig
if not cert_valid:
app_logger.info("🔧 Erstelle neue browser-kompatible SSL-Zertifikate...")
try:
# Führe SSL-Fix-Skript aus falls vorhanden
ssl_fix_script = '/opt/myp/fix_ssl_raspberry.sh'
if os.path.exists(ssl_fix_script):
import subprocess
result = subprocess.run(['sudo', ssl_fix_script],
capture_output=True, text=True, timeout=60)
if result.returncode == 0:
app_logger.info("✅ SSL-Fix-Skript erfolgreich ausgeführt")
else:
app_logger.error(f"❌ SSL-Fix-Skript Fehler: {result.stderr}")
raise Exception("SSL-Fix-Skript fehlgeschlagen")
else:
# Fallback: Manuelle SSL-Erstellung
create_production_ssl_certificates(ssl_dir)
except Exception as e:
app_logger.error(f"❌ SSL-Zertifikat-Erstellung fehlgeschlagen: {e}")
raise
return cert_file, key_file
def create_production_ssl_certificates(ssl_dir):
"""Erstelle browser-kompatible SSL-Zertifikate plattformübergreifend"""
app_logger.info("🔧 Erstelle browser-kompatible SSL-Zertifikate...")
# Versuche OpenSSL (Linux/Raspberry Pi)
if platform.system() != 'Windows':
try:
create_ssl_with_openssl(ssl_dir)
return
except Exception as e:
app_logger.warning(f"⚠️ OpenSSL fehlgeschlagen: {e}")
# Fallback: Python Cryptography Library (Windows + Linux)
try:
create_ssl_with_python(ssl_dir)
except ImportError as e:
app_logger.error("❌ Cryptography Library nicht installiert")
app_logger.error("💡 Installiere mit: pip install cryptography")
app_logger.error("💡 Dann starte das Skript neu")
raise Exception("SSL-Zertifikat-Erstellung erfordert 'cryptography' library")
def create_ssl_with_openssl(ssl_dir):
"""Erstelle SSL-Zertifikate mit OpenSSL"""
import subprocess
import tempfile
# OpenSSL-Konfiguration für Browser-Kompatibilität
openssl_config = f"""[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no
[req_distinguished_name]
C = DE
ST = Baden-Wuerttemberg
L = Stuttgart
O = Mercedes-Benz AG
OU = MYP Druckerverwaltung
CN = m040tbaraspi001
[v3_req]
# KRITISCH für Browser-Kompatibilität
basicConstraints = critical, CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment, keyAgreement
extendedKeyUsage = critical, serverAuth, clientAuth
subjectAltName = critical, @alt_names
nsCertType = server
nsComment = "MYP Production SSL - Browser Compatible"
[alt_names]
# Lokale Entwicklung
DNS.1 = localhost
DNS.2 = *.localhost
IP.1 = 127.0.0.1
IP.2 = ::1
# Raspberry Pi Hostname
DNS.3 = m040tbaraspi001
DNS.4 = m040tbaraspi001.local
DNS.5 = raspberrypi
DNS.6 = raspberrypi.local
# Intranet-Domain
DNS.7 = m040tbaraspi001.de040.corpintra.net
DNS.8 = *.de040.corpintra.net
"""
# Schreibe Konfiguration in temporäre Datei
with tempfile.NamedTemporaryFile(mode='w', suffix='.conf', delete=False) as f:
f.write(openssl_config)
config_file = f.name
try:
# Generiere Private Key
subprocess.run([
'openssl', 'genrsa', '-out', f'{ssl_dir}/key.pem', '2048'
], check=True, capture_output=True)
# Generiere browser-kompatibles Zertifikat
subprocess.run([
'openssl', 'req', '-new', '-x509',
'-key', f'{ssl_dir}/key.pem',
'-out', f'{ssl_dir}/cert.pem',
'-days', '365',
'-config', config_file,
'-extensions', 'v3_req',
'-sha256'
], check=True, capture_output=True)
# Setze korrekte Berechtigungen
os.chmod(f'{ssl_dir}/cert.pem', 0o644)
os.chmod(f'{ssl_dir}/key.pem', 0o600)
app_logger.info("✅ Browser-kompatible SSL-Zertifikate mit OpenSSL erstellt")
finally:
# Räume temporäre Datei auf
try:
os.unlink(config_file)
except:
pass
def create_ssl_with_python(ssl_dir):
"""Erstelle SSL-Zertifikate mit Python Cryptography Library"""
from cryptography import x509
from cryptography.x509.oid import NameOID, ExtensionOID
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
import ipaddress
app_logger.info("🐍 Erstelle SSL-Zertifikate mit Python Cryptography...")
# Generiere Private Key
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
)
# Subject und Issuer
subject = issuer = x509.Name([
x509.NameAttribute(NameOID.COUNTRY_NAME, "DE"),
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Baden-Wuerttemberg"),
x509.NameAttribute(NameOID.LOCALITY_NAME, "Stuttgart"),
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Mercedes-Benz AG"),
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, "MYP Druckerverwaltung"),
x509.NameAttribute(NameOID.COMMON_NAME, "m040tbaraspi001"),
])
# Subject Alternative Names für Browser-Kompatibilität
san_list = [
# Lokale Entwicklung
x509.DNSName("localhost"),
x509.DNSName("*.localhost"),
x509.IPAddress(ipaddress.IPv4Address("127.0.0.1")),
x509.IPAddress(ipaddress.IPv6Address("::1")),
# Raspberry Pi Hostname
x509.DNSName("m040tbaraspi001"),
x509.DNSName("m040tbaraspi001.local"),
x509.DNSName("raspberrypi"),
x509.DNSName("raspberrypi.local"),
# Intranet-Domain
x509.DNSName("m040tbaraspi001.de040.corpintra.net"),
x509.DNSName("*.de040.corpintra.net"),
]
# Erstelle Zertifikat
cert = x509.CertificateBuilder().subject_name(
subject
).issuer_name(
issuer
).public_key(
private_key.public_key()
).serial_number(
x509.random_serial_number()
).not_valid_before(
datetime.now()
).not_valid_after(
datetime.now() + timedelta(days=365)
).add_extension(
x509.SubjectAlternativeName(san_list),
critical=True,
).add_extension(
x509.BasicConstraints(ca=False, path_length=None),
critical=True,
).add_extension(
x509.KeyUsage(
digital_signature=True,
key_encipherment=True,
key_agreement=True,
key_cert_sign=False,
crl_sign=False,
content_commitment=False,
data_encipherment=False,
encipher_only=False,
decipher_only=False
),
critical=True,
).add_extension(
x509.ExtendedKeyUsage([
x509.oid.ExtendedKeyUsageOID.SERVER_AUTH,
x509.oid.ExtendedKeyUsageOID.CLIENT_AUTH,
]),
critical=True,
).sign(private_key, hashes.SHA256())
# Schreibe Private Key
with open(f'{ssl_dir}/key.pem', 'wb') as f:
f.write(private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
))
# Schreibe Zertifikat
with open(f'{ssl_dir}/cert.pem', 'wb') as f:
f.write(cert.public_bytes(serialization.Encoding.PEM))
# Setze Berechtigungen falls möglich
try:
os.chmod(f'{ssl_dir}/cert.pem', 0o644)
os.chmod(f'{ssl_dir}/key.pem', 0o600)
except:
pass # Windows hat andere Berechtigungen
app_logger.info("✅ Browser-kompatible SSL-Zertifikate mit Python erstellt")
# =========================== PRODUKTIONS-SSL-KONTEXT ===========================
def get_production_ssl_context():
"""Erstelle optimierten SSL-Kontext für Produktionsbetrieb"""
cert_file, key_file = setup_production_ssl()
# Erstelle SSL-Kontext mit optimalen Einstellungen
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
# Lade Zertifikat und Key
context.load_cert_chain(cert_file, key_file)
# Optimale SSL-Einstellungen für Browser-Kompatibilität
context.set_ciphers('ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:!aNULL:!MD5:!DSS')
context.options |= ssl.OP_NO_SSLv2
context.options |= ssl.OP_NO_SSLv3
context.options |= ssl.OP_NO_TLSv1
context.options |= ssl.OP_NO_TLSv1_1
context.options |= ssl.OP_SINGLE_DH_USE
context.options |= ssl.OP_SINGLE_ECDH_USE
# Deaktiviere Kompression (CRIME-Angriff-Schutz)
context.options |= ssl.OP_NO_COMPRESSION
app_logger.info("✅ Produktions-SSL-Kontext konfiguriert")
return context
# =========================== HTTPS-REDIRECT MIDDLEWARE ===========================
@app.before_request
def force_https():
"""Erzwinge HTTPS für alle Anfragen"""
if not request.is_secure and app.config.get('FORCE_HTTPS', False):
# Redirect zu HTTPS
url = request.url.replace('http://', 'https://', 1)
# Ändere Port zu 443 falls anders
if ':5000' in url:
url = url.replace(':5000', ':443')
elif ':80' in url:
url = url.replace(':80', ':443')
return redirect(url, code=301)
# =========================== SICHERHEITS-HEADERS ===========================
@app.after_request
def add_security_headers(response):
"""Füge Sicherheits-Headers für Produktionsbetrieb hinzu"""
# HTTPS-Sicherheits-Headers
response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
response.headers['X-Content-Type-Options'] = 'nosniff'
response.headers['X-Frame-Options'] = 'SAMEORIGIN'
response.headers['X-XSS-Protection'] = '1; mode=block'
response.headers['Referrer-Policy'] = 'strict-origin-when-cross-origin'
# Content Security Policy für Kiosk-Modus
csp = (
"default-src 'self'; "
"script-src 'self' 'unsafe-inline' 'unsafe-eval'; "
"style-src 'self' 'unsafe-inline'; "
"img-src 'self' data: blob:; "
"font-src 'self'; "
"connect-src 'self'; "
"frame-ancestors 'self'"
)
response.headers['Content-Security-Policy'] = csp
# Cache-Control für statische Assets
if request.endpoint and 'static' in request.endpoint:
response.headers['Cache-Control'] = 'public, max-age=31536000'
return response
# =========================== PRODUKTIONS-LOGGING ===========================
def setup_production_logging():
"""Konfiguriere optimiertes Logging für Produktionsbetrieb"""
# Reduziere Log-Level für Performance
logging.getLogger('werkzeug').setLevel(logging.WARNING)
logging.getLogger('urllib3').setLevel(logging.WARNING)
# Produktions-Log-Format
formatter = logging.Formatter(
'%(asctime)s [%(levelname)s] %(name)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
# Stelle sicher, dass App-Logger korrekt konfiguriert ist
app_logger.setLevel(logging.INFO)
# Entferne Debug-Handler falls vorhanden
for handler in app_logger.handlers[:]:
if handler.level == logging.DEBUG:
app_logger.removeHandler(handler)
app_logger.info("✅ Produktions-Logging konfiguriert")
# =========================== HAUPTFUNKTION ===========================
def main():
"""Hauptfunktion für Produktions-Server"""
try:
app_logger.info("🚀 MYP Produktions-Server startet...")
app_logger.info(f"📅 Start-Zeit: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
app_logger.info(f"🖥️ Hostname: {platform.node()}")
app_logger.info(f"🐍 Python: {sys.version}")
# Produktions-Logging einrichten
setup_production_logging()
# Prüfe Root-Berechtigung für Port 443 (nur Unix/Linux)
if hasattr(os, 'geteuid') and os.geteuid() != 0:
app_logger.error("❌ Root-Berechtigung erforderlich für Port 443")
app_logger.error("💡 Führe aus mit: sudo python3 app_production.py")
sys.exit(1)
elif platform.system() == 'Windows':
app_logger.info("🪟 Windows-Modus: Root-Check übersprungen")
# SSL-Kontext erstellen
ssl_context = get_production_ssl_context()
# Datenbank initialisieren (aus Haupt-App)
from app import init_database, create_initial_admin
init_database()
create_initial_admin()
# Queue Manager und Scheduler starten
from app import start_queue_manager, get_job_scheduler
start_queue_manager()
scheduler = get_job_scheduler()
if scheduler:
scheduler.start()
app_logger.info("✅ Job-Scheduler gestartet")
# Server-Konfiguration
host = '0.0.0.0' # Alle Interfaces
port = 443 # Nur HTTPS Port 443
app_logger.info("🔐 HTTPS-Only Produktions-Modus")
app_logger.info(f"🌐 Server läuft auf: https://{host}:{port}")
app_logger.info(f"🏠 Lokaler Zugriff: https://localhost")
app_logger.info(f"🌍 Intranet-Zugriff: https://m040tbaraspi001.de040.corpintra.net")
app_logger.info("🔥 Firewall: Nur Port 443 erforderlich")
app_logger.info("🛡️ SSL-Zertifikate: Browser-kompatibel")
# Starte Flask-Server mit SSL
app.run(
host=host,
port=port,
ssl_context=ssl_context,
threaded=True,
debug=False,
use_reloader=False
)
except PermissionError:
app_logger.error("❌ Berechtigung verweigert für Port 443")
if platform.system() != 'Windows':
app_logger.error("💡 Führe aus mit: sudo python3 app_production.py")
else:
app_logger.error("💡 Führe als Administrator aus")
sys.exit(1)
except OSError as e:
if "Address already in use" in str(e):
app_logger.error("❌ Port 443 bereits belegt")
app_logger.error("💡 Stoppe andere Services: sudo systemctl stop apache2 nginx")
else:
app_logger.error(f"❌ Netzwerk-Fehler: {e}")
sys.exit(1)
except Exception as e:
app_logger.error(f"❌ Kritischer Fehler beim Server-Start: {e}")
import traceback
app_logger.error(f"Traceback: {traceback.format_exc()}")
sys.exit(1)
finally:
# Cleanup
try:
from app import stop_queue_manager, cleanup_rate_limiter
stop_queue_manager()
if 'scheduler' in locals() and scheduler:
scheduler.shutdown()
cleanup_rate_limiter()
app_logger.info("✅ Cleanup abgeschlossen")
except:
pass
if __name__ == "__main__":
main()

View File

@ -12,7 +12,7 @@ Group=root
WorkingDirectory=/opt/myp
# Produktions-App mit HTTPS-Only auf Port 443
ExecStart=/usr/bin/python3 /opt/myp/app_production.py
ExecStart=/usr/bin/python3 /opt/myp/app.py
Restart=always
RestartSec=10
StartLimitBurst=5