/**
* Mercedes-Benz MYP Admin Dashboard - Unified JavaScript
* Konsolidierte Admin-Funktionalitäten ohne Event-Handler-Konflikte
*/
// Globale Variablen und State-Management
class AdminDashboard {
constructor() {
this.csrfToken = null;
this.updateInterval = null;
this.eventListenersAttached = false;
this.apiBaseUrl = this.detectApiBaseUrl();
this.retryCount = 0;
this.maxRetries = 3;
this.isInitialized = false;
this.init();
}
detectApiBaseUrl() {
const currentHost = window.location.hostname;
const currentPort = window.location.port;
if (currentPort === '5000') {
return '';
}
return `http://${currentHost}:5000`;
}
init() {
if (this.isInitialized) {
console.log('🔄 Admin Dashboard bereits initialisiert, überspringe...');
return;
}
console.log('🚀 Initialisiere Mercedes-Benz MYP Admin Dashboard');
// CSRF Token mit verbesserter Extraktion
this.csrfToken = this.extractCSRFToken();
console.log('🔒 CSRF Token:', this.csrfToken ? 'verfügbar' : 'FEHLT!');
// Event-Listener nur einmal registrieren
this.attachEventListeners();
// Live-Updates starten
this.startLiveUpdates();
// Initiale Daten laden
this.loadInitialData();
this.isInitialized = true;
console.log('✅ Admin Dashboard erfolgreich initialisiert');
}
extractCSRFToken() {
// Methode 1: Meta Tag
const metaToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
if (metaToken) {
console.log('🔒 CSRF Token aus meta Tag geladen');
return metaToken;
}
// Methode 2: Hidden Input
const hiddenInput = document.querySelector('input[name="csrf_token"]')?.value;
if (hiddenInput) {
console.log('🔒 CSRF Token aus hidden input geladen');
return hiddenInput;
}
// Methode 3: Cookie (falls verfügbar)
const cookieToken = document.cookie.split('; ').find(row => row.startsWith('csrf_token='))?.split('=')[1];
if (cookieToken) {
console.log('🔒 CSRF Token aus Cookie geladen');
return cookieToken;
}
// Methode 4: Flask-WTF Standard
const flaskToken = document.querySelector('meta[name="csrf-token"]')?.content;
if (flaskToken) {
console.log('🔒 CSRF Token aus Flask-WTF Meta geladen');
return flaskToken;
}
console.error('❌ CSRF Token konnte nicht gefunden werden!');
return null;
}
attachEventListeners() {
if (this.eventListenersAttached) {
console.log('⚠️ Event-Listener bereits registriert, überspringe...');
return;
}
// System-Action-Buttons mit Event-Delegation
this.attachSystemButtons();
this.attachUserManagement();
this.attachPrinterManagement();
this.attachJobManagement();
this.attachModalEvents();
this.eventListenersAttached = true;
console.log('📌 Event-Listener erfolgreich registriert');
}
attachSystemButtons() {
// System Status Button
this.addEventListenerSafe('#system-status-btn', 'click', (e) => {
e.preventDefault();
e.stopPropagation();
this.showSystemStatus();
});
// Analytics Button
this.addEventListenerSafe('#analytics-btn', 'click', (e) => {
e.preventDefault();
e.stopPropagation();
this.showAnalytics();
});
// Maintenance Button - DEAKTIVIERT wegen Konflikt mit MaintenanceModal
// Das Wartungs-Modal wird jetzt direkt in admin.html verwaltet
// this.addEventListenerSafe('#maintenance-btn', 'click', (e) => {
// e.preventDefault();
// e.stopPropagation();
// this.showMaintenance();
// });
// Cache leeren
this.addEventListenerSafe('#clear-cache-btn', 'click', (e) => {
e.preventDefault();
e.stopPropagation();
this.clearSystemCache();
});
// Datenbank optimieren
this.addEventListenerSafe('#optimize-db-btn', 'click', (e) => {
e.preventDefault();
e.stopPropagation();
this.optimizeDatabase();
});
// Backup erstellen
this.addEventListenerSafe('#create-backup-btn', 'click', (e) => {
e.preventDefault();
e.stopPropagation();
this.createSystemBackup();
});
// Drucker-Initialisierung erzwingen
this.addEventListenerSafe('#force-init-printers-btn', 'click', (e) => {
e.preventDefault();
e.stopPropagation();
this.forceInitializePrinters();
});
}
attachUserManagement() {
// Neuer Benutzer Button
this.addEventListenerSafe('#add-user-btn', 'click', (e) => {
e.preventDefault();
e.stopPropagation();
this.showUserModal();
});
// Event-Delegation für Benutzer-Aktionen
document.addEventListener('click', (e) => {
if (e.target.closest('.edit-user-btn')) {
e.preventDefault();
e.stopPropagation();
const userId = e.target.closest('button').dataset.userId;
this.editUser(userId);
}
if (e.target.closest('.delete-user-btn')) {
e.preventDefault();
e.stopPropagation();
const userId = e.target.closest('button').dataset.userId;
const userName = e.target.closest('button').dataset.userName;
this.deleteUser(userId, userName);
}
});
}
attachPrinterManagement() {
// Drucker hinzufügen Button
this.addEventListenerSafe('#add-printer-btn', 'click', (e) => {
e.preventDefault();
e.stopPropagation();
this.showPrinterModal();
});
// Event-Delegation für Drucker-Aktionen
document.addEventListener('click', (e) => {
if (e.target.closest('.manage-printer-btn')) {
e.preventDefault();
e.stopPropagation();
const printerId = e.target.closest('button').dataset.printerId;
this.managePrinter(printerId);
}
if (e.target.closest('.settings-printer-btn')) {
e.preventDefault();
e.stopPropagation();
const printerId = e.target.closest('button').dataset.printerId;
this.showPrinterSettings(printerId);
}
// Smart-Plug Ein/Aus Toggle für Drucker
if (e.target.closest('.toggle-printer-power-btn')) {
e.preventDefault();
e.stopPropagation();
const button = e.target.closest('button');
const printerId = button.dataset.printerId;
const printerName = button.dataset.printerName;
this.togglePrinterPower(printerId, printerName, button);
}
});
}
attachJobManagement() {
// Event-Delegation für Job-Aktionen
document.addEventListener('click', (e) => {
if (e.target.closest('.job-action-btn')) {
e.preventDefault();
e.stopPropagation();
const action = e.target.closest('button').dataset.action;
const jobId = e.target.closest('button').dataset.jobId;
this.handleJobAction(action, jobId);
}
});
}
attachModalEvents() {
// Error-Alert Buttons
this.addEventListenerSafe('#fix-errors-btn', 'click', (e) => {
e.preventDefault();
e.stopPropagation();
this.fixErrors();
});
this.addEventListenerSafe('#dismiss-errors-btn', 'click', (e) => {
e.preventDefault();
e.stopPropagation();
this.dismissErrors();
});
this.addEventListenerSafe('#view-error-details-btn', 'click', (e) => {
e.preventDefault();
e.stopPropagation();
window.location.href = '/admin-dashboard?tab=logs';
});
// Logs-Funktionalität
this.addEventListenerSafe('#refresh-logs-btn', 'click', (e) => {
e.preventDefault();
e.stopPropagation();
this.loadLogs();
});
this.addEventListenerSafe('#export-logs-btn', 'click', (e) => {
e.preventDefault();
e.stopPropagation();
this.exportLogs();
});
this.addEventListenerSafe('#log-level-filter', 'change', (e) => {
this.loadLogs();
});
}
addEventListenerSafe(selector, event, handler) {
const element = document.querySelector(selector);
if (element && !element.dataset.listenerAttached) {
element.addEventListener(event, handler);
element.dataset.listenerAttached = 'true';
}
}
startLiveUpdates() {
if (this.updateInterval) {
clearInterval(this.updateInterval);
}
// Statistiken alle 30 Sekunden aktualisieren
this.updateInterval = setInterval(() => {
this.loadLiveStats();
}, 30000);
// Live-Zeit jede Sekunde aktualisieren
setInterval(() => {
this.updateLiveTime();
}, 1000);
// System-Health alle 30 Sekunden prüfen
setInterval(() => {
this.checkSystemHealth();
}, 30000);
console.log('🔄 Live-Updates gestartet');
}
async loadInitialData() {
await this.loadLiveStats();
await this.checkSystemHealth();
// Button-Test ausführen
setTimeout(() => {
this.testButtons();
}, 1000);
// Logs laden falls wir auf dem Logs-Tab sind
if (window.location.search.includes('tab=logs') || document.querySelector('.tabs [href*="logs"]')?.classList.contains('active')) {
await this.loadLogs();
}
// Prüfe auch ob der Logs-Tab durch die URL-Parameter aktiv ist
const urlParams = new URLSearchParams(window.location.search);
const activeTab = urlParams.get('tab');
if (activeTab === 'logs') {
await this.loadLogs();
}
// Oder prüfe ob das Logs-Container-Element sichtbar ist
const logsContainer = document.getElementById('logs-container');
if (logsContainer && logsContainer.offsetParent !== null) {
await this.loadLogs();
}
}
async loadLiveStats() {
try {
const url = `${this.apiBaseUrl}/api/stats`;
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
this.updateStatsDisplay(data);
this.retryCount = 0;
} catch (error) {
console.error('Fehler beim Laden der Live-Statistiken:', error);
this.retryCount++;
if (this.retryCount <= this.maxRetries) {
setTimeout(() => this.loadLiveStats(), 5000);
}
}
}
updateStatsDisplay(data) {
// Sichere Updates mit null-Checks
this.updateElement('live-users-count', data.total_users || 0);
this.updateElement('live-printers-count', data.total_printers || 0);
this.updateElement('live-printers-online', `${data.online_printers || 0} online`);
this.updateElement('live-jobs-active', data.active_jobs || 0);
this.updateElement('live-jobs-queued', `${data.queued_jobs || 0} in Warteschlange`);
this.updateElement('live-success-rate', `${data.success_rate || 0}%`);
// Progress Bars aktualisieren
this.updateProgressBar('users-progress', data.total_users, 20);
this.updateProgressBar('printers-progress', data.online_printers, data.total_printers);
this.updateProgressBar('jobs-progress', data.active_jobs, 10);
this.updateProgressBar('success-progress', data.success_rate, 100);
console.log('📊 Live-Statistiken aktualisiert');
}
updateElement(elementId, value) {
const element = document.getElementById(elementId);
if (element) {
element.textContent = value;
}
}
updateProgressBar(progressId, currentValue, maxValue) {
const progressEl = document.getElementById(progressId);
if (progressEl && currentValue !== undefined && maxValue > 0) {
const percentage = Math.min(100, Math.max(0, (currentValue / maxValue) * 100));
progressEl.style.width = `${percentage}%`;
}
}
updateLiveTime() {
const timeElement = document.getElementById('live-time');
if (timeElement) {
const now = new Date();
timeElement.textContent = now.toLocaleTimeString('de-DE');
}
}
// System-Funktionen
async showSystemStatus() {
console.log('🔧 System Status wird angezeigt');
this.showNotification('System Status wird geladen...', 'info');
}
async showAnalytics() {
console.log('📈 Analytics wird angezeigt');
this.showNotification('Analytics werden geladen...', 'info');
}
async showMaintenance() {
console.log('🛠️ Wartung wird angezeigt');
const systemTab = document.querySelector('a[href*="tab=system"]');
if (systemTab) {
systemTab.click();
}
}
async clearSystemCache() {
if (!confirm('🗑️ Möchten Sie wirklich den System-Cache leeren?')) return;
try {
const response = await fetch(`${this.apiBaseUrl}/api/admin/cache/clear`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': this.csrfToken
}
});
const data = await response.json();
if (data.success) {
this.showNotification('✅ Cache erfolgreich geleert!', 'success');
setTimeout(() => window.location.reload(), 2000);
} else {
this.showNotification('❌ Fehler beim Leeren des Cache', 'error');
}
} catch (error) {
this.showNotification('❌ Fehler beim Leeren des Cache', 'error');
}
}
async optimizeDatabase() {
if (!confirm('🔧 Möchten Sie die Datenbank optimieren?')) return;
this.showNotification('🔄 Datenbank wird optimiert...', 'info');
try {
const response = await fetch(`${this.apiBaseUrl}/api/admin/database/optimize`, {
method: 'POST',
headers: { 'X-CSRFToken': this.csrfToken }
});
const data = await response.json();
if (data.success) {
this.showNotification('✅ Datenbank erfolgreich optimiert!', 'success');
} else {
this.showNotification('❌ Fehler bei der Datenbank-Optimierung', 'error');
}
} catch (error) {
this.showNotification('❌ Fehler bei der Datenbank-Optimierung', 'error');
}
}
async createSystemBackup() {
if (!confirm('💾 Möchten Sie ein System-Backup erstellen?')) return;
this.showNotification('🔄 Backup wird erstellt...', 'info');
try {
const response = await fetch(`${this.apiBaseUrl}/api/admin/backup/create`, {
method: 'POST',
headers: { 'X-CSRFToken': this.csrfToken }
});
const data = await response.json();
if (data.success) {
this.showNotification('✅ Backup erfolgreich erstellt!', 'success');
} else {
this.showNotification('❌ Fehler beim Erstellen des Backups', 'error');
}
} catch (error) {
this.showNotification('❌ Fehler beim Erstellen des Backups', 'error');
}
}
async forceInitializePrinters() {
if (!confirm('🔄 Möchten Sie die Drucker-Initialisierung erzwingen?')) return;
this.showNotification('🔄 Drucker werden initialisiert...', 'info');
try {
const response = await fetch(`${this.apiBaseUrl}/api/admin/printers/force-init`, {
method: 'POST',
headers: { 'X-CSRFToken': this.csrfToken }
});
const data = await response.json();
if (data.success) {
this.showNotification('✅ Drucker erfolgreich initialisiert!', 'success');
setTimeout(() => window.location.reload(), 2000);
} else {
this.showNotification('❌ Fehler bei der Drucker-Initialisierung', 'error');
}
} catch (error) {
this.showNotification('❌ Fehler bei der Drucker-Initialisierung', 'error');
}
}
// User-Management
showUserModal(userId = null) {
const isEdit = userId !== null;
const title = isEdit ? 'Benutzer bearbeiten' : 'Neuer Benutzer';
// Modal HTML erstellen
const modalHtml = `
`;
// Modal zum DOM hinzufügen
document.body.insertAdjacentHTML('beforeend', modalHtml);
// Event-Listener für das Formular
const form = document.getElementById('user-form');
form.addEventListener('submit', (e) => {
e.preventDefault();
e.stopPropagation();
if (isEdit) {
this.updateUser(userId, new FormData(form));
} else {
this.createUser(new FormData(form));
}
});
// Bei Bearbeitung: Benutzer-Daten laden
if (isEdit) {
this.loadUserData(userId);
}
// Fokus auf erstes Eingabefeld
setTimeout(() => {
document.getElementById('user-email').focus();
}, 100);
}
async loadUserData(userId) {
try {
const response = await fetch(`${this.apiBaseUrl}/api/admin/users/${userId}`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
if (data.success) {
const user = data.user;
// Formular mit Benutzerdaten füllen
document.getElementById('user-email').value = user.email || '';
document.getElementById('user-username').value = user.username || '';
document.getElementById('user-name').value = user.name || '';
document.getElementById('user-role').value = user.is_admin ? 'admin' : 'user';
const activeCheckbox = document.getElementById('user-active');
if (activeCheckbox) {
activeCheckbox.checked = user.is_active !== false;
}
} else {
this.showNotification('❌ Fehler beim Laden der Benutzerdaten', 'error');
}
} catch (error) {
console.error('Fehler beim Laden der Benutzerdaten:', error);
this.showNotification('❌ Fehler beim Laden der Benutzerdaten', 'error');
}
}
async createUser(formData) {
const submitBtn = document.getElementById('user-submit-btn');
const originalText = submitBtn.innerHTML;
try {
// Loading-Zustand
submitBtn.innerHTML = '';
submitBtn.disabled = true;
// FormData zu JSON konvertieren
const userData = {
email: formData.get('email'),
username: formData.get('username') || formData.get('email').split('@')[0],
name: formData.get('name'),
password: formData.get('password'),
is_admin: formData.get('role') === 'admin'
};
const response = await fetch(`${this.apiBaseUrl}/api/admin/users`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': this.csrfToken
},
body: JSON.stringify(userData)
});
const data = await response.json();
if (data.success) {
this.showNotification('✅ Benutzer erfolgreich erstellt!', 'success');
document.getElementById('user-modal').remove();
// Seite nach 1 Sekunde neu laden um neue Benutzerliste zu zeigen
setTimeout(() => {
window.location.reload();
}, 1000);
} else {
this.showNotification(`❌ Fehler: ${data && data.error ? data.error : 'Unbekannter Fehler'}`, 'error');
}
} catch (error) {
console.error('Fehler beim Erstellen des Benutzers:', error);
this.showNotification('❌ Fehler beim Erstellen des Benutzers', 'error');
} finally {
// Button zurücksetzen
submitBtn.innerHTML = originalText;
submitBtn.disabled = false;
}
}
async updateUser(userId, formData) {
const submitBtn = document.getElementById('user-submit-btn');
const originalText = submitBtn.innerHTML;
try {
// Loading-Zustand
submitBtn.innerHTML = '';
submitBtn.disabled = true;
// FormData zu JSON konvertieren
const userData = {
email: formData.get('email'),
username: formData.get('username'),
name: formData.get('name'),
is_admin: formData.get('role') === 'admin',
is_active: formData.get('is_active') === 'on'
};
// Passwort nur hinzufügen wenn es gesetzt wurde
const password = formData.get('password');
if (password && password.trim()) {
userData.password = password;
}
const response = await fetch(`${this.apiBaseUrl}/api/admin/users/${userId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': this.csrfToken
},
body: JSON.stringify(userData)
});
const data = await response.json();
if (data.success) {
this.showNotification('✅ Benutzer erfolgreich aktualisiert!', 'success');
document.getElementById('user-modal').remove();
// Seite nach 1 Sekunde neu laden um aktualisierte Benutzerliste zu zeigen
setTimeout(() => {
window.location.reload();
}, 1000);
} else {
this.showNotification(`❌ Fehler: ${data && data.error ? data.error : 'Unbekannter Fehler'}`, 'error');
}
} catch (error) {
console.error('Fehler beim Aktualisieren des Benutzers:', error);
this.showNotification('❌ Fehler beim Aktualisieren des Benutzers', 'error');
} finally {
// Button zurücksetzen
submitBtn.innerHTML = originalText;
submitBtn.disabled = false;
}
}
editUser(userId) {
console.log(`✏️ Benutzer ${userId} wird bearbeitet`);
this.showUserModal(userId);
}
async deleteUser(userId, userName) {
if (!confirm(`🗑️ Möchten Sie den Benutzer "${userName}" wirklich löschen?\n\nDiese Aktion kann nicht rückgängig gemacht werden!`)) {
return;
}
try {
this.showNotification(`🔄 Benutzer "${userName}" wird gelöscht...`, 'info');
const response = await fetch(`${this.apiBaseUrl}/api/admin/users/${userId}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': this.csrfToken
}
});
const data = await response.json();
if (data.success) {
this.showNotification(`✅ Benutzer "${userName}" erfolgreich gelöscht!`, 'success');
// Seite nach 1 Sekunde neu laden um aktualisierte Benutzerliste zu zeigen
setTimeout(() => {
window.location.reload();
}, 1000);
} else {
this.showNotification(`❌ Fehler beim Löschen: ${data && data.error ? data.error : 'Unbekannter Fehler'}`, 'error');
}
} catch (error) {
console.error('Fehler beim Löschen des Benutzers:', error);
this.showNotification('❌ Fehler beim Löschen des Benutzers', 'error');
}
}
// Printer-Management
showPrinterModal() {
console.log('🖨️ Drucker-Modal wird angezeigt');
this.showNotification('Drucker-Funktionen werden geladen...', 'info');
}
managePrinter(printerId) {
console.log(`🔧 Drucker ${printerId} wird verwaltet`);
this.showNotification(`Drucker ${printerId} wird verwaltet...`, 'info');
}
showPrinterSettings(printerId) {
console.log(`⚙️ Drucker-Einstellungen ${printerId} werden angezeigt`);
this.showNotification(`Drucker-Einstellungen werden geladen...`, 'info');
}
// Smart-Plug Ein/Aus Toggle für Drucker
async togglePrinterPower(printerId, printerName, button) {
console.log(`🔌 Smart-Plug Toggle für Drucker ${printerId} (${printerName})`);
// Validierung der Parameter
if (!button || !button.classList) {
console.error('❌ Ungültiger Button-Parameter:', button);
this.showNotification('❌ Fehler: Ungültiger Button-Parameter', 'error');
return;
}
// Bestätigungsdialog
const confirmMessage = `Möchten Sie die Steckdose für "${printerName}" umschalten?\n\nDies schaltet den Drucker ein/aus.`;
if (!confirm(confirmMessage)) return;
// Button-Zustand während der Anfrage anzeigen
const originalContent = button.innerHTML;
button.disabled = true;
button.innerHTML = `
Schaltet...
`;
try {
const response = await fetch(`${this.apiBaseUrl}/api/admin/printers/${printerId}/toggle`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': this.csrfToken
},
body: JSON.stringify({
reason: 'Admin-Panel Smart-Plug Toggle'
})
});
const data = await response.json();
if (response.ok && data.success) {
const action = data.action || 'umgeschaltet';
this.showNotification(`✅ Steckdose für "${printerName}" erfolgreich ${action}`, 'success');
// Button-Visual-Feedback
button.classList.remove('from-orange-500', 'to-red-500');
button.classList.add('from-green-500', 'to-green-600');
setTimeout(() => {
button.classList.remove('from-green-500', 'to-green-600');
button.classList.add('from-orange-500', 'to-red-500');
}, 2000);
// Live-Statistiken nach kurzer Verzögerung aktualisieren
setTimeout(() => {
this.loadLiveStats();
}, 1000);
} else {
const errorMsg = data.error || 'Unbekannter Fehler beim Schalten der Steckdose';
this.showNotification(`❌ Fehler: ${errorMsg}`, 'error');
console.error('Smart-Plug Toggle Fehler:', data);
}
} catch (error) {
console.error('Netzwerkfehler beim Smart-Plug Toggle:', error);
this.showNotification(`❌ Netzwerkfehler beim Schalten der Steckdose für "${printerName}"`, 'error');
} finally {
// Button-Zustand zurücksetzen
button.disabled = false;
button.innerHTML = originalContent;
}
}
// Job-Management
handleJobAction(action, jobId) {
console.log(`📋 Job-Aktion "${action}" für Job ${jobId}`);
this.showNotification(`Job-Aktion "${action}" wird ausgeführt...`, 'info');
}
// Error-Management
async checkSystemHealth() {
try {
const response = await fetch('/api/admin/system-health');
const data = await response.json();
if (data.success) {
this.updateHealthDisplay(data);
this.updateErrorAlerts(data);
}
} catch (error) {
console.error('Fehler bei System-Health-Check:', error);
}
}
updateHealthDisplay(data) {
const statusIndicator = document.getElementById('db-status-indicator');
const statusText = document.getElementById('db-status-text');
if (statusIndicator && statusText) {
if (data.health_status === 'critical') {
statusIndicator.className = 'w-3 h-3 bg-red-500 rounded-full animate-pulse';
statusText.textContent = 'Kritisch';
statusText.className = 'text-sm font-medium text-red-600 dark:text-red-400';
} else if (data.health_status === 'warning') {
statusIndicator.className = 'w-3 h-3 bg-yellow-500 rounded-full animate-pulse';
statusText.textContent = 'Warnung';
statusText.className = 'text-sm font-medium text-yellow-600 dark:text-yellow-400';
} else {
statusIndicator.className = 'w-3 h-3 bg-green-400 rounded-full animate-pulse';
statusText.textContent = 'Gesund';
statusText.className = 'text-sm font-medium text-green-600 dark:text-green-400';
}
}
this.updateElement('last-migration', data.last_migration || 'Unbekannt');
this.updateElement('schema-integrity', data.schema_integrity || 'Prüfung');
this.updateElement('recent-errors-count', data.recent_errors_count || 0);
}
updateErrorAlerts(data) {
const alertContainer = document.getElementById('critical-errors-alert');
if (!alertContainer) return;
const allErrors = [...(data.critical_errors || []), ...(data.warnings || [])];
if (allErrors.length > 0) {
alertContainer.classList.remove('hidden');
} else {
alertContainer.classList.add('hidden');
}
}
async fixErrors() {
if (!confirm('🔧 Möchten Sie die automatische Fehlerkorrektur durchführen?')) return;
this.showNotification('🔄 Fehler werden automatisch behoben...', 'info');
// Debug: CSRF Token prüfen
if (!this.csrfToken) {
console.error('❌ CSRF Token fehlt! Versuche Token neu zu laden...');
this.csrfToken = this.extractCSRFToken();
if (!this.csrfToken) {
this.showNotification('❌ Sicherheitsfehler: CSRF Token nicht verfügbar', 'error');
return;
}
}
console.log('🔧 Starte automatische Fehlerkorrektur...');
console.log('🔒 CSRF Token für Request:', this.csrfToken ? this.csrfToken.substring(0, 10) + '...' : 'NICHT VERFÜGBAR');
try {
const requestOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': this.csrfToken
}
};
console.log('📡 Sende Request an:', '/api/admin/fix-errors');
console.log('📝 Request Headers:', requestOptions.headers);
const response = await fetch('/api/admin/fix-errors', requestOptions);
console.log('📡 Response Status:', response.status);
console.log('📡 Response Headers:', Object.fromEntries(response.headers.entries()));
if (!response.ok) {
const errorText = await response.text();
console.error('❌ Response Error:', errorText);
throw new Error(`HTTP ${response.status}: ${response.statusText} - ${errorText}`);
}
const data = await response.json();
console.log('✅ Response Data:', data);
if (data.success) {
this.showNotification('✅ Automatische Reparatur erfolgreich!', 'success');
setTimeout(() => this.checkSystemHealth(), 2000);
} else {
this.showNotification(`❌ Automatische Reparatur fehlgeschlagen: ${data.message || 'Unbekannter Fehler'}`, 'error');
}
} catch (error) {
console.error('❌ Fehler bei automatischer Reparatur:', error);
this.showNotification(`❌ Fehler bei der automatischen Reparatur: ${error.message}`, 'error');
}
}
dismissErrors() {
const alertContainer = document.getElementById('critical-errors-alert');
if (alertContainer) {
alertContainer.classList.add('hidden');
}
}
// Notification System
showNotification(message, type = 'info') {
let notification = document.getElementById('admin-notification');
if (!notification) {
notification = document.createElement('div');
notification.id = 'admin-notification';
notification.className = 'fixed top-4 right-4 z-50 p-4 rounded-lg shadow-lg max-w-sm transition-all duration-300';
document.body.appendChild(notification);
}
const colors = {
success: 'bg-green-500 text-white',
error: 'bg-red-500 text-white',
info: 'bg-blue-500 text-white',
warning: 'bg-yellow-500 text-white'
};
notification.className = `fixed top-4 right-4 z-50 p-4 rounded-lg shadow-lg max-w-sm transition-all duration-300 ${colors[type]}`;
notification.textContent = message;
notification.style.transform = 'translateX(0)';
// Auto-Hide nach 3 Sekunden
setTimeout(() => {
if (notification) {
notification.style.transform = 'translateX(100%)';
setTimeout(() => {
if (notification && notification.parentNode) {
notification.parentNode.removeChild(notification);
}
}, 300);
}
}, 3000);
}
/**
* Logs-Management Funktionen
*/
async loadLogs(level = null) {
const logsContainer = document.getElementById('logs-container');
if (!logsContainer) return;
// Loading-Indikator anzeigen
logsContainer.innerHTML = `
`;
try {
const filter = level || document.getElementById('log-level-filter')?.value || 'all';
const url = `${this.apiBaseUrl}/api/admin/logs?level=${filter}&limit=100`;
const response = await fetch(url, {
headers: {
'X-CSRFToken': this.csrfToken
}
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
this.displayLogs(data.logs || []);
console.log('📋 Logs erfolgreich geladen');
} catch (error) {
console.error('Fehler beim Laden der Logs:', error);
logsContainer.innerHTML = `
Fehler beim Laden der Logs
${error.message}
`;
}
}
displayLogs(logs) {
const logsContainer = document.getElementById('logs-container');
if (!logsContainer) return;
if (!logs || logs.length === 0) {
logsContainer.innerHTML = `
Keine Logs gefunden
Es sind keine Logs für die ausgewählten Kriterien vorhanden.
`;
return;
}
const logsHtml = logs.map(log => {
const levelColors = {
'error': 'bg-red-50 dark:bg-red-900/20 border-red-200 dark:border-red-800 text-red-800 dark:text-red-200',
'warning': 'bg-yellow-50 dark:bg-yellow-900/20 border-yellow-200 dark:border-yellow-800 text-yellow-800 dark:text-yellow-200',
'info': 'bg-blue-50 dark:bg-blue-900/20 border-blue-200 dark:border-blue-800 text-blue-800 dark:text-blue-200',
'debug': 'bg-gray-50 dark:bg-gray-900/20 border-gray-200 dark:border-gray-800 text-gray-800 dark:text-gray-200',
'critical': 'bg-red-100 dark:bg-red-900/40 border-red-300 dark:border-red-700 text-red-900 dark:text-red-100'
};
const levelIcons = {
'error': '❌',
'warning': '⚠️',
'info': 'ℹ️',
'debug': '🔍',
'critical': '🚨'
};
const levelClass = levelColors[log.level] || levelColors['info'];
const levelIcon = levelIcons[log.level] || 'ℹ️';
return `
${levelIcon}
${log.level}
${log.component || 'System'}
${this.formatLogTimestamp(log.timestamp)}
${this.escapeHtml(log.message)}
${log.details ? `
${this.escapeHtml(log.details)}
` : ''}
${log.user ? `
Benutzer: ${this.escapeHtml(log.user)}
` : ''}
${log.ip_address ? `
IP: ${this.escapeHtml(log.ip_address)}
` : ''}
${log.request_id ? `
Request-ID: ${this.escapeHtml(log.request_id)}
` : ''}
`;
}).join('');
logsContainer.innerHTML = logsHtml;
}
formatLogTimestamp(timestamp) {
if (!timestamp) return 'Unbekannt';
try {
const date = new Date(timestamp);
return date.toLocaleString('de-DE', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
} catch (error) {
return timestamp;
}
}
escapeHtml(text) {
if (!text) return '';
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
async exportLogs() {
try {
this.showNotification('📥 Logs werden exportiert...', 'info');
const filter = document.getElementById('log-level-filter')?.value || 'all';
const url = `${this.apiBaseUrl}/api/admin/logs/export?level=${filter}`;
const response = await fetch(url, {
headers: {
'X-CSRFToken': this.csrfToken
}
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
// Download als Datei
const blob = await response.blob();
const downloadUrl = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = downloadUrl;
a.download = `system-logs-${new Date().toISOString().split('T')[0]}.csv`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(downloadUrl);
this.showNotification('✅ Logs erfolgreich exportiert!', 'success');
} catch (error) {
console.error('Fehler beim Exportieren der Logs:', error);
this.showNotification('❌ Fehler beim Exportieren der Logs: ' + error.message, 'error');
}
}
// ===== BUTTON-FUNKTIONALITÄT-TEST =====
testButtons() {
console.log('🧪 Teste Button-Funktionalität...');
// Teste Fix-Errors Button
const fixBtn = document.querySelector('#fix-errors-btn');
if (fixBtn) {
console.log('✅ Fix-Errors Button gefunden:', fixBtn);
console.log('🔗 Event-Listener-Status:', fixBtn.dataset.listenerAttached);
// Manueller Test-Click
fixBtn.addEventListener('click', (e) => {
console.log('🖱️ Fix-Errors Button wurde geklickt (manueller Listener)');
e.preventDefault();
e.stopPropagation();
this.testFixErrors();
});
} else {
console.error('❌ Fix-Errors Button NICHT gefunden!');
}
// Teste View-Details Button
const viewBtn = document.querySelector('#view-error-details-btn');
if (viewBtn) {
console.log('✅ View-Details Button gefunden:', viewBtn);
console.log('🔗 Event-Listener-Status:', viewBtn.dataset.listenerAttached);
// Manueller Test-Click
viewBtn.addEventListener('click', (e) => {
console.log('🖱️ View-Details Button wurde geklickt (manueller Listener)');
e.preventDefault();
e.stopPropagation();
console.log('🔄 Weiterleitung zu Logs-Tab...');
window.location.href = '/admin-dashboard?tab=logs';
});
} else {
console.error('❌ View-Details Button NICHT gefunden!');
}
}
// Test-Version der Fix-Errors Funktion
async testFixErrors() {
console.log('🧪 TEST: Fix-Errors wird ausgeführt...');
// Token-Test
console.log('🔒 Aktueller CSRF Token:', this.csrfToken);
if (!this.csrfToken) {
console.error('❌ CSRF Token fehlt - versuche neu zu laden...');
this.csrfToken = this.extractCSRFToken();
console.log('🔒 Neu geladener Token:', this.csrfToken);
}
this.showNotification('🧪 TEST: Starte automatische Fehlerkorrektur...', 'info');
try {
const requestOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': this.csrfToken,
'X-Requested-With': 'XMLHttpRequest'
}
};
console.log('📡 TEST Request an:', '/api/admin/fix-errors');
console.log('📝 TEST Headers:', requestOptions.headers);
const response = await fetch('/api/admin/fix-errors', requestOptions);
console.log('📡 TEST Response Status:', response.status);
console.log('📡 TEST Response Headers:', Object.fromEntries(response.headers.entries()));
if (!response.ok) {
const errorText = await response.text();
console.error('❌ TEST Response Error:', errorText);
this.showNotification(`❌ TEST Fehler: ${response.status} - ${errorText}`, 'error');
return;
}
const data = await response.json();
console.log('✅ TEST Response Data:', data);
if (data.success) {
this.showNotification('✅ TEST: Automatische Reparatur erfolgreich!', 'success');
} else {
this.showNotification(`❌ TEST: Reparatur fehlgeschlagen - ${data.message}`, 'error');
}
} catch (error) {
console.error('❌ TEST Fehler:', error);
this.showNotification(`❌ TEST Netzwerk-Fehler: ${error.message}`, 'error');
}
}
}
// Sichere Initialisierung - nur einmal ausführen
let adminDashboardInstance = null;
document.addEventListener('DOMContentLoaded', function() {
if (!adminDashboardInstance) {
adminDashboardInstance = new AdminDashboard();
window.AdminDashboard = adminDashboardInstance;
console.log('🎯 Admin Dashboard erfolgreich initialisiert (unified)');
}
});
// Export für globalen Zugriff
window.AdminDashboard = AdminDashboard;