diff --git a/backend/static/js/admin-unified.js b/backend/static/js/admin-unified.js index e7f08010f..3227159da 100644 --- a/backend/static/js/admin-unified.js +++ b/backend/static/js/admin-unified.js @@ -332,12 +332,8 @@ class AdminDashboard { try { const url = `${this.apiBaseUrl}/api/stats`; const response = await fetch(url); + const data = await this.validateApiResponse(response, 'Live-Statistiken'); - if (!response.ok) { - throw new Error(`HTTP ${response.status}: ${response.statusText}`); - } - - const data = await response.json(); this.updateStatsDisplay(data); this.retryCount = 0; @@ -347,6 +343,8 @@ class AdminDashboard { if (this.retryCount <= this.maxRetries) { setTimeout(() => this.loadLiveStats(), 5000); + } else { + this.showNotification('Live-Statistiken konnten nicht geladen werden', 'error'); } } } @@ -624,12 +622,7 @@ class AdminDashboard { 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(); + const data = await this.validateApiResponse(response, 'Benutzerdaten laden'); if (data.success) { const user = data.user; @@ -649,7 +642,7 @@ class AdminDashboard { } } catch (error) { console.error('Fehler beim Laden der Benutzerdaten:', error); - this.showNotification('❌ Fehler beim Laden der Benutzerdaten', 'error'); + this.showNotification(`❌ Fehler beim Laden der Benutzerdaten: ${error.message}`, 'error'); } } @@ -680,7 +673,7 @@ class AdminDashboard { body: JSON.stringify(userData) }); - const data = await response.json(); + const data = await this.validateApiResponse(response, 'Benutzer erstellen'); if (data.success) { this.showNotification('✅ Benutzer erfolgreich erstellt!', 'success'); @@ -895,20 +888,101 @@ class AdminDashboard { } // Error-Management + /** + * Zentrale API-Response-Validierung mit umfassendem Error-Handling + * @param {Response} response - Fetch Response-Objekt + * @param {string} context - Kontext der API-Anfrage für bessere Fehlermeldungen + * @returns {Promise} - Validierte JSON-Daten + * @throws {Error} - Bei Validierungsfehlern + */ + async validateApiResponse(response, context = 'API-Anfrage') { + try { + // 1. HTTP Status Code prüfen + if (!response.ok) { + // Spezielle Behandlung für bekannte Fehler-Codes + switch (response.status) { + case 401: + throw new Error(`Authentifizierung fehlgeschlagen (${context})`); + case 403: + throw new Error(`Zugriff verweigert (${context})`); + case 404: + throw new Error(`Ressource nicht gefunden (${context})`); + case 429: + throw new Error(`Zu viele Anfragen (${context})`); + case 500: + throw new Error(`Serverfehler (${context})`); + case 503: + throw new Error(`Service nicht verfügbar (${context})`); + default: + throw new Error(`HTTP ${response.status}: ${response.statusText} (${context})`); + } + } + + // 2. Content-Type prüfen (muss application/json enthalten) + const contentType = response.headers.get('content-type'); + if (!contentType || !contentType.includes('application/json')) { + // Versuche Response-Text zu lesen für bessere Fehlermeldung + const responseText = await response.text(); + + // Prüfe auf HTML-Fehlerseiten (typisch für 404/500 Seiten) + if (responseText.includes('') || responseText.includes('} - Validierte JSON-Daten + * @throws {Error} - Bei Validierungsfehlern + */ + async validateApiResponse(response, context = 'API-Anfrage') { + try { + // 1. HTTP Status Code prüfen + if (!response.ok) { + // Spezielle Behandlung für bekannte Fehler-Codes + switch (response.status) { + case 401: + throw new Error(`Authentifizierung fehlgeschlagen (${context})`); + case 403: + throw new Error(`Zugriff verweigert (${context})`); + case 404: + throw new Error(`Ressource nicht gefunden (${context})`); + case 429: + throw new Error(`Zu viele Anfragen (${context})`); + case 500: + throw new Error(`Serverfehler (${context})`); + case 503: + throw new Error(`Service nicht verfügbar (${context})`); + default: + throw new Error(`HTTP ${response.status}: ${response.statusText} (${context})`); + } + } + + // 2. Content-Type prüfen (muss application/json enthalten) + const contentType = response.headers.get('content-type'); + if (!contentType || !contentType.includes('application/json')) { + // Versuche Response-Text zu lesen für bessere Fehlermeldung + const responseText = await response.text(); + + // Prüfe auf HTML-Fehlerseiten (typisch für 404/500 Seiten) + if (responseText.includes('') || responseText.includes(' 0 && timeout !== 'never') { - this.timeout = timeout; - } else { - this.timeout = 0; // Deaktiviert - } + const response = await fetch(`${this.apiBaseUrl}/api/user/settings`); + const data = await this.validateApiResponse(response, 'Benutzereinstellungen laden'); + + if (data.success && data.settings.privacy?.auto_logout) { + const timeout = parseInt(data.settings.privacy.auto_logout); + if (timeout > 0 && timeout !== 'never') { + this.timeout = timeout; + } else { + this.timeout = 0; // Deaktiviert } } } catch (error) { @@ -111,13 +223,15 @@ class AutoLogoutManager { async sendKeepAlive() { try { - await fetch('/api/auth/keep-alive', { + const response = await fetch(`${this.apiBaseUrl}/api/auth/keep-alive`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': this.getCSRFToken() } }); + + await this.validateApiResponse(response, 'Keep-Alive senden'); } catch (error) { console.warn('Keep-Alive fehlgeschlagen:', error); } diff --git a/backend/static/js/charts.js b/backend/static/js/charts.js index e134e4fce..8c00e8fed 100644 --- a/backend/static/js/charts.js +++ b/backend/static/js/charts.js @@ -8,6 +8,119 @@ // Chart.js Instanzen Global verfügbar machen window.statsCharts = {}; +// API Base URL Detection +function detectApiBaseUrl() { + const currentPort = window.location.port; + const currentProtocol = window.location.protocol; + const currentHost = window.location.hostname; + + // Development-Umgebung (Port 5000) + if (currentPort === '5000') { + return `${currentProtocol}//${currentHost}:${currentPort}`; + } + + // Production-Umgebung (Port 443 oder kein Port) + if (currentPort === '443' || currentPort === '') { + return `${currentProtocol}//${currentHost}`; + } + + // Fallback für andere Ports + return window.location.origin; +} + +const API_BASE_URL = detectApiBaseUrl(); + +/** + * Zentrale API-Response-Validierung mit umfassendem Error-Handling + * @param {Response} response - Fetch Response-Objekt + * @param {string} context - Kontext der API-Anfrage für bessere Fehlermeldungen + * @returns {Promise} - Validierte JSON-Daten + * @throws {Error} - Bei Validierungsfehlern + */ +async function validateApiResponse(response, context = 'API-Anfrage') { + try { + // 1. HTTP Status Code prüfen + if (!response.ok) { + // Spezielle Behandlung für bekannte Fehler-Codes + switch (response.status) { + case 401: + throw new Error(`Authentifizierung fehlgeschlagen (${context})`); + case 403: + throw new Error(`Zugriff verweigert (${context})`); + case 404: + throw new Error(`Ressource nicht gefunden (${context})`); + case 429: + throw new Error(`Zu viele Anfragen (${context})`); + case 500: + throw new Error(`Serverfehler (${context})`); + case 503: + throw new Error(`Service nicht verfügbar (${context})`); + default: + throw new Error(`HTTP ${response.status}: ${response.statusText} (${context})`); + } + } + + // 2. Content-Type prüfen (muss application/json enthalten) + const contentType = response.headers.get('content-type'); + if (!contentType || !contentType.includes('application/json')) { + // Versuche Response-Text zu lesen für bessere Fehlermeldung + const responseText = await response.text(); + + // Prüfe auf HTML-Fehlerseiten (typisch für 404/500 Seiten) + if (responseText.includes('') || responseText.includes('} - Validierte JSON-Daten + * @throws {Error} - Bei Validierungsfehlern + */ +async function validateApiResponse(response, context = 'API-Anfrage') { + try { + // 1. HTTP Status Code prüfen + if (!response.ok) { + // Spezielle Behandlung für bekannte Fehler-Codes + switch (response.status) { + case 401: + throw new Error(`Authentifizierung fehlgeschlagen (${context})`); + case 403: + throw new Error(`Zugriff verweigert (${context})`); + case 404: + throw new Error(`Ressource nicht gefunden (${context})`); + case 429: + throw new Error(`Zu viele Anfragen (${context})`); + case 500: + throw new Error(`Serverfehler (${context})`); + case 503: + throw new Error(`Service nicht verfügbar (${context})`); + default: + throw new Error(`HTTP ${response.status}: ${response.statusText} (${context})`); + } + } + + // 2. Content-Type prüfen (muss application/json enthalten) + const contentType = response.headers.get('content-type'); + if (!contentType || !contentType.includes('application/json')) { + // Versuche Response-Text zu lesen für bessere Fehlermeldung + const responseText = await response.text(); + + // Prüfe auf HTML-Fehlerseiten (typisch für 404/500 Seiten) + if (responseText.includes('') || responseText.includes('Fehler beim Laden: ${error.message}`; } } } @@ -167,18 +250,14 @@ function updateRecentJobsList(jobs) { // Aktuelle Aktivitäten laden async function loadRecentActivities() { try { - const response = await fetch('/api/activity/recent'); - if (!response.ok) { - throw new Error(`HTTP ${response.status}: ${response.statusText}`); - } - - const data = await response.json(); + const response = await fetch(`${API_BASE_URL}/api/activity/recent`); + const data = await validateApiResponse(response, 'Aktivitäten'); updateRecentActivitiesList(data.activities); } catch (error) { console.error('Fehler beim Laden der Aktivitäten:', error); if (elements.recentActivitiesList) { - elements.recentActivitiesList.innerHTML = '
  • Fehler beim Laden
  • '; + elements.recentActivitiesList.innerHTML = `
  • Fehler beim Laden: ${error.message}
  • `; } } } @@ -209,12 +288,8 @@ function updateRecentActivitiesList(activities) { // Scheduler-Status laden async function loadSchedulerStatus() { try { - const response = await fetch('/api/scheduler/status'); - if (!response.ok) { - throw new Error(`HTTP ${response.status}: ${response.statusText}`); - } - - const data = await response.json(); + const response = await fetch(`${API_BASE_URL}/api/scheduler/status`); + const data = await validateApiResponse(response, 'Scheduler-Status'); updateSchedulerStatus(data.running); } catch (error) { @@ -253,11 +328,7 @@ async function toggleScheduler() { } }); - if (!response.ok) { - throw new Error(`HTTP ${response.status}: ${response.statusText}`); - } - - const result = await response.json(); + const result = await validateApiResponse(response, 'Scheduler umschalten'); if (result.success) { showSuccess(result.message); diff --git a/backend/static/js/job-manager.js b/backend/static/js/job-manager.js index 3e31ceb94..b220a55bf 100644 --- a/backend/static/js/job-manager.js +++ b/backend/static/js/job-manager.js @@ -19,7 +19,124 @@ this.refreshInterval = null; this.autoRefreshEnabled = false; + // API Base URL Detection + this.apiBaseUrl = this.detectApiBaseUrl(); + console.log('🔧 JobManager wird initialisiert...'); + console.log('🌐 API Base URL:', this.apiBaseUrl); + } + + /** + * Zentrale API-Response-Validierung mit umfassendem Error-Handling + * @param {Response} response - Fetch Response-Objekt + * @param {string} context - Kontext der API-Anfrage für bessere Fehlermeldungen + * @returns {Promise} - Validierte JSON-Daten + * @throws {Error} - Bei Validierungsfehlern + */ + async validateApiResponse(response, context = 'API-Anfrage') { + try { + // 1. HTTP Status Code prüfen + if (!response.ok) { + // Spezielle Behandlung für bekannte Fehler-Codes + switch (response.status) { + case 401: + throw new Error(`Authentifizierung fehlgeschlagen (${context})`); + case 403: + throw new Error(`Zugriff verweigert (${context})`); + case 404: + throw new Error(`Ressource nicht gefunden (${context})`); + case 429: + throw new Error(`Zu viele Anfragen (${context})`); + case 500: + throw new Error(`Serverfehler (${context})`); + case 503: + throw new Error(`Service nicht verfügbar (${context})`); + default: + throw new Error(`HTTP ${response.status}: ${response.statusText} (${context})`); + } + } + + // 2. Content-Type prüfen (muss application/json enthalten) + const contentType = response.headers.get('content-type'); + if (!contentType || !contentType.includes('application/json')) { + // Versuche Response-Text zu lesen für bessere Fehlermeldung + const responseText = await response.text(); + + // Prüfe auf HTML-Fehlerseiten (typisch für 404/500 Seiten) + if (responseText.includes('') || responseText.includes('} - Validierte JSON-Daten + * @throws {Error} - Bei Validierungsfehlern + */ + async validateApiResponse(response, context = 'API-Anfrage') { + try { + // 1. HTTP Status Code prüfen + if (!response.ok) { + // Spezielle Behandlung für bekannte Fehler-Codes + switch (response.status) { + case 401: + throw new Error(`Authentifizierung fehlgeschlagen (${context})`); + case 403: + throw new Error(`Zugriff verweigert (${context})`); + case 404: + throw new Error(`Ressource nicht gefunden (${context})`); + case 429: + throw new Error(`Zu viele Anfragen (${context})`); + case 500: + throw new Error(`Serverfehler (${context})`); + case 503: + throw new Error(`Service nicht verfügbar (${context})`); + default: + throw new Error(`HTTP ${response.status}: ${response.statusText} (${context})`); + } + } + + // 2. Content-Type prüfen (muss application/json enthalten) + const contentType = response.headers.get('content-type'); + if (!contentType || !contentType.includes('application/json')) { + // Versuche Response-Text zu lesen für bessere Fehlermeldung + const responseText = await response.text(); + + // Prüfe auf HTML-Fehlerseiten (typisch für 404/500 Seiten) + if (responseText.includes('') || responseText.includes('