Newwebshop/templates/admin/security/dashboard.html.twig

536 lines
26 KiB
Twig

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= $title ?> - Webshop Admin</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/chart.js@4.0.0/dist/chart.min.css" rel="stylesheet">
<style>
.security-score {
font-size: 3rem;
font-weight: bold;
}
.score-excellent { color: #28a745; }
.score-good { color: #17a2b8; }
.score-warning { color: #ffc107; }
.score-danger { color: #dc3545; }
.security-card {
border-left: 4px solid #dee2e6;
transition: all 0.3s ease;
}
.security-card.secure { border-left-color: #28a745; }
.security-card.warning { border-left-color: #ffc107; }
.security-card.danger { border-left-color: #dc3545; }
.status-indicator {
width: 12px;
height: 12px;
border-radius: 50%;
display: inline-block;
margin-right: 0.5rem;
}
.status-secure { background-color: #28a745; }
.status-warning { background-color: #ffc107; }
.status-danger { background-color: #dc3545; }
.event-log {
max-height: 400px;
overflow-y: auto;
}
.event-item {
padding: 0.75rem;
border-bottom: 1px solid #dee2e6;
transition: background-color 0.2s;
}
.event-item:hover {
background-color: #f8f9fa;
}
.event-level-error { border-left: 4px solid #dc3545; }
.event-level-warning { border-left: 4px solid #ffc107; }
.event-level-info { border-left: 4px solid #17a2b8; }
.metric-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 0.5rem;
padding: 1.5rem;
margin-bottom: 1rem;
}
.metric-value {
font-size: 2rem;
font-weight: bold;
margin-bottom: 0.5rem;
}
.metric-label {
font-size: 0.875rem;
opacity: 0.9;
}
.chart-container {
position: relative;
height: 300px;
margin-bottom: 2rem;
}
.quick-action {
background: white;
border: 1px solid #dee2e6;
border-radius: 0.5rem;
padding: 1rem;
text-align: center;
transition: all 0.3s ease;
cursor: pointer;
}
.quick-action:hover {
border-color: #667eea;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.quick-action i {
font-size: 2rem;
color: #667eea;
margin-bottom: 0.5rem;
}
</style>
</head>
<body>
<div class="container-fluid">
<div class="row">
<!-- Sidebar -->
<nav class="col-md-3 col-lg-2 d-md-block bg-light sidebar">
<div class="position-sticky pt-3">
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link active" href="/admin/security/dashboard">
<i class="fas fa-shield-alt me-2"></i>Dashboard
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/admin/security/settings">
<i class="fas fa-cog me-2"></i>Einstellungen
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/admin/security/backup">
<i class="fas fa-database me-2"></i>Backup
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/admin/security/logs">
<i class="fas fa-list me-2"></i>Logs
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/admin/security/rate_limiting">
<i class="fas fa-tachometer-alt me-2"></i>Rate Limiting
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/admin/security/ssl">
<i class="fas fa-lock me-2"></i>SSL/TLS
</a>
</li>
</ul>
</div>
</nav>
<!-- Main content -->
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">
<i class="fas fa-shield-alt me-2"></i>Sicherheits-Dashboard
</h1>
<div class="btn-toolbar mb-2 mb-md-0">
<div class="btn-group me-2">
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="refreshDashboard()">
<i class="fas fa-sync-alt me-1"></i>Aktualisieren
</button>
<button type="button" class="btn btn-sm btn-outline-primary" onclick="createBackup()">
<i class="fas fa-database me-1"></i>Backup erstellen
</button>
</div>
</div>
</div>
<!-- Security Score -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-body text-center">
<h5 class="card-title">Sicherheits-Score</h5>
<div class="security-score score-<?= $security_status.overall_score >= 90 ? 'excellent' : ($security_status.overall_score >= 70 ? 'good' : ($security_status.overall_score >= 50 ? 'warning' : 'danger')) ?>">
<?= $security_status.overall_score ?>%
</div>
<p class="text-muted">
<?php if ($security_status.overall_score >= 90): ?>
<i class="fas fa-check-circle text-success me-1"></i>Ausgezeichnete Sicherheit
<?php elseif ($security_status.overall_score >= 70): ?>
<i class="fas fa-check text-info me-1"></i>Gute Sicherheit
<?php elseif ($security_status.overall_score >= 50): ?>
<i class="fas fa-exclamation-triangle text-warning me-1"></i>Verbesserungen empfohlen
<?php else: ?>
<i class="fas fa-times-circle text-danger me-1"></i>Kritische Sicherheitsprobleme
<?php endif; ?>
</p>
</div>
</div>
</div>
</div>
<!-- Security Metrics -->
<div class="row mb-4">
<div class="col-md-3">
<div class="metric-card">
<div class="metric-value">
<i class="fas fa-shield-alt me-2"></i><?= $backup_status.backup_count ?>
</div>
<div class="metric-label">Backups verfügbar</div>
</div>
</div>
<div class="col-md-3">
<div class="metric-card">
<div class="metric-value">
<i class="fas fa-clock me-2"></i><?= $backup_status.backup_age_hours ?? 0 ?>
</div>
<div class="metric-label">Stunden seit letztem Backup</div>
</div>
</div>
<div class="col-md-3">
<div class="metric-card">
<div class="metric-value">
<i class="fas fa-exclamation-triangle me-2"></i><?= count(array_filter($recent_events, fn($e) => $e.level === 'error')) ?>
</div>
<div class="metric-label">Kritische Events (24h)</div>
</div>
</div>
<div class="col-md-3">
<div class="metric-card">
<div class="metric-value">
<i class="fas fa-ban me-2"></i><?= array_sum(array_column($rate_limit_stats, 'recent_attempts')) ?>
</div>
<div class="metric-label">Rate Limit Verstöße (1h)</div>
</div>
</div>
</div>
<!-- Security Status -->
<div class="row mb-4">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-lock me-2"></i>SSL/TLS Status
</h5>
</div>
<div class="card-body">
<div class="security-card <?= $security_status.ssl_enabled ? 'secure' : 'danger' ?> p-3 mb-3">
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="status-indicator status-<?= $security_status.ssl_enabled ? 'secure' : 'danger' ?>"></span>
<strong>HTTPS</strong>
</div>
<span class="badge bg-<?= $security_status.ssl_enabled ? 'success' : 'danger' ?>">
<?= $security_status.ssl_enabled ? 'Aktiv' : 'Inaktiv' ?>
</span>
</div>
</div>
<?php foreach ($security_status.security_headers as $header => $enabled): ?>
<div class="security-card <?= $enabled ? 'secure' : 'warning' ?> p-3 mb-2">
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="status-indicator status-<?= $enabled ? 'secure' : 'warning' ?>"></span>
<strong><?= $header ?></strong>
</div>
<span class="badge bg-<?= $enabled ? 'success' : 'warning' ?>">
<?= $enabled ? 'Aktiv' : 'Inaktiv' ?>
</span>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-database me-2"></i>Datenbank-Sicherheit
</h5>
</div>
<div class="card-body">
<div class="security-card <?= $database_security.secure ? 'secure' : 'danger' ?> p-3 mb-3">
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="status-indicator status-<?= $database_security.secure ? 'secure' : 'danger' ?>"></span>
<strong>Admin-Benutzer</strong>
</div>
<span class="badge bg-<?= $database_security.secure ? 'success' : 'danger' ?>">
<?= $database_security.admin_users ?> Benutzer
</span>
</div>
</div>
<div class="security-card <?= $database_security.recent_failed_logins < 50 ? 'secure' : 'warning' ?> p-3 mb-2">
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="status-indicator status-<?= $database_security.recent_failed_logins < 50 ? 'secure' : 'warning' ?>"></span>
<strong>Fehlgeschlagene Logins (1h)</strong>
</div>
<span class="badge bg-<?= $database_security.recent_failed_logins < 50 ? 'success' : 'warning' ?>">
<?= $database_security.recent_failed_logins ?>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Recent Events & Quick Actions -->
<div class="row">
<div class="col-md-8">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-list me-2"></i>Letzte Sicherheits-Events
</h5>
</div>
<div class="card-body">
<div class="event-log">
<?php if (empty($recent_events)): ?>
<div class="text-center text-muted py-4">
<i class="fas fa-check-circle fa-3x mb-3"></i>
<p>Keine kritischen Events in den letzten 24 Stunden</p>
</div>
<?php else: ?>
<?php foreach ($recent_events as $event): ?>
<div class="event-item event-level-<?= $event.level ?>">
<div class="d-flex justify-content-between align-items-start">
<div>
<strong><?= htmlspecialchars($event.event) ?></strong>
<br>
<small class="text-muted">
<i class="fas fa-globe me-1"></i><?= htmlspecialchars($event.ip_address) ?>
<span class="mx-2">•</span>
<i class="fas fa-clock me-1"></i><?= date('d.m.Y H:i', strtotime($event.created_at)) ?>
</small>
</div>
<span class="badge bg-<?= $event.level === 'error' ? 'danger' : ($event.level === 'warning' ? 'warning' : 'info') ?>">
<?= strtoupper($event.level) ?>
</span>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-bolt me-2"></i>Schnellaktionen
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-6 mb-3">
<div class="quick-action" onclick="createBackup()">
<i class="fas fa-database"></i>
<div><strong>Backup</strong></div>
<small class="text-muted">Erstellen</small>
</div>
</div>
<div class="col-6 mb-3">
<div class="quick-action" onclick="clearLogs()">
<i class="fas fa-trash"></i>
<div><strong>Logs</strong></div>
<small class="text-muted">Bereinigen</small>
</div>
</div>
<div class="col-6 mb-3">
<div class="quick-action" onclick="testSSL()">
<i class="fas fa-lock"></i>
<div><strong>SSL</strong></div>
<small class="text-muted">Testen</small>
</div>
</div>
<div class="col-6 mb-3">
<div class="quick-action" onclick="securityScan()">
<i class="fas fa-search"></i>
<div><strong>Scan</strong></div>
<small class="text-muted">Durchführen</small>
</div>
</div>
</div>
</div>
</div>
<!-- Backup Status -->
<div class="card mt-3">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-database me-2"></i>Backup Status
</h5>
</div>
<div class="card-body">
<div class="d-flex justify-content-between align-items-center mb-2">
<span>Letztes Backup:</span>
<span class="text-muted">
<?= $backup_status.last_backup ? date('d.m.Y H:i', strtotime($backup_status.last_backup)) : 'Nie' ?>
</span>
</div>
<div class="d-flex justify-content-between align-items-center mb-2">
<span>Backup-Alter:</span>
<span class="text-<?= ($backup_status.backup_age_hours ?? 999) > 24 ? 'danger' : 'success' ?>">
<?= $backup_status.backup_age_hours ?? 0 ?> Stunden
</span>
</div>
<div class="d-flex justify-content-between align-items-center">
<span>Backup benötigt:</span>
<span class="badge bg-<?= $backup_status.backup_needed ? 'danger' : 'success' ?>">
<?= $backup_status.backup_needed ? 'Ja' : 'Nein' ?>
</span>
</div>
<?php if ($backup_status.backup_needed): ?>
<div class="mt-3">
<button class="btn btn-warning btn-sm w-100" onclick="createBackup()">
<i class="fas fa-exclamation-triangle me-1"></i>Backup erstellen
</button>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
</main>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.0.0/dist/chart.min.js"></script>
<script>
// Dashboard Funktionen
function refreshDashboard() {
location.reload();
}
function createBackup() {
if (confirm('Möchten Sie ein neues Backup erstellen?')) {
fetch('/admin/security/backup?action=create', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': getCSRFToken()
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('Backup erfolgreich erstellt!');
location.reload();
} else {
alert('Fehler beim Erstellen des Backups: ' + data.error);
}
})
.catch(error => {
console.error('Error:', error);
alert('Fehler beim Erstellen des Backups');
});
}
}
function clearLogs() {
if (confirm('Möchten Sie alle Sicherheits-Logs löschen?')) {
fetch('/admin/security/logs/clear', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': getCSRFToken()
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('Logs erfolgreich bereinigt!');
location.reload();
} else {
alert('Fehler beim Bereinigen der Logs');
}
});
}
}
function testSSL() {
fetch('/admin/security/ssl/test', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': getCSRFToken()
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('SSL-Test erfolgreich: ' + data.message);
} else {
alert('SSL-Test fehlgeschlagen: ' + data.error);
}
});
}
function securityScan() {
if (confirm('Möchten Sie einen vollständigen Sicherheits-Scan durchführen?')) {
fetch('/admin/security/scan', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': getCSRFToken()
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('Sicherheits-Scan abgeschlossen!\n\nGefundene Probleme: ' + data.issues.length);
location.reload();
} else {
alert('Sicherheits-Scan fehlgeschlagen: ' + data.error);
}
});
}
}
function getCSRFToken() {
return document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '';
}
// Auto-Refresh alle 5 Minuten
setInterval(() => {
// Nur wenn Dashboard aktiv ist
if (!document.hidden) {
fetch('/admin/security/dashboard/status')
.then(response => response.json())
.then(data => {
// Update nur wenn sich etwas geändert hat
if (data.needs_refresh) {
location.reload();
}
});
}
}, 300000); // 5 Minuten
</script>
</body>
</html>