📝 "feat: Implement new database functions in ADMIN_PANEL_FUNKTIONEN
This commit is contained in:
parent
2406aedf38
commit
4245f48caa
Binary file not shown.
1
backend/app/docs/ADMIN_PANEL_FUNKTIONEN.md
Normal file
1
backend/app/docs/ADMIN_PANEL_FUNKTIONEN.md
Normal file
@ -0,0 +1 @@
|
||||
|
214
backend/app/static/js/admin-system.js
Normal file
214
backend/app/static/js/admin-system.js
Normal file
@ -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 = '<span class="w-2 h-2 mr-1 rounded-full bg-blue-400 animate-pulse"></span>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 = '<span class="w-2 h-2 mr-1 rounded-full bg-red-400"></span>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 = '<span class="w-2 h-2 mr-1 rounded-full bg-green-400 animate-pulse"></span>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 = '<span class="w-2 h-2 mr-1 rounded-full bg-red-400"></span>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
|
||||
};
|
@ -7,6 +7,7 @@
|
||||
<!-- CSRF Token für AJAX-Anfragen -->
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<script src="{{ url_for('static', filename='js/admin.js') }}" defer></script>
|
||||
<script src="{{ url_for('static', filename='js/admin-system.js') }}" defer></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
@ -478,7 +479,7 @@
|
||||
<div class="bg-white/60 dark:bg-slate-700/60 backdrop-blur-sm rounded-xl border border-slate-200 dark:border-slate-600 p-6 shadow-lg">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h3 class="text-lg font-semibold text-slate-900 dark:text-white">Server Status</h3>
|
||||
<span class="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">
|
||||
<span class="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 server-status">
|
||||
<span class="w-2 h-2 mr-1 rounded-full bg-green-400 animate-pulse"></span>
|
||||
Online
|
||||
</span>
|
||||
@ -502,7 +503,7 @@
|
||||
<div class="bg-white/60 dark:bg-slate-700/60 backdrop-blur-sm rounded-xl border border-slate-200 dark:border-slate-600 p-6 shadow-lg">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h3 class="text-lg font-semibold text-slate-900 dark:text-white">Datenbank</h3>
|
||||
<span class="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">
|
||||
<span class="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 database-status">
|
||||
<span class="w-2 h-2 mr-1 rounded-full bg-green-400 animate-pulse"></span>
|
||||
Verbunden
|
||||
</span>
|
||||
@ -522,9 +523,9 @@
|
||||
<div class="bg-white/60 dark:bg-slate-700/60 backdrop-blur-sm rounded-xl border border-slate-200 dark:border-slate-600 p-6 shadow-lg">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h3 class="text-lg font-semibold text-slate-900 dark:text-white">Scheduler</h3>
|
||||
<span class="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">
|
||||
<span class="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 scheduler-status">
|
||||
<span class="w-2 h-2 mr-1 rounded-full bg-blue-400 animate-pulse"></span>
|
||||
Läuft
|
||||
{{ 'Läuft' if system_info.scheduler_running else 'Gestoppt' }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
|
@ -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 @@
|
||||
<h3 class="text-base sm:text-lg font-bold text-slate-900 dark:text-white">${printer.name}</h3>
|
||||
<p class="text-xs sm:text-sm text-slate-600 dark:text-slate-400">${printer.model}</p>
|
||||
</div>
|
||||
<span class="inline-flex items-center px-2 py-0.5 sm:px-2.5 sm:py-0.5 rounded-full text-xs font-medium ${statusColor}">
|
||||
${statusText}
|
||||
</span>
|
||||
<div class="flex flex-col items-end">
|
||||
<span class="inline-flex items-center px-2 py-0.5 sm:px-2.5 sm:py-0.5 rounded-full text-xs font-medium ${statusColor}">
|
||||
${statusText}
|
||||
</span>
|
||||
${printer.last_checked ? `<span class="text-xs text-slate-500 dark:text-slate-400 mt-1">Geprüft: ${formatTime(printer.last_checked)}</span>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-1.5 sm:space-y-2 mb-3 sm:mb-4">
|
||||
@ -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 = `
|
||||
<svg class="h-5 w-5 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="${iconPath}" />
|
||||
</svg>
|
||||
<span class="text-sm font-medium">${message}</span>
|
||||
<button onclick="this.parentElement.remove()" class="ml-2 text-current hover:opacity-75">
|
||||
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
`;
|
||||
|
||||
// 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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user