diff --git a/backend/app/database/myp.db b/backend/app/database/myp.db index 35e1f1ca..9c28d98f 100644 Binary files a/backend/app/database/myp.db and b/backend/app/database/myp.db differ diff --git a/backend/app/docs/ADMIN_PANEL_FUNKTIONEN.md b/backend/app/docs/ADMIN_PANEL_FUNKTIONEN.md new file mode 100644 index 00000000..0519ecba --- /dev/null +++ b/backend/app/docs/ADMIN_PANEL_FUNKTIONEN.md @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/backend/app/static/js/admin-system.js b/backend/app/static/js/admin-system.js new file mode 100644 index 00000000..2eabf15e --- /dev/null +++ b/backend/app/static/js/admin-system.js @@ -0,0 +1,214 @@ +/** + * Admin System Management JavaScript + * Funktionen für System-Wartung und -Konfiguration + */ + +// CSRF Token für AJAX-Anfragen +function getCsrfToken() { + const token = document.querySelector('meta[name="csrf-token"]'); + return token ? token.getAttribute('content') : ''; +} + +// Hilfsfunktion für API-Aufrufe +async function makeApiCall(url, method = 'GET', data = null) { + const options = { + method: method, + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': getCsrfToken() + } + }; + + if (data) { + options.body = JSON.stringify(data); + } + + try { + const response = await fetch(url, options); + const result = await response.json(); + + if (response.ok) { + showNotification(result.message || 'Aktion erfolgreich ausgeführt', 'success'); + return result; + } else { + showNotification(result.error || 'Ein Fehler ist aufgetreten', 'error'); + return null; + } + } catch (error) { + showNotification('Netzwerkfehler: ' + error.message, 'error'); + return null; + } +} + +// Notification anzeigen +function showNotification(message, type = 'info') { + // Erstelle Notification-Element falls nicht vorhanden + 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 transform translate-x-full'; + document.body.appendChild(notification); + } + + // Setze Farbe basierend auf Typ + const colors = { + success: 'bg-green-500 text-white', + error: 'bg-red-500 text-white', + warning: 'bg-yellow-500 text-white', + info: 'bg-blue-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] || colors.info}`; + notification.textContent = message; + + // Zeige Notification + notification.style.transform = 'translateX(0)'; + + // Verstecke nach 5 Sekunden + setTimeout(() => { + notification.style.transform = 'translateX(100%)'; + }, 5000); +} + +// Cache leeren +async function clearCache() { + if (confirm('Möchten Sie wirklich den Cache leeren?')) { + showNotification('Cache wird geleert...', 'info'); + const result = await makeApiCall('/api/admin/cache/clear', 'POST'); + if (result) { + setTimeout(() => location.reload(), 2000); + } + } +} + +// Datenbank optimieren +async function optimizeDatabase() { + if (confirm('Möchten Sie wirklich die Datenbank optimieren? Dies kann einige Minuten dauern.')) { + showNotification('Datenbank wird optimiert...', 'info'); + const result = await makeApiCall('/api/admin/database/optimize', 'POST'); + if (result) { + setTimeout(() => location.reload(), 2000); + } + } +} + +// Backup erstellen +async function createBackup() { + if (confirm('Möchten Sie wirklich ein Backup erstellen?')) { + showNotification('Backup wird erstellt...', 'info'); + const result = await makeApiCall('/api/admin/backup/create', 'POST'); + } +} + +// Drucker aktualisieren +async function updatePrinters() { + if (confirm('Möchten Sie alle Drucker-Verbindungen aktualisieren?')) { + showNotification('Drucker werden aktualisiert...', 'info'); + const result = await makeApiCall('/api/admin/printers/update', 'POST'); + if (result) { + setTimeout(() => location.reload(), 2000); + } + } +} + +// System neustarten +async function restartSystem() { + if (confirm('WARNUNG: Möchten Sie wirklich das System neustarten? Alle aktiven Verbindungen werden getrennt.')) { + const result = await makeApiCall('/api/admin/system/restart', 'POST'); + if (result) { + showNotification('System wird neugestartet...', 'warning'); + setTimeout(() => { + window.location.href = '/'; + }, 3000); + } + } +} + +// Einstellungen bearbeiten +function editSettings() { + window.location.href = '/settings'; +} + +// Systemstatus automatisch aktualisieren +async function updateSystemStatus() { + if (window.location.search.includes('tab=system')) { + const result = await makeApiCall('/api/admin/system/status'); + if (result) { + // Aktualisiere die Anzeige + updateStatusDisplay('cpu_usage', result.cpu_usage + '%'); + updateStatusDisplay('memory_usage', result.memory_usage + '%'); + updateStatusDisplay('disk_usage', result.disk_usage + '%'); + updateStatusDisplay('uptime', result.uptime); + updateStatusDisplay('db_size', result.db_size); + updateStatusDisplay('scheduler_jobs', result.scheduler_jobs); + updateStatusDisplay('next_job', result.next_job); + + // Scheduler-Status aktualisieren + const schedulerStatus = document.querySelector('.scheduler-status'); + if (schedulerStatus) { + if (result.scheduler_running) { + schedulerStatus.innerHTML = 'Läuft'; + schedulerStatus.className = 'inline-flex items-center px-2 py-1 text-xs font-semibold rounded-full bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200'; + } else { + schedulerStatus.innerHTML = 'Gestoppt'; + schedulerStatus.className = 'inline-flex items-center px-2 py-1 text-xs font-semibold rounded-full bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200'; + } + } + } + } +} + +// Hilfsfunktion zum Aktualisieren der Status-Anzeige +function updateStatusDisplay(key, value) { + const element = document.querySelector(`[data-status="${key}"]`); + if (element) { + element.textContent = value; + } +} + +// Datenbankstatus aktualisieren +async function updateDatabaseStatus() { + if (window.location.search.includes('tab=system')) { + const result = await makeApiCall('/api/admin/database/status'); + if (result) { + const dbStatus = document.querySelector('.database-status'); + if (dbStatus) { + if (result.connected) { + dbStatus.innerHTML = 'Verbunden'; + dbStatus.className = 'inline-flex items-center px-2 py-1 text-xs font-semibold rounded-full bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200'; + } else { + dbStatus.innerHTML = 'Getrennt'; + dbStatus.className = 'inline-flex items-center px-2 py-1 text-xs font-semibold rounded-full bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200'; + } + } + + updateStatusDisplay('db_size', result.size); + updateStatusDisplay('db_connections', result.connected ? 'Aktiv' : 'Getrennt'); + } + } +} + +// Auto-Update alle 30 Sekunden +setInterval(() => { + updateSystemStatus(); + updateDatabaseStatus(); +}, 30000); + +// Initial load +document.addEventListener('DOMContentLoaded', function() { + updateSystemStatus(); + updateDatabaseStatus(); +}); + +// Export für globale Verwendung +window.adminSystem = { + clearCache, + optimizeDatabase, + createBackup, + updatePrinters, + restartSystem, + editSettings, + updateSystemStatus, + updateDatabaseStatus +}; \ No newline at end of file diff --git a/backend/app/templates/admin.html b/backend/app/templates/admin.html index 82bea5eb..697b934d 100644 --- a/backend/app/templates/admin.html +++ b/backend/app/templates/admin.html @@ -7,6 +7,7 @@ + {% endblock %} {% block content %} @@ -478,7 +479,7 @@

Server Status

- + Online @@ -502,7 +503,7 @@

Datenbank

- + Verbunden @@ -522,9 +523,9 @@

Scheduler

- + - Läuft + {{ 'Läuft' if system_info.scheduler_running else 'Gestoppt' }}
diff --git a/backend/app/templates/printers.html b/backend/app/templates/printers.html index 11b0a2ec..86503a7c 100644 --- a/backend/app/templates/printers.html +++ b/backend/app/templates/printers.html @@ -221,7 +221,7 @@ document.getElementById('printerDetailModal').classList.add('hidden'); } - // Load printers + // Load printers (verwendet jetzt auch Status-Check) async function loadPrinters() { try { const response = await fetch('/api/printers'); @@ -276,9 +276,12 @@

${printer.name}

${printer.model}

- - ${statusText} - +
+ + ${statusText} + + ${printer.last_checked ? `Geprüft: ${formatTime(printer.last_checked)}` : ''} +
@@ -366,6 +369,23 @@ return date.toLocaleString('de-DE'); } + // Format time helper für Status-Zeitstempel + function formatTime(dateString) { + const date = new Date(dateString); + const now = new Date(); + const diffMs = now - date; + const diffSecs = Math.floor(diffMs / 1000); + const diffMins = Math.floor(diffSecs / 60); + + if (diffSecs < 60) { + return 'gerade eben'; + } else if (diffMins < 60) { + return `vor ${diffMins} Min`; + } else { + return date.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' }); + } + } + // Show error message function showError(message) { const grid = document.getElementById('printers-grid'); @@ -382,6 +402,65 @@ `; } + // Show status message (success, info, warning, error) + function showStatusMessage(message, type = 'info') { + // Entferne vorherige Status-Nachrichten + const existingMessage = document.getElementById('status-message'); + if (existingMessage) { + existingMessage.remove(); + } + + // Bestimme Farben basierend auf Typ + let bgColor, textColor, iconPath; + switch (type) { + case 'success': + bgColor = 'bg-green-100 dark:bg-green-900/30'; + textColor = 'text-green-800 dark:text-green-200'; + iconPath = 'M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z'; + break; + case 'warning': + bgColor = 'bg-yellow-100 dark:bg-yellow-900/30'; + textColor = 'text-yellow-800 dark:text-yellow-200'; + iconPath = 'M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z'; + break; + case 'error': + bgColor = 'bg-red-100 dark:bg-red-900/30'; + textColor = 'text-red-800 dark:text-red-200'; + iconPath = 'M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z'; + break; + default: // info + bgColor = 'bg-blue-100 dark:bg-blue-900/30'; + textColor = 'text-blue-800 dark:text-blue-200'; + iconPath = 'M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z'; + } + + // Erstelle Status-Nachricht + const statusMessage = document.createElement('div'); + statusMessage.id = 'status-message'; + statusMessage.className = `fixed top-4 right-4 z-50 ${bgColor} ${textColor} px-4 py-3 rounded-lg shadow-lg flex items-center space-x-3 max-w-md`; + statusMessage.innerHTML = ` + + + + ${message} + + `; + + // Füge zur Seite hinzu + document.body.appendChild(statusMessage); + + // Automatisch nach 5 Sekunden entfernen + setTimeout(() => { + if (statusMessage && statusMessage.parentElement) { + statusMessage.remove(); + } + }, 5000); + } + // Add printer async function handleAddPrinter(event) { event.preventDefault();