🎉 Refactor backend files & add documentation 📚, remove legacy installer scripts. #123

This commit is contained in:
Till Tomczak 2025-06-01 00:36:48 +02:00
parent 9e1719df4d
commit 7f38f8a7e5
10 changed files with 2604 additions and 172 deletions

View File

@ -1595,7 +1595,44 @@ def admin():
flash("Nur Administratoren haben Zugriff auf diesen Bereich.", "error")
return redirect(url_for("index"))
return render_template("admin.html")
# Daten für das Template sammeln (gleiche Logik wie admin-dashboard)
db_session = get_db_session()
try:
# Statistiken sammeln
stats = {
'total_users': db_session.query(User).count(),
'total_printers': db_session.query(Printer).count(),
'online_printers': db_session.query(Printer).filter(Printer.status == 'online').count(),
'active_jobs': db_session.query(Job).filter(Job.status.in_(['running', 'queued'])).count(),
'queued_jobs': db_session.query(Job).filter(Job.status == 'queued').count(),
'success_rate': 85 # Placeholder - könnte aus echten Daten berechnet werden
}
# Tab-Parameter
active_tab = request.args.get('tab', 'users')
# Benutzer laden (für users tab)
users = []
if active_tab == 'users':
users = db_session.query(User).all()
# Drucker laden (für printers tab)
printers = []
if active_tab == 'printers':
printers = db_session.query(Printer).all()
db_session.close()
return render_template("admin.html",
stats=stats,
active_tab=active_tab,
users=users,
printers=printers)
except Exception as e:
app_logger.error(f"Fehler beim Laden der Admin-Daten: {str(e)}")
db_session.close()
flash("Fehler beim Laden des Admin-Bereichs.", "error")
return redirect(url_for("index"))
@app.route("/socket-test")
@login_required
@ -1643,7 +1680,68 @@ def admin_page():
"""Erweiterte Admin-Dashboard-Seite mit Live-Funktionen"""
if not current_user.is_admin:
return redirect(url_for("index"))
return render_template("admin_dashboard.html", title="Admin Dashboard")
# Daten für das Template sammeln
db_session = get_db_session()
try:
# Statistiken sammeln
stats = {
'total_users': db_session.query(User).count(),
'total_printers': db_session.query(Printer).count(),
'online_printers': db_session.query(Printer).filter(Printer.status == 'online').count(),
'active_jobs': db_session.query(Job).filter(Job.status.in_(['running', 'queued'])).count(),
'queued_jobs': db_session.query(Job).filter(Job.status == 'queued').count(),
'success_rate': 85 # Placeholder - könnte aus echten Daten berechnet werden
}
# Tab-Parameter
active_tab = request.args.get('tab', 'users')
# Benutzer laden (für users tab)
users = []
if active_tab == 'users':
users = db_session.query(User).all()
# Drucker laden (für printers tab)
printers = []
if active_tab == 'printers':
printers = db_session.query(Printer).all()
db_session.close()
return render_template("admin.html",
title="Admin Dashboard",
stats=stats,
active_tab=active_tab,
users=users,
printers=printers)
except Exception as e:
app_logger.error(f"Fehler beim Laden der Admin-Dashboard-Daten: {str(e)}")
db_session.close()
flash("Fehler beim Laden des Admin-Dashboards.", "error")
return redirect(url_for("index"))
# ===== RECHTLICHE SEITEN =====
@app.route("/privacy")
def privacy():
"""Datenschutzerklärung-Seite"""
return render_template("privacy.html", title="Datenschutzerklärung")
@app.route("/terms")
def terms():
"""Nutzungsbedingungen-Seite"""
return render_template("terms.html", title="Nutzungsbedingungen")
@app.route("/imprint")
def imprint():
"""Impressum-Seite"""
return render_template("imprint.html", title="Impressum")
@app.route("/legal")
def legal():
"""Rechtliche Hinweise-Übersichtsseite"""
return render_template("legal.html", title="Rechtliche Hinweise")
# ===== NEUE SYSTEM UI-ROUTEN =====
@ -5060,190 +5158,282 @@ def setup_database_with_migrations():
app_logger.error(f"❌ Fehler bei Datenbank-Setup: {str(e)}")
raise e
# ===== PRIVACY UND TERMS ROUTEN =====
# ===== LOG-MANAGEMENT API =====
@app.route("/privacy")
def privacy_policy():
"""Datenschutzerklärung anzeigen"""
try:
return render_template("privacy_policy.html", title="Datenschutzerklärung")
except Exception as e:
app_logger.error(f"Fehler beim Laden der Datenschutzerklärung: {str(e)}")
flash("Fehler beim Laden der Datenschutzerklärung", "error")
return redirect(url_for("index"))
@app.route("/terms")
def terms_of_service():
"""Nutzungsbedingungen anzeigen"""
try:
return render_template("terms_of_service.html", title="Nutzungsbedingungen")
except Exception as e:
app_logger.error(f"Fehler beim Laden der Nutzungsbedingungen: {str(e)}")
flash("Fehler beim Laden der Nutzungsbedingungen", "error")
return redirect(url_for("index"))
@app.route("/legal")
def legal_notice():
"""Impressum anzeigen"""
try:
return render_template("legal_notice.html", title="Impressum")
except Exception as e:
app_logger.error(f"Fehler beim Laden des Impressums: {str(e)}")
flash("Fehler beim Laden des Impressums", "error")
return redirect(url_for("index"))
@app.route("/api/privacy/accept", methods=["POST"])
@app.route("/api/logs", methods=['GET'])
@login_required
def accept_privacy_policy():
"""API-Endpunkt für Akzeptierung der Datenschutzerklärung"""
db_session = get_db_session()
@admin_required
def api_logs():
"""
API-Endpunkt für Log-Daten-Abruf
Query Parameter:
level: Log-Level Filter (DEBUG, INFO, WARNING, ERROR, CRITICAL)
limit: Anzahl der Einträge (Standard: 100, Max: 1000)
offset: Offset für Paginierung (Standard: 0)
search: Suchbegriff für Log-Nachrichten
start_date: Start-Datum (ISO-Format)
end_date: End-Datum (ISO-Format)
"""
try:
data = request.get_json() or {}
version = data.get("version", "1.0")
# Parameter aus Query-String extrahieren
level = request.args.get('level', '').upper()
limit = min(int(request.args.get('limit', 100)), 1000)
offset = int(request.args.get('offset', 0))
search = request.args.get('search', '').strip()
start_date = request.args.get('start_date')
end_date = request.args.get('end_date')
# Benutzer aus der Datenbank laden
user = db_session.query(User).filter(User.id == int(current_user.id)).first()
# Log-Dateien aus dem logs-Verzeichnis lesen
import os
import glob
from datetime import datetime, timedelta
if not user:
return jsonify({"error": "Benutzer nicht gefunden"}), 404
logs_dir = os.path.join(os.path.dirname(__file__), 'logs')
log_entries = []
# Privacy-Akzeptierung in Benutzer-Einstellungen speichern
if hasattr(user, 'settings'):
import json
settings = json.loads(user.settings) if user.settings else {}
else:
settings = session.get('user_settings', {})
if os.path.exists(logs_dir):
# Alle .log Dateien finden
log_files = glob.glob(os.path.join(logs_dir, '*.log'))
log_files.sort(key=os.path.getmtime, reverse=True) # Neueste zuerst
# Datum-Filter vorbereiten
start_dt = None
end_dt = None
if start_date:
try:
start_dt = datetime.fromisoformat(start_date.replace('Z', '+00:00'))
except:
pass
if end_date:
try:
end_dt = datetime.fromisoformat(end_date.replace('Z', '+00:00'))
except:
pass
# Log-Dateien durchgehen (maximal die letzten 5 Dateien)
for log_file in log_files[:5]:
try:
with open(log_file, 'r', encoding='utf-8') as f:
lines = f.readlines()
# Zeilen rückwärts durchgehen (neueste zuerst)
for line in reversed(lines):
line = line.strip()
if not line:
continue
# Log-Zeile parsen
try:
# Format: 2025-06-01 00:34:08 - logger_name - [LEVEL] MESSAGE
parts = line.split(' - ', 3)
if len(parts) >= 4:
timestamp_str = parts[0]
logger_name = parts[1]
level_part = parts[2]
message = parts[3]
# Level extrahieren
if level_part.startswith('[') and ']' in level_part:
log_level = level_part.split(']')[0][1:]
else:
log_level = 'INFO'
# Timestamp parsen
try:
log_timestamp = datetime.strptime(timestamp_str, '%Y-%m-%d %H:%M:%S')
except:
continue
# Filter anwenden
if level and log_level != level:
continue
if start_dt and log_timestamp < start_dt:
continue
if end_dt and log_timestamp > end_dt:
continue
if search and search.lower() not in message.lower():
continue
log_entries.append({
'timestamp': log_timestamp.isoformat(),
'level': log_level,
'logger': logger_name,
'message': message,
'file': os.path.basename(log_file)
})
except Exception as parse_error:
# Fehlerhafte Zeile überspringen
continue
except Exception as file_error:
app_logger.error(f"Fehler beim Lesen der Log-Datei {log_file}: {str(file_error)}")
continue
# Privacy-Akzeptierung hinzufügen
if 'privacy_acceptance' not in settings:
settings['privacy_acceptance'] = {}
# Sortieren nach Timestamp (neueste zuerst)
log_entries.sort(key=lambda x: x['timestamp'], reverse=True)
settings['privacy_acceptance'] = {
'accepted': True,
'version': version,
'timestamp': datetime.now().isoformat(),
'ip_address': request.remote_addr
}
# Einstellungen speichern
if hasattr(user, 'settings'):
user.settings = json.dumps(settings)
user.updated_at = datetime.now()
db_session.commit()
else:
session['user_settings'] = settings
user_logger.info(f"Benutzer {current_user.username} hat Datenschutzerklärung v{version} akzeptiert")
# Paginierung anwenden
total_count = len(log_entries)
paginated_entries = log_entries[offset:offset + limit]
return jsonify({
"success": True,
"message": "Datenschutzerklärung erfolgreich akzeptiert",
"version": version,
"timestamp": datetime.now().isoformat()
})
except Exception as e:
db_session.rollback()
app_logger.error(f"Fehler bei Privacy-Akzeptierung: {str(e)}")
return jsonify({"error": "Interner Serverfehler"}), 500
finally:
db_session.close()
@app.route("/api/terms/accept", methods=["POST"])
@login_required
def accept_terms_of_service():
"""API-Endpunkt für Akzeptierung der Nutzungsbedingungen"""
db_session = get_db_session()
try:
data = request.get_json() or {}
version = data.get("version", "1.0")
# Benutzer aus der Datenbank laden
user = db_session.query(User).filter(User.id == int(current_user.id)).first()
if not user:
return jsonify({"error": "Benutzer nicht gefunden"}), 404
# Terms-Akzeptierung in Benutzer-Einstellungen speichern
if hasattr(user, 'settings'):
import json
settings = json.loads(user.settings) if user.settings else {}
else:
settings = session.get('user_settings', {})
# Terms-Akzeptierung hinzufügen
if 'terms_acceptance' not in settings:
settings['terms_acceptance'] = {}
settings['terms_acceptance'] = {
'accepted': True,
'version': version,
'timestamp': datetime.now().isoformat(),
'ip_address': request.remote_addr
}
# Einstellungen speichern
if hasattr(user, 'settings'):
user.settings = json.dumps(settings)
user.updated_at = datetime.now()
db_session.commit()
else:
session['user_settings'] = settings
user_logger.info(f"Benutzer {current_user.username} hat Nutzungsbedingungen v{version} akzeptiert")
return jsonify({
"success": True,
"message": "Nutzungsbedingungen erfolgreich akzeptiert",
"version": version,
"timestamp": datetime.now().isoformat()
})
except Exception as e:
db_session.rollback()
app_logger.error(f"Fehler bei Terms-Akzeptierung: {str(e)}")
return jsonify({"error": "Interner Serverfehler"}), 500
finally:
db_session.close()
@app.route("/api/legal/status", methods=["GET"])
@login_required
def get_legal_status():
"""API-Endpunkt für Abfrage des rechtlichen Status (Privacy/Terms Akzeptierung)"""
try:
# Benutzer-Einstellungen laden
if hasattr(current_user, 'settings') and current_user.settings:
import json
settings = json.loads(current_user.settings)
else:
settings = session.get('user_settings', {})
privacy_acceptance = settings.get('privacy_acceptance', {})
terms_acceptance = settings.get('terms_acceptance', {})
return jsonify({
"success": True,
"legal_status": {
"privacy_policy": {
"accepted": privacy_acceptance.get('accepted', False),
"version": privacy_acceptance.get('version'),
"timestamp": privacy_acceptance.get('timestamp')
},
"terms_of_service": {
"accepted": terms_acceptance.get('accepted', False),
"version": terms_acceptance.get('version'),
"timestamp": terms_acceptance.get('timestamp')
},
"compliance_required": not (
privacy_acceptance.get('accepted', False) and
terms_acceptance.get('accepted', False)
)
'success': True,
'logs': paginated_entries,
'pagination': {
'total': total_count,
'limit': limit,
'offset': offset,
'has_more': offset + limit < total_count
},
'filters': {
'level': level or None,
'search': search or None,
'start_date': start_date,
'end_date': end_date
}
})
except Exception as e:
app_logger.error(f"Fehler bei Legal-Status-Abfrage: {str(e)}")
return jsonify({"error": "Interner Serverfehler"}), 500
app_logger.error(f"Fehler beim Abrufen der Log-Daten: {str(e)}")
return jsonify({
'error': f'Fehler beim Abrufen der Log-Daten: {str(e)}'
}), 500
# ===== LIVE ADMIN STATISTIKEN API =====
@app.route("/api/admin/stats/live", methods=['GET'])
@login_required
@admin_required
def api_admin_stats_live():
"""
API-Endpunkt für Live-Statistiken im Admin-Dashboard
Liefert aktuelle System-Statistiken für Echtzeit-Updates
"""
try:
db_session = get_db_session()
# Basis-Statistiken sammeln
stats = {
'timestamp': datetime.now().isoformat(),
'users': {
'total': db_session.query(User).count(),
'active_today': db_session.query(User).filter(
User.last_login >= datetime.now() - timedelta(days=1)
).count() if hasattr(User, 'last_login') else 0,
'new_this_week': db_session.query(User).filter(
User.created_at >= datetime.now() - timedelta(days=7)
).count() if hasattr(User, 'created_at') else 0
},
'printers': {
'total': db_session.query(Printer).count(),
'online': db_session.query(Printer).filter(Printer.status == 'online').count(),
'offline': db_session.query(Printer).filter(Printer.status == 'offline').count(),
'maintenance': db_session.query(Printer).filter(Printer.status == 'maintenance').count()
},
'jobs': {
'total': db_session.query(Job).count(),
'running': db_session.query(Job).filter(Job.status == 'running').count(),
'queued': db_session.query(Job).filter(Job.status == 'queued').count(),
'completed_today': db_session.query(Job).filter(
Job.status == 'completed',
Job.updated_at >= datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
).count() if hasattr(Job, 'updated_at') else 0,
'failed_today': db_session.query(Job).filter(
Job.status == 'failed',
Job.updated_at >= datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
).count() if hasattr(Job, 'updated_at') else 0
}
}
# System-Performance-Metriken
import psutil
import os
# CPU und Memory
stats['system'] = {
'cpu_percent': psutil.cpu_percent(interval=1),
'memory_percent': psutil.virtual_memory().percent,
'disk_percent': psutil.disk_usage('/').percent if os.name != 'nt' else psutil.disk_usage('C:\\').percent,
'uptime_seconds': int((datetime.now() - datetime.fromtimestamp(psutil.boot_time())).total_seconds())
}
# Erfolgsrate berechnen (letzte 24 Stunden)
try:
completed_jobs = db_session.query(Job).filter(
Job.status == 'completed',
Job.updated_at >= datetime.now() - timedelta(days=1)
).count() if hasattr(Job, 'updated_at') else 0
failed_jobs = db_session.query(Job).filter(
Job.status == 'failed',
Job.updated_at >= datetime.now() - timedelta(days=1)
).count() if hasattr(Job, 'updated_at') else 0
total_finished = completed_jobs + failed_jobs
success_rate = (completed_jobs / total_finished * 100) if total_finished > 0 else 100
stats['performance'] = {
'success_rate': round(success_rate, 1),
'completed_24h': completed_jobs,
'failed_24h': failed_jobs,
'total_finished_24h': total_finished
}
except Exception as perf_error:
app_logger.warning(f"Fehler bei Performance-Berechnung: {str(perf_error)}")
stats['performance'] = {
'success_rate': 0,
'completed_24h': 0,
'failed_24h': 0,
'total_finished_24h': 0
}
# Queue-Status (falls Queue Manager läuft)
try:
from queue_manager import get_queue_status
queue_status = get_queue_status()
stats['queue'] = queue_status
except Exception as queue_error:
stats['queue'] = {
'status': 'unknown',
'pending_jobs': 0,
'active_workers': 0
}
# Letzte Aktivitäten (Top 5)
try:
recent_jobs = db_session.query(Job).order_by(Job.id.desc()).limit(5).all()
stats['recent_activity'] = [
{
'id': job.id,
'filename': job.filename,
'status': job.status,
'user': job.user.username if job.user else 'Unbekannt',
'created_at': job.created_at.isoformat() if hasattr(job, 'created_at') and job.created_at else None
}
for job in recent_jobs
]
except Exception as activity_error:
app_logger.warning(f"Fehler bei Recent Activity: {str(activity_error)}")
stats['recent_activity'] = []
db_session.close()
return jsonify({
'success': True,
'stats': stats
})
except Exception as e:
app_logger.error(f"Fehler beim Abrufen der Live-Statistiken: {str(e)}")
return jsonify({
'error': f'Fehler beim Abrufen der Live-Statistiken: {str(e)}'
}), 500
# ===== STARTUP UND MAIN =====

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@

File diff suppressed because it is too large Load Diff

View File

@ -1714,3 +1714,17 @@ information about how to avoid this problem.
2025-05-31 23:21:32 - myp.app - ERROR - Fehler beim System-Gesundheitscheck: argument 1 (impossible<bad format char>)
2025-05-31 23:21:33 - myp.app - ERROR - Fehler beim Laden der Admin-Daten: 'charmap' codec can't decode byte 0x9d in position 2106: character maps to <undefined>
2025-05-31 23:21:34 - myp.app - ERROR - Fehler beim System-Gesundheitscheck: argument 1 (impossible<bad format char>)
2025-06-01 00:29:13 - myp.app - ERROR - ❌ Fehler beim Datenbank-Cleanup: (sqlite3.OperationalError) database is locked
[SQL: PRAGMA journal_mode=DELETE]
(Background on this error at: https://sqlalche.me/e/20/e3q8)
2025-06-01 00:30:54 - myp.app - ERROR - ❌ Fehler beim Datenbank-Cleanup: (sqlite3.OperationalError) database is locked
[SQL: PRAGMA journal_mode=DELETE]
(Background on this error at: https://sqlalche.me/e/20/e3q8)
2025-06-01 00:35:32 - myp.app - ERROR - ❌ Fehler beim Datenbank-Cleanup: (sqlite3.OperationalError) database is locked
[SQL: PRAGMA journal_mode=DELETE]
(Background on this error at: https://sqlalche.me/e/20/e3q8)
2025-06-01 00:35:56 - myp.app - ERROR - Fehler beim Abrufen der Live-Statistiken: argument 1 (impossible<bad format char>)
2025-06-01 00:36:06 - myp.app - ERROR - Fehler beim Abrufen der Live-Statistiken: argument 1 (impossible<bad format char>)
2025-06-01 00:36:38 - myp.app - ERROR - ❌ Fehler beim Datenbank-Cleanup: (sqlite3.OperationalError) database is locked
[SQL: PRAGMA journal_mode=DELETE]
(Background on this error at: https://sqlalche.me/e/20/e3q8)

View File

@ -2521,3 +2521,50 @@
2025-05-31 23:45:28 - myp.printers - INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-05-31 23:45:29 - myp.printers - INFO - Schnelles Laden abgeschlossen: 6 Drucker geladen (ohne Status-Check)
2025-05-31 23:45:32 - myp.printers - INFO - Schnelles Laden abgeschlossen: 6 Drucker geladen (ohne Status-Check)
2025-06-01 00:29:17 - myp.printers - INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 00:29:17 - myp.printers - INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 00:29:21 - myp.printers - INFO - Schnelles Laden abgeschlossen: 6 Drucker geladen (ohne Status-Check)
2025-06-01 00:29:21 - myp.printers - INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 00:29:21 - myp.printers - INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 00:29:21 - myp.printers - INFO - Schnelles Laden abgeschlossen: 6 Drucker geladen (ohne Status-Check)
2025-06-01 00:29:24 - myp.printers - INFO - Schnelles Laden abgeschlossen: 6 Drucker geladen (ohne Status-Check)
2025-06-01 00:30:10 - myp.printers - INFO - Schnelles Laden abgeschlossen: 6 Drucker geladen (ohne Status-Check)
2025-06-01 00:33:08 - myp.printers - INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 00:33:08 - myp.printers - INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 00:33:17 - myp.printers - INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 00:33:17 - myp.printers - INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 00:33:19 - myp.printers - INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 00:33:19 - myp.printers - INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 00:33:23 - myp.printers - INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 00:33:23 - myp.printers - INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 00:33:32 - myp.printers - INFO - Schnelles Laden abgeschlossen: 6 Drucker geladen (ohne Status-Check)
2025-06-01 00:33:32 - myp.printers - INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 00:33:32 - myp.printers - INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 00:33:32 - myp.printers - INFO - Schnelles Laden abgeschlossen: 6 Drucker geladen (ohne Status-Check)
2025-06-01 00:33:33 - myp.printers - INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 00:33:33 - myp.printers - INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 00:33:37 - myp.printers - INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 00:33:37 - myp.printers - INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 00:33:40 - myp.printers - INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 00:33:40 - myp.printers - INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 00:33:52 - myp.printers - INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 00:33:52 - myp.printers - INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 00:33:55 - myp.printers - INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 00:33:55 - myp.printers - INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 00:34:01 - myp.printers - INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 00:34:01 - myp.printers - INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 00:34:06 - myp.printers - INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 00:34:06 - myp.printers - INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 00:34:36 - myp.printers - INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 00:34:36 - myp.printers - INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 00:35:06 - myp.printers - INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 00:35:06 - myp.printers - INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 00:35:36 - myp.printers - INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 00:35:36 - myp.printers - INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 00:35:45 - myp.printers - INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 00:35:45 - myp.printers - INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 00:36:15 - myp.printers - INFO - Schnelles Laden abgeschlossen: 6 Drucker geladen (ohne Status-Check)
2025-06-01 00:36:15 - myp.printers - INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 00:36:15 - myp.printers - INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 00:36:15 - myp.printers - INFO - Schnelles Laden abgeschlossen: 6 Drucker geladen (ohne Status-Check)
2025-06-01 00:36:16 - myp.printers - INFO - Schnelles Laden abgeschlossen: 6 Drucker geladen (ohne Status-Check)

View File

@ -2717,3 +2717,27 @@
2025-06-01 00:20:28 - myp.scheduler - INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
2025-06-01 00:20:49 - myp.scheduler - INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
2025-06-01 00:21:36 - myp.scheduler - INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
2025-06-01 00:23:27 - myp.scheduler - INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
2025-06-01 00:23:28 - myp.scheduler - INFO - Scheduler-Thread gestartet
2025-06-01 00:23:28 - myp.scheduler - INFO - Scheduler gestartet
2025-06-01 00:24:52 - myp.scheduler - INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
2025-06-01 00:24:53 - myp.scheduler - INFO - Scheduler-Thread gestartet
2025-06-01 00:24:53 - myp.scheduler - INFO - Scheduler gestartet
2025-06-01 00:29:13 - myp.scheduler - INFO - Scheduler-Thread beendet
2025-06-01 00:29:13 - myp.scheduler - INFO - Scheduler gestoppt
2025-06-01 00:29:15 - myp.scheduler - INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
2025-06-01 00:29:16 - myp.scheduler - INFO - Scheduler-Thread gestartet
2025-06-01 00:29:16 - myp.scheduler - INFO - Scheduler gestartet
2025-06-01 00:30:54 - myp.scheduler - INFO - Scheduler-Thread beendet
2025-06-01 00:30:54 - myp.scheduler - INFO - Scheduler gestoppt
2025-06-01 00:30:56 - myp.scheduler - INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
2025-06-01 00:33:06 - myp.scheduler - INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
2025-06-01 00:33:07 - myp.scheduler - INFO - Scheduler-Thread gestartet
2025-06-01 00:33:07 - myp.scheduler - INFO - Scheduler gestartet
2025-06-01 00:35:32 - myp.scheduler - INFO - Scheduler-Thread beendet
2025-06-01 00:35:32 - myp.scheduler - INFO - Scheduler gestoppt
2025-06-01 00:35:34 - myp.scheduler - INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
2025-06-01 00:35:36 - myp.scheduler - INFO - Scheduler-Thread gestartet
2025-06-01 00:35:36 - myp.scheduler - INFO - Scheduler gestartet
2025-06-01 00:36:38 - myp.scheduler - INFO - Scheduler-Thread beendet
2025-06-01 00:36:38 - myp.scheduler - INFO - Scheduler gestoppt

View File

@ -0,0 +1,203 @@
{% extends "base.html" %}
{% block title %}{{ title }} - MYP Platform{% endblock %}
{% block content %}
<div class="container mx-auto px-4 py-8 max-w-4xl">
<!-- Header -->
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-8 mb-8">
<div class="flex items-center mb-6">
<div class="w-12 h-12 bg-gradient-to-r from-blue-600 to-blue-700 rounded-lg flex items-center justify-center mr-4">
<i class="fas fa-info-circle text-white text-xl"></i>
</div>
<div>
<h1 class="text-3xl font-bold text-gray-900">Impressum</h1>
<p class="text-gray-600">Rechtliche Angaben gemäß § 5 TMG</p>
</div>
</div>
<!-- Unternehmensinformationen -->
<div class="space-y-8">
<section>
<h2 class="text-xl font-semibold text-gray-900 mb-4 flex items-center">
<i class="fas fa-building text-blue-600 mr-3"></i>
Anbieter
</h2>
<div class="bg-gray-50 rounded-lg p-6">
<div class="grid md:grid-cols-2 gap-6">
<div>
<h3 class="font-semibold text-gray-900 mb-2">Unternehmen</h3>
<p class="text-gray-700">Mercedes-Benz AG</p>
<p class="text-gray-700">Ausbildungsabteilung</p>
<p class="text-gray-700">3D-Druck & Digitale Fertigung</p>
</div>
<div>
<h3 class="font-semibold text-gray-900 mb-2">Adresse</h3>
<p class="text-gray-700">Mercedes-Benz Platz 1</p>
<p class="text-gray-700">70546 Stuttgart</p>
<p class="text-gray-700">Deutschland</p>
</div>
</div>
</div>
</section>
<!-- Kontaktinformationen -->
<section>
<h2 class="text-xl font-semibold text-gray-900 mb-4 flex items-center">
<i class="fas fa-envelope text-blue-600 mr-3"></i>
Kontakt
</h2>
<div class="bg-gray-50 rounded-lg p-6">
<div class="grid md:grid-cols-2 gap-6">
<div>
<h3 class="font-semibold text-gray-900 mb-2">E-Mail</h3>
<p class="text-gray-700">
<a href="mailto:till.tomczak@mercedes-benz.com" class="text-blue-600 hover:text-blue-800">
till.tomczak@mercedes-benz.com
</a>
</p>
</div>
<div>
<h3 class="font-semibold text-gray-900 mb-2">Telefon</h3>
<p class="text-gray-700">+49 (0) 711 17-0</p>
</div>
</div>
</div>
</section>
<!-- Rechtliche Angaben -->
<section>
<h2 class="text-xl font-semibold text-gray-900 mb-4 flex items-center">
<i class="fas fa-gavel text-blue-600 mr-3"></i>
Rechtliche Angaben
</h2>
<div class="bg-gray-50 rounded-lg p-6">
<div class="grid md:grid-cols-2 gap-6">
<div>
<h3 class="font-semibold text-gray-900 mb-2">Registergericht</h3>
<p class="text-gray-700">Amtsgericht Stuttgart</p>
<p class="text-gray-700">HRB 19360</p>
</div>
<div>
<h3 class="font-semibold text-gray-900 mb-2">Umsatzsteuer-ID</h3>
<p class="text-gray-700">DE811944017</p>
</div>
</div>
</div>
</section>
<!-- Verantwortlich für den Inhalt -->
<section>
<h2 class="text-xl font-semibold text-gray-900 mb-4 flex items-center">
<i class="fas fa-user-tie text-blue-600 mr-3"></i>
Verantwortlich für den Inhalt
</h2>
<div class="bg-gray-50 rounded-lg p-6">
<p class="text-gray-700">Till Tomczak</p>
<p class="text-gray-700">Projektleiter MYP Platform</p>
<p class="text-gray-700">Mercedes-Benz AG</p>
<p class="text-gray-700">Ausbildungsabteilung</p>
</div>
</section>
<!-- Haftungsausschluss -->
<section>
<h2 class="text-xl font-semibold text-gray-900 mb-4 flex items-center">
<i class="fas fa-shield-alt text-blue-600 mr-3"></i>
Haftungsausschluss
</h2>
<div class="bg-amber-50 border border-amber-200 rounded-lg p-6">
<h3 class="font-semibold text-gray-900 mb-3">Haftung für Inhalte</h3>
<p class="text-gray-700 mb-4">
Als Diensteanbieter sind wir gemäß § 7 Abs.1 TMG für eigene Inhalte auf diesen Seiten nach den
allgemeinen Gesetzen verantwortlich. Nach §§ 8 bis 10 TMG sind wir als Diensteanbieter jedoch nicht
unter der Verpflichtung, übermittelte oder gespeicherte fremde Informationen zu überwachen oder nach
Umständen zu forschen, die auf eine rechtswidrige Tätigkeit hinweisen.
</p>
<h3 class="font-semibold text-gray-900 mb-3">Haftung für Links</h3>
<p class="text-gray-700 mb-4">
Unser Angebot enthält Links zu externen Webseiten Dritter, auf deren Inhalte wir keinen Einfluss haben.
Deshalb können wir für diese fremden Inhalte auch keine Gewähr übernehmen. Für die Inhalte der verlinkten
Seiten ist stets der jeweilige Anbieter oder Betreiber der Seiten verantwortlich.
</p>
<h3 class="font-semibold text-gray-900 mb-3">Urheberrecht</h3>
<p class="text-gray-700">
Die durch die Seitenbetreiber erstellten Inhalte und Werke auf diesen Seiten unterliegen dem deutschen
Urheberrecht. Die Vervielfältigung, Bearbeitung, Verbreitung und jede Art der Verwertung außerhalb der
Grenzen des Urheberrechtes bedürfen der schriftlichen Zustimmung des jeweiligen Autors bzw. Erstellers.
</p>
</div>
</section>
<!-- Streitschlichtung -->
<section>
<h2 class="text-xl font-semibold text-gray-900 mb-4 flex items-center">
<i class="fas fa-balance-scale text-blue-600 mr-3"></i>
Streitschlichtung
</h2>
<div class="bg-gray-50 rounded-lg p-6">
<p class="text-gray-700">
Die Europäische Kommission stellt eine Plattform zur Online-Streitbeilegung (OS) bereit:
<a href="https://ec.europa.eu/consumers/odr/" target="_blank" class="text-blue-600 hover:text-blue-800 underline">
https://ec.europa.eu/consumers/odr/
</a>
</p>
<p class="text-gray-700 mt-2">
Wir sind nicht bereit oder verpflichtet, an Streitbeilegungsverfahren vor einer
Verbraucherschlichtungsstelle teilzunehmen.
</p>
</div>
</section>
<!-- System-Information -->
<section>
<h2 class="text-xl font-semibold text-gray-900 mb-4 flex items-center">
<i class="fas fa-cogs text-blue-600 mr-3"></i>
System-Information
</h2>
<div class="bg-blue-50 border border-blue-200 rounded-lg p-6">
<div class="grid md:grid-cols-2 gap-6">
<div>
<h3 class="font-semibold text-gray-900 mb-2">MYP Platform</h3>
<p class="text-gray-700">Manage Your Printers</p>
<p class="text-gray-700">Version 2.0.0</p>
</div>
<div>
<h3 class="font-semibold text-gray-900 mb-2">Entwicklung</h3>
<p class="text-gray-700">Mercedes-Benz AG</p>
<p class="text-gray-700">Interne Projektarbeit</p>
</div>
</div>
</div>
</section>
</div>
</div>
<!-- Navigation -->
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
<div class="flex flex-wrap gap-4 justify-center">
<a href="{{ url_for('index') }}" class="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors">
<i class="fas fa-home mr-2"></i>
Zur Startseite
</a>
<a href="{{ url_for('legal') }}" class="inline-flex items-center px-4 py-2 bg-gray-600 text-white rounded-md hover:bg-gray-700 transition-colors">
<i class="fas fa-file-contract mr-2"></i>
Rechtliche Hinweise
</a>
{% if current_user.is_authenticated %}
<a href="{{ url_for('dashboard') }}" class="inline-flex items-center px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition-colors">
<i class="fas fa-chart-line mr-2"></i>
Dashboard
</a>
{% endif %}
</div>
</div>
</div>
<!-- Letzte Aktualisierung -->
<div class="text-center text-sm text-gray-500 mt-8 pb-8">
<p>Letzte Aktualisierung: {{ moment().format('DD.MM.YYYY') }}</p>
</div>
{% endblock %}

View File

@ -0,0 +1,510 @@
{% extends "base.html" %}
{% block title %}{{ title }} - MYP Platform{% endblock %}
{% block content %}
<div class="container mx-auto px-4 py-8 max-w-6xl">
<!-- Header -->
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-8 mb-8">
<div class="flex items-center mb-6">
<div class="w-12 h-12 bg-gradient-to-r from-purple-600 to-purple-700 rounded-lg flex items-center justify-center mr-4">
<i class="fas fa-file-contract text-white text-xl"></i>
</div>
<div>
<h1 class="text-3xl font-bold text-gray-900">Rechtliche Hinweise</h1>
<p class="text-gray-600">Datenschutz, Nutzungsbedingungen und weitere rechtliche Informationen</p>
</div>
</div>
<!-- Navigation Links -->
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mt-8">
<a href="#datenschutz" class="flex items-center p-4 bg-blue-50 rounded-lg hover:bg-blue-100 transition-colors">
<i class="fas fa-shield-alt text-blue-600 mr-3"></i>
<span class="font-medium text-blue-900">Datenschutz</span>
</a>
<a href="#nutzungsbedingungen" class="flex items-center p-4 bg-green-50 rounded-lg hover:bg-green-100 transition-colors">
<i class="fas fa-file-signature text-green-600 mr-3"></i>
<span class="font-medium text-green-900">AGB</span>
</a>
<a href="#cookies" class="flex items-center p-4 bg-amber-50 rounded-lg hover:bg-amber-100 transition-colors">
<i class="fas fa-cookie-bite text-amber-600 mr-3"></i>
<span class="font-medium text-amber-900">Cookies</span>
</a>
<a href="#sicherheit" class="flex items-center p-4 bg-red-50 rounded-lg hover:bg-red-100 transition-colors">
<i class="fas fa-lock text-red-600 mr-3"></i>
<span class="font-medium text-red-900">Sicherheit</span>
</a>
</div>
</div>
<!-- Datenschutzerklärung -->
<section id="datenschutz" class="bg-white rounded-lg shadow-sm border border-gray-200 p-8 mb-8">
<h2 class="text-2xl font-bold text-gray-900 mb-6 flex items-center">
<i class="fas fa-shield-alt text-blue-600 mr-3"></i>
Datenschutzerklärung
</h2>
<div class="space-y-6">
<!-- Grundsätzliches -->
<div>
<h3 class="text-lg font-semibold text-gray-900 mb-3">1. Grundsätzliches zum Datenschutz</h3>
<div class="bg-blue-50 rounded-lg p-6">
<p class="text-gray-700 mb-4">
Der Schutz Ihrer persönlichen Daten ist uns wichtig. Diese Datenschutzerklärung informiert Sie über
die Art, den Umfang und Zweck der Verarbeitung personenbezogener Daten innerhalb des MYP-Systems
(Manage Your Printers) der Mercedes-Benz AG.
</p>
<p class="text-gray-700">
Verantwortlicher im Sinne der Datenschutz-Grundverordnung (DSGVO) ist die Mercedes-Benz AG,
vertreten durch die Ausbildungsabteilung.
</p>
</div>
</div>
<!-- Datenerhebung -->
<div>
<h3 class="text-lg font-semibold text-gray-900 mb-3">2. Erhebung und Verarbeitung personenbezogener Daten</h3>
<div class="grid md:grid-cols-2 gap-6">
<div class="bg-gray-50 rounded-lg p-6">
<h4 class="font-semibold text-gray-900 mb-3">Registrierungsdaten</h4>
<ul class="text-gray-700 space-y-2">
<li>• Benutzername</li>
<li>• E-Mail-Adresse (Mercedes-Benz)</li>
<li>• Name und Abteilung</li>
<li>• Rolle im System</li>
</ul>
</div>
<div class="bg-gray-50 rounded-lg p-6">
<h4 class="font-semibold text-gray-900 mb-3">Nutzungsdaten</h4>
<ul class="text-gray-700 space-y-2">
<li>• Druckaufträge und -verlauf</li>
<li>• Login-Zeiten und -Häufigkeit</li>
<li>• IP-Adresse und Browser-Info</li>
<li>• Systemaktivitäten</li>
</ul>
</div>
</div>
</div>
<!-- Zweck der Datenverarbeitung -->
<div>
<h3 class="text-lg font-semibold text-gray-900 mb-3">3. Zweck der Datenverarbeitung</h3>
<div class="bg-green-50 rounded-lg p-6">
<div class="grid md:grid-cols-2 gap-6">
<div>
<h4 class="font-semibold text-gray-900 mb-3">Primäre Zwecke</h4>
<ul class="text-gray-700 space-y-2">
<li>• Bereitstellung der 3D-Druck-Services</li>
<li>• Verwaltung von Druckaufträgen</li>
<li>• Benutzerauthentifizierung</li>
<li>• Ressourcenplanung</li>
</ul>
</div>
<div>
<h4 class="font-semibold text-gray-900 mb-3">Sekundäre Zwecke</h4>
<ul class="text-gray-700 space-y-2">
<li>• Systemoptimierung</li>
<li>• Qualitätssicherung</li>
<li>• Ausbildungszwecke</li>
<li>• Sicherheitsüberwachung</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Rechtsgrundlage -->
<div>
<h3 class="text-lg font-semibold text-gray-900 mb-3">4. Rechtsgrundlage</h3>
<div class="bg-amber-50 border border-amber-200 rounded-lg p-6">
<p class="text-gray-700 mb-4">
Die Verarbeitung erfolgt auf Grundlage von:
</p>
<ul class="text-gray-700 space-y-2">
<li><strong>Art. 6 Abs. 1 lit. b DSGVO:</strong> Vertragserfüllung (Nutzung der Druckdienste)</li>
<li><strong>Art. 6 Abs. 1 lit. f DSGVO:</strong> Berechtigte Interessen (Systemsicherheit, Optimierung)</li>
<li><strong>Art. 6 Abs. 1 lit. c DSGVO:</strong> Rechtliche Verpflichtung (Dokumentation, Compliance)</li>
</ul>
</div>
</div>
<!-- Ihre Rechte -->
<div>
<h3 class="text-lg font-semibold text-gray-900 mb-3">5. Ihre Rechte</h3>
<div class="grid md:grid-cols-3 gap-4">
<div class="bg-blue-50 rounded-lg p-4">
<h4 class="font-semibold text-blue-900 mb-2">Auskunftsrecht</h4>
<p class="text-blue-800 text-sm">Art. 15 DSGVO</p>
</div>
<div class="bg-green-50 rounded-lg p-4">
<h4 class="font-semibold text-green-900 mb-2">Berichtigungsrecht</h4>
<p class="text-green-800 text-sm">Art. 16 DSGVO</p>
</div>
<div class="bg-red-50 rounded-lg p-4">
<h4 class="font-semibold text-red-900 mb-2">Löschungsrecht</h4>
<p class="text-red-800 text-sm">Art. 17 DSGVO</p>
</div>
<div class="bg-purple-50 rounded-lg p-4">
<h4 class="font-semibold text-purple-900 mb-2">Einschränkungsrecht</h4>
<p class="text-purple-800 text-sm">Art. 18 DSGVO</p>
</div>
<div class="bg-amber-50 rounded-lg p-4">
<h4 class="font-semibold text-amber-900 mb-2">Datenübertragbarkeit</h4>
<p class="text-amber-800 text-sm">Art. 20 DSGVO</p>
</div>
<div class="bg-gray-50 rounded-lg p-4">
<h4 class="font-semibold text-gray-900 mb-2">Widerspruchsrecht</h4>
<p class="text-gray-700 text-sm">Art. 21 DSGVO</p>
</div>
</div>
</div>
</div>
</section>
<!-- Nutzungsbedingungen -->
<section id="nutzungsbedingungen" class="bg-white rounded-lg shadow-sm border border-gray-200 p-8 mb-8">
<h2 class="text-2xl font-bold text-gray-900 mb-6 flex items-center">
<i class="fas fa-file-signature text-green-600 mr-3"></i>
Allgemeine Nutzungsbedingungen
</h2>
<div class="space-y-6">
<!-- Geltungsbereich -->
<div>
<h3 class="text-lg font-semibold text-gray-900 mb-3">1. Geltungsbereich</h3>
<div class="bg-gray-50 rounded-lg p-6">
<p class="text-gray-700">
Diese Nutzungsbedingungen gelten für die Nutzung des MYP-Systems (Manage Your Printers)
durch Mitarbeiter und Auszubildende der Mercedes-Benz AG. Mit der Registrierung und
Nutzung des Systems erkennen Sie diese Bedingungen an.
</p>
</div>
</div>
<!-- Nutzungsrechte -->
<div>
<h3 class="text-lg font-semibold text-gray-900 mb-3">2. Nutzungsrechte und -pflichten</h3>
<div class="grid md:grid-cols-2 gap-6">
<div class="bg-green-50 rounded-lg p-6">
<h4 class="font-semibold text-green-900 mb-3">Erlaubte Nutzung</h4>
<ul class="text-green-800 space-y-2">
<li>• Druckaufträge für Ausbildungszwecke</li>
<li>• Prototyping und Projektarbeit</li>
<li>• Lernmaterialien und Demonstrationen</li>
<li>• Interne Mercedes-Benz Projekte</li>
</ul>
</div>
<div class="bg-red-50 rounded-lg p-6">
<h4 class="font-semibold text-red-900 mb-3">Verbotene Nutzung</h4>
<ul class="text-red-800 space-y-2">
<li>• Kommerzielle Zwecke ohne Genehmigung</li>
<li>• Urheberrechtsverletzungen</li>
<li>• Gefährliche oder illegale Objekte</li>
<li>• Systemmanipulation oder -missbrauch</li>
</ul>
</div>
</div>
</div>
<!-- Verantwortlichkeiten -->
<div>
<h3 class="text-lg font-semibold text-gray-900 mb-3">3. Verantwortlichkeiten</h3>
<div class="bg-amber-50 border border-amber-200 rounded-lg p-6">
<div class="grid md:grid-cols-2 gap-6">
<div>
<h4 class="font-semibold text-gray-900 mb-3">Nutzer-Verantwortung</h4>
<ul class="text-gray-700 space-y-2">
<li>• Sichere Aufbewahrung der Zugangsdaten</li>
<li>• Einhaltung der Sicherheitsrichtlinien</li>
<li>• Ordnungsgemäße Nutzung der Geräte</li>
<li>• Meldung von Problemen</li>
</ul>
</div>
<div>
<h4 class="font-semibold text-gray-900 mb-3">System-Verantwortung</h4>
<ul class="text-gray-700 space-y-2">
<li>• Bereitstellung der Infrastruktur</li>
<li>• Wartung und Support</li>
<li>• Datenschutz und Sicherheit</li>
<li>• Kontinuierliche Verbesserung</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Cookie-Policy -->
<section id="cookies" class="bg-white rounded-lg shadow-sm border border-gray-200 p-8 mb-8">
<h2 class="text-2xl font-bold text-gray-900 mb-6 flex items-center">
<i class="fas fa-cookie-bite text-amber-600 mr-3"></i>
Cookie-Richtlinie
</h2>
<div class="space-y-6">
<!-- Was sind Cookies -->
<div>
<h3 class="text-lg font-semibold text-gray-900 mb-3">Was sind Cookies?</h3>
<div class="bg-amber-50 rounded-lg p-6">
<p class="text-gray-700">
Cookies sind kleine Textdateien, die beim Besuch einer Website auf Ihrem Computer gespeichert werden.
Sie helfen dabei, Ihre Präferenzen zu speichern und die Funktionalität der Website zu verbessern.
</p>
</div>
</div>
<!-- Cookie-Kategorien -->
<div>
<h3 class="text-lg font-semibold text-gray-900 mb-3">Verwendete Cookie-Kategorien</h3>
<div class="grid md:grid-cols-2 gap-6">
<div class="bg-blue-50 rounded-lg p-6">
<h4 class="font-semibold text-blue-900 mb-3">Technisch notwendige Cookies</h4>
<ul class="text-blue-800 space-y-2">
<li>• Session-Management</li>
<li>• Anmeldestatus</li>
<li>• CSRF-Schutz</li>
<li>• Spracheinstellungen</li>
</ul>
<p class="text-blue-700 text-sm mt-3">Diese Cookies sind für die Funktionalität der Website erforderlich.</p>
</div>
<div class="bg-green-50 rounded-lg p-6">
<h4 class="font-semibold text-green-900 mb-3">Funktionale Cookies</h4>
<ul class="text-green-800 space-y-2">
<li>• Benutzereinstellungen</li>
<li>• Dashboard-Konfiguration</li>
<li>• Theme-Präferenzen</li>
<li>• Accessibility-Optionen</li>
</ul>
<p class="text-green-700 text-sm mt-3">Diese Cookies verbessern die Benutzererfahrung.</p>
</div>
</div>
</div>
<!-- Cookie-Kontrolle -->
<div>
<h3 class="text-lg font-semibold text-gray-900 mb-3">Cookie-Kontrolle</h3>
<div class="bg-gray-50 rounded-lg p-6">
<p class="text-gray-700 mb-4">
Sie können Cookies in Ihren Browser-Einstellungen verwalten. Beachten Sie jedoch, dass das
Deaktivieren bestimmter Cookies die Funktionalität der Website beeinträchtigen kann.
</p>
<div class="grid md:grid-cols-3 gap-4">
<div class="bg-white rounded-lg p-4 border">
<h5 class="font-semibold text-gray-900 mb-2">Chrome</h5>
<p class="text-gray-600 text-sm">Einstellungen → Datenschutz und Sicherheit → Cookies</p>
</div>
<div class="bg-white rounded-lg p-4 border">
<h5 class="font-semibold text-gray-900 mb-2">Firefox</h5>
<p class="text-gray-600 text-sm">Einstellungen → Datenschutz & Sicherheit</p>
</div>
<div class="bg-white rounded-lg p-4 border">
<h5 class="font-semibold text-gray-900 mb-2">Edge</h5>
<p class="text-gray-600 text-sm">Einstellungen → Cookies und Websiteberechtigungen</p>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Sicherheitsrichtlinien -->
<section id="sicherheit" class="bg-white rounded-lg shadow-sm border border-gray-200 p-8 mb-8">
<h2 class="text-2xl font-bold text-gray-900 mb-6 flex items-center">
<i class="fas fa-lock text-red-600 mr-3"></i>
Sicherheitsrichtlinien
</h2>
<div class="space-y-6">
<!-- Technische Sicherheit -->
<div>
<h3 class="text-lg font-semibold text-gray-900 mb-3">Technische Sicherheitsmaßnahmen</h3>
<div class="grid md:grid-cols-2 gap-6">
<div class="bg-red-50 rounded-lg p-6">
<h4 class="font-semibold text-red-900 mb-3">Infrastruktursicherheit</h4>
<ul class="text-red-800 space-y-2">
<li>• HTTPS-Verschlüsselung</li>
<li>• Sichere Datenübertragung</li>
<li>• Regelmäßige Security-Updates</li>
<li>• Firewalls und Intrusion Detection</li>
</ul>
</div>
<div class="bg-blue-50 rounded-lg p-6">
<h4 class="font-semibold text-blue-900 mb-3">Anwendungssicherheit</h4>
<ul class="text-blue-800 space-y-2">
<li>• Sichere Authentifizierung</li>
<li>• Rollenbasierte Zugriffskontrolle</li>
<li>• Input-Validierung</li>
<li>• Session-Management</li>
</ul>
</div>
</div>
</div>
<!-- Benutzer-Sicherheit -->
<div>
<h3 class="text-lg font-semibold text-gray-900 mb-3">Empfehlungen für Benutzer</h3>
<div class="bg-green-50 rounded-lg p-6">
<div class="grid md:grid-cols-2 gap-6">
<div>
<h4 class="font-semibold text-green-900 mb-3">Passwort-Sicherheit</h4>
<ul class="text-green-800 space-y-2">
<li>• Verwenden Sie starke Passwörter</li>
<li>• Teilen Sie keine Zugangsdaten</li>
<li>• Melden Sie sich nach der Nutzung ab</li>
<li>• Verwenden Sie nicht öffentliche Computer</li>
</ul>
</div>
<div>
<h4 class="font-semibold text-green-900 mb-3">Allgemeine Sicherheit</h4>
<ul class="text-green-800 space-y-2">
<li>• Halten Sie Ihren Browser aktuell</li>
<li>• Verwenden Sie Antivirus-Software</li>
<li>• Seien Sie vorsichtig bei Downloads</li>
<li>• Melden Sie verdächtige Aktivitäten</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Incident Response -->
<div>
<h3 class="text-lg font-semibold text-gray-900 mb-3">Sicherheitsvorfälle melden</h3>
<div class="bg-amber-50 border border-amber-200 rounded-lg p-6">
<p class="text-gray-700 mb-4">
Falls Sie einen Sicherheitsvorfall bemerken oder vermuten, wenden Sie sich umgehend an:
</p>
<div class="grid md:grid-cols-2 gap-6">
<div>
<h4 class="font-semibold text-gray-900 mb-2">Technischer Support</h4>
<p class="text-gray-700">
E-Mail: <a href="mailto:till.tomczak@mercedes-benz.com" class="text-blue-600 hover:text-blue-800">
till.tomczak@mercedes-benz.com
</a>
</p>
</div>
<div>
<h4 class="font-semibold text-gray-900 mb-2">IT-Sicherheit</h4>
<p class="text-gray-700">
Interne IT-Security-Hotline
</p>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Kontakt und Weitere Informationen -->
<section class="bg-white rounded-lg shadow-sm border border-gray-200 p-8 mb-8">
<h2 class="text-2xl font-bold text-gray-900 mb-6 flex items-center">
<i class="fas fa-info-circle text-blue-600 mr-3"></i>
Weitere Informationen
</h2>
<div class="grid md:grid-cols-2 gap-8">
<!-- Kontakt -->
<div>
<h3 class="text-lg font-semibold text-gray-900 mb-4">Bei Fragen wenden Sie sich an:</h3>
<div class="space-y-4">
<div class="flex items-start">
<i class="fas fa-envelope text-blue-600 mt-1 mr-3"></i>
<div>
<p class="font-medium text-gray-900">E-Mail</p>
<a href="mailto:till.tomczak@mercedes-benz.com" class="text-blue-600 hover:text-blue-800">
till.tomczak@mercedes-benz.com
</a>
</div>
</div>
<div class="flex items-start">
<i class="fas fa-building text-blue-600 mt-1 mr-3"></i>
<div>
<p class="font-medium text-gray-900">Abteilung</p>
<p class="text-gray-700">Ausbildungsabteilung - 3D-Druck</p>
</div>
</div>
</div>
</div>
<!-- Updates -->
<div>
<h3 class="text-lg font-semibold text-gray-900 mb-4">Aktualisierungen</h3>
<div class="bg-gray-50 rounded-lg p-4">
<p class="text-gray-700 mb-2">
Diese rechtlichen Hinweise können bei Bedarf aktualisiert werden.
Über wesentliche Änderungen werden Sie informiert.
</p>
<p class="text-sm text-gray-600">
Stand: {{ moment().format('DD.MM.YYYY') }}
</p>
</div>
</div>
</div>
</section>
<!-- Navigation -->
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
<div class="flex flex-wrap gap-4 justify-center">
<a href="{{ url_for('index') }}" class="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors">
<i class="fas fa-home mr-2"></i>
Zur Startseite
</a>
<a href="{{ url_for('imprint') }}" class="inline-flex items-center px-4 py-2 bg-gray-600 text-white rounded-md hover:bg-gray-700 transition-colors">
<i class="fas fa-info-circle mr-2"></i>
Impressum
</a>
{% if current_user.is_authenticated %}
<a href="{{ url_for('dashboard') }}" class="inline-flex items-center px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition-colors">
<i class="fas fa-chart-line mr-2"></i>
Dashboard
</a>
<a href="{{ url_for('user_settings') }}" class="inline-flex items-center px-4 py-2 bg-purple-600 text-white rounded-md hover:bg-purple-700 transition-colors">
<i class="fas fa-cog mr-2"></i>
Einstellungen
</a>
{% endif %}
</div>
</div>
</div>
<!-- Scroll-to-Top Button -->
<button id="scrollToTop" class="fixed bottom-6 right-6 bg-blue-600 text-white p-3 rounded-full shadow-lg hover:bg-blue-700 transition-all duration-300 opacity-0 pointer-events-none">
<i class="fas fa-chevron-up"></i>
</button>
<script>
// Smooth scrolling for anchor links
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
});
// Scroll-to-top functionality
const scrollToTopBtn = document.getElementById('scrollToTop');
window.addEventListener('scroll', () => {
if (window.pageYOffset > 300) {
scrollToTopBtn.classList.remove('opacity-0', 'pointer-events-none');
scrollToTopBtn.classList.add('opacity-100');
} else {
scrollToTopBtn.classList.add('opacity-0', 'pointer-events-none');
scrollToTopBtn.classList.remove('opacity-100');
}
});
scrollToTopBtn.addEventListener('click', () => {
window.scrollTo({
top: 0,
behavior: 'smooth'
});
});
</script>
{% endblock %}