"feat: Update admin and printer templates with new design"

This commit is contained in:
2025-05-26 12:50:13 +02:00
parent e122fe0cc9
commit 2406aedf38
3 changed files with 427 additions and 70 deletions

View File

@@ -1,15 +1,21 @@
import os import os
import sys
import logging
import threading import threading
import time import time
import subprocess
import socket
import json import json
import secrets import secrets
import subprocess from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Dict, List, Optional, Tuple, Any, Union
from functools import wraps from functools import wraps
from typing import Optional, Dict, List, Tuple, Any, Union
from flask import Flask, request, jsonify, session, render_template, redirect, url_for, flash, Response from flask import Flask, render_template, request, redirect, url_for, flash, jsonify, session, send_file, Response
from flask_login import LoginManager, UserMixin, login_user, logout_user, current_user, login_required from flask_login import LoginManager, login_user, logout_user, login_required, current_user, UserMixin
from werkzeug.security import check_password_hash, generate_password_hash
from werkzeug.utils import secure_filename
import sqlalchemy.exc import sqlalchemy.exc
import sqlalchemy import sqlalchemy
from PyP100 import PyP110 from PyP100 import PyP110
@@ -119,6 +125,82 @@ def job_owner_required(f):
return f(job_id, *args, **kwargs) return f(job_id, *args, **kwargs)
return decorated_function return decorated_function
def check_printer_status(ip_address: str, timeout: int = 7) -> Tuple[str, bool]:
"""
Überprüft den Status eines Druckers über Ping mit Timeout.
Args:
ip_address: IP-Adresse des Druckers
timeout: Timeout in Sekunden (Standard: 7)
Returns:
Tuple[str, bool]: (Status, Aktiv) - Status ist "online" oder "offline", Aktiv ist True/False
"""
if not ip_address:
return "offline", False
try:
# Windows-spezifischer Ping-Befehl mit Timeout
if os.name == 'nt': # Windows
cmd = ['ping', '-n', '1', '-w', str(timeout * 1000), ip_address]
else: # Unix/Linux/macOS
cmd = ['ping', '-c', '1', '-W', str(timeout), ip_address]
# Ping ausführen mit Timeout
result = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=timeout + 1 # Zusätzlicher Timeout für subprocess
)
# Erfolgreicher Ping (Return Code 0)
if result.returncode == 0:
return "online", True
else:
return "offline", False
except subprocess.TimeoutExpired:
printers_logger.warning(f"Ping-Timeout für Drucker {ip_address} nach {timeout} Sekunden")
return "offline", False
except Exception as e:
printers_logger.error(f"Fehler beim Ping für Drucker {ip_address}: {str(e)}")
return "offline", False
def check_multiple_printers_status(printers: List[Dict], timeout: int = 7) -> Dict[int, Tuple[str, bool]]:
"""
Überprüft den Status mehrerer Drucker parallel mit Timeout.
Args:
printers: Liste von Drucker-Dictionaries mit 'id' und 'ip_address'
timeout: Timeout in Sekunden pro Drucker (Standard: 7)
Returns:
Dict[int, Tuple[str, bool]]: Dictionary mit Drucker-ID als Key und (Status, Aktiv) als Value
"""
results = {}
# Parallel-Ausführung mit ThreadPoolExecutor
with ThreadPoolExecutor(max_workers=min(len(printers), 10)) as executor:
# Futures für alle Drucker erstellen
future_to_printer = {
executor.submit(check_printer_status, printer.get('ip_address'), timeout): printer
for printer in printers
}
# Ergebnisse sammeln
for future in as_completed(future_to_printer, timeout=timeout + 2):
printer = future_to_printer[future]
try:
status, active = future.result()
results[printer['id']] = (status, active)
printers_logger.info(f"Drucker {printer['name']} ({printer.get('ip_address')}): {status}")
except Exception as e:
printers_logger.error(f"Fehler bei Status-Check für Drucker {printer['name']}: {str(e)}")
results[printer['id']] = ("offline", False)
return results
# UI-Routen # UI-Routen
@app.route("/") @app.route("/")
def index(): def index():
@@ -216,12 +298,18 @@ def admin_page():
# Benutzeranzahl # Benutzeranzahl
stats["total_users"] = db_session.query(User).count() stats["total_users"] = db_session.query(User).count()
# Druckeranzahl # Druckeranzahl und Online-Status
stats["total_printers"] = db_session.query(Printer).count() all_printers = db_session.query(Printer).all()
stats["total_printers"] = len(all_printers)
stats["online_printers"] = len([p for p in all_printers if p.status == "online"])
# Aktive Jobs # Aktive Jobs und Warteschlange
stats["active_jobs"] = db_session.query(Job).filter( stats["active_jobs"] = db_session.query(Job).filter(
Job.status.in_(["scheduled", "running"]) Job.status.in_(["printing", "running"])
).count()
stats["queued_jobs"] = db_session.query(Job).filter(
Job.status == "scheduled"
).count() ).count()
# Erfolgsrate # Erfolgsrate
@@ -259,11 +347,55 @@ def admin_page():
# System-Informationen laden # System-Informationen laden
if active_tab == 'system': if active_tab == 'system':
import psutil import psutil
# CPU und Memory
cpu_percent = psutil.cpu_percent(interval=1)
memory = psutil.virtual_memory()
disk = psutil.disk_usage('/')
# Uptime
boot_time = psutil.boot_time()
uptime_seconds = time.time() - boot_time
uptime_days = int(uptime_seconds // 86400)
uptime_hours = int((uptime_seconds % 86400) // 3600)
uptime_minutes = int((uptime_seconds % 3600) // 60)
# Datenbank-Status
db_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'instance', 'database.db')
db_size = 0
if os.path.exists(db_path):
db_size = os.path.getsize(db_path) / (1024 * 1024) # MB
# Scheduler-Status
scheduler_running = False
scheduler_jobs = 0
try:
from utils.job_scheduler import scheduler
scheduler_running = scheduler.running
if hasattr(scheduler, 'get_jobs'):
scheduler_jobs = len(scheduler.get_jobs())
except:
pass
# Nächster Job
next_job = db_session.query(Job).filter(
Job.status == "scheduled"
).order_by(Job.created_at.asc()).first()
next_job_time = "Keine geplanten Jobs"
if next_job:
next_job_time = next_job.created_at.strftime("%d.%m.%Y %H:%M")
system_info = { system_info = {
"cpu": psutil.cpu_percent(), "cpu_usage": round(cpu_percent, 1),
"memory": psutil.virtual_memory().percent, "memory_usage": round(memory.percent, 1),
"disk": psutil.disk_usage('/').percent, "disk_usage": round((disk.used / disk.total) * 100, 1),
"uptime": get_system_uptime_days() "uptime": f"{uptime_days}d {uptime_hours}h {uptime_minutes}m",
"db_size": f"{db_size:.1f} MB",
"db_connections": "Aktiv",
"scheduler_running": scheduler_running,
"scheduler_jobs": scheduler_jobs,
"next_job": next_job_time
} }
# Logs laden # Logs laden
@@ -586,36 +718,58 @@ def finish_job(job_id):
@app.route("/api/printers", methods=["GET"]) @app.route("/api/printers", methods=["GET"])
@login_required @login_required
def get_printers(): def get_printers():
"""Gibt alle Drucker mit aktuellem Status zurück (mit Ping-Check und 7-Sekunden-Timeout)."""
db_session = get_db_session() db_session = get_db_session()
try: try:
printers = db_session.query(Printer).all() printers = db_session.query(Printer).all()
# Optimierte Drucker-Liste mit schneller Status-Bestimmung # Drucker-Daten für Status-Check vorbereiten
printer_data = []
for printer in printers:
printer_data.append({
'id': printer.id,
'name': printer.name,
'ip_address': printer.ip_address,
'location': printer.location
})
# Status aller Drucker parallel überprüfen mit 7-Sekunden-Timeout
printers_logger.info(f"Starte Drucker-Status-Check für {len(printer_data)} Drucker")
status_results = check_multiple_printers_status(printer_data, timeout=7)
# Drucker-Liste mit aktuellem Status erstellen
printer_list = [] printer_list = []
for printer in printers: for printer in printers:
# Bestimme Status basierend auf hardkodierten Druckern if printer.id in status_results:
printer_config = PRINTERS.get(printer.name) status, active = status_results[printer.id]
if printer_config: # Mapping für Frontend-Kompatibilität
status = "available" # Drucker verfügbar if status == "online":
active = True frontend_status = "available"
else:
frontend_status = "offline"
else: else:
status = "offline" # Fallback falls kein Ergebnis vorliegt
frontend_status = "offline"
active = False active = False
# Aktualisiere Status in der Datenbank # Status in der Datenbank aktualisieren
printer.status = status printer.status = frontend_status
printer.active = active printer.active = active
printer_data = printer.to_dict() printer_data = printer.to_dict()
printer_data["status"] = status printer_data["status"] = frontend_status
printer_data["active"] = active printer_data["active"] = active
printer_data["last_checked"] = datetime.now().isoformat()
printer_list.append(printer_data) printer_list.append(printer_data)
# Speichere Updates # Speichere Updates
db_session.commit() db_session.commit()
db_session.close() db_session.close()
online_count = len([p for p in printer_list if p["status"] == "available"])
printers_logger.info(f"Drucker-Status-Check abgeschlossen: {online_count} von {len(printer_list)} Drucker verfügbar")
return jsonify({ return jsonify({
"printers": printer_list "printers": printer_list
}) })
@@ -739,26 +893,35 @@ def get_activity():
@app.route("/api/printers/status", methods=["GET"]) @app.route("/api/printers/status", methods=["GET"])
@login_required @login_required
def get_printers_status(): def get_printers_status():
"""Gibt den Status aller Drucker zurück - optimiert für schnelle Antwort.""" """Gibt den Status aller Drucker zurück mit echtem Ping-Check und 7-Sekunden-Timeout."""
db_session = get_db_session() db_session = get_db_session()
try: try:
printers = db_session.query(Printer).all() printers = db_session.query(Printer).all()
# Schnelle Status-Bestimmung basierend auf hardkodierten Druckern # Drucker-Daten für Status-Check vorbereiten
printer_data = []
for printer in printers:
printer_data.append({
'id': printer.id,
'name': printer.name,
'ip_address': printer.ip_address,
'location': printer.location
})
# Status aller Drucker parallel überprüfen mit 7-Sekunden-Timeout
printers_logger.info(f"Starte Status-Check für {len(printer_data)} Drucker mit 7-Sekunden-Timeout")
status_results = check_multiple_printers_status(printer_data, timeout=7)
# Ergebnisse zusammenstellen und Datenbank aktualisieren
status_data = [] status_data = []
for printer in printers: for printer in printers:
# Bestimme Status basierend auf IP-Adresse aus der Konfiguration if printer.id in status_results:
printer_config = PRINTERS.get(printer.name) status, active = status_results[printer.id]
if printer_config:
# Drucker ist in der Konfiguration -> als online betrachten
status = "online"
active = True
else: else:
# Drucker nicht in Konfiguration -> offline # Fallback falls kein Ergebnis vorliegt
status = "offline" status, active = "offline", False
active = False
# Aktualisiere den Status in der Datenbank für Konsistenz # Status in der Datenbank aktualisieren
printer.status = status printer.status = status
printer.active = active printer.active = active
@@ -768,13 +931,16 @@ def get_printers_status():
"status": status, "status": status,
"active": active, "active": active,
"ip_address": printer.ip_address, "ip_address": printer.ip_address,
"location": printer.location "location": printer.location,
"last_checked": datetime.now().isoformat()
}) })
# Speichere die aktualisierten Status # Speichere die aktualisierten Status
db_session.commit() db_session.commit()
db_session.close() db_session.close()
printers_logger.info(f"Status-Check abgeschlossen: {len([s for s in status_data if s['status'] == 'online'])} von {len(status_data)} Drucker online")
return jsonify(status_data) return jsonify(status_data)
except Exception as e: except Exception as e:
db_session.rollback() db_session.rollback()

View File

@@ -442,7 +442,7 @@
{% if job.status == 'queued' %} {% if job.status == 'queued' %}
<button class="text-green-600 hover:text-green-900 dark:text-green-400 dark:hover:text-green-300 transition-colors"> <button class="text-green-600 hover:text-green-900 dark:text-green-400 dark:hover:text-green-300 transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.828 14.828a4 4 0 01-5.656 0M9 10h1m4 0h1m-6 4h8m2-10h.01M5 20h14a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.828 14.828a4 4 0 01-5.656 0M9 10h1m4 0h1m-6 4h8m2-10h.01M5 20h14a2 2 0 002-2V7a2 2 0 00-2-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2zM9 9h6v6H9V9z"/>
</svg> </svg>
</button> </button>
{% endif %} {% endif %}
@@ -486,15 +486,15 @@
<div class="space-y-2"> <div class="space-y-2">
<div class="flex justify-between text-sm"> <div class="flex justify-between text-sm">
<span class="text-slate-600 dark:text-slate-400">Uptime:</span> <span class="text-slate-600 dark:text-slate-400">Uptime:</span>
<span class="text-slate-900 dark:text-white font-medium">{{ system_info.uptime }}</span> <span class="text-slate-900 dark:text-white font-medium">{{ system_info.uptime if system_info.uptime else 'Unbekannt' }}</span>
</div> </div>
<div class="flex justify-between text-sm"> <div class="flex justify-between text-sm">
<span class="text-slate-600 dark:text-slate-400">CPU:</span> <span class="text-slate-600 dark:text-slate-400">CPU:</span>
<span class="text-slate-900 dark:text-white font-medium">{{ system_info.cpu_usage }}%</span> <span class="text-slate-900 dark:text-white font-medium">{{ system_info.cpu_usage if system_info.cpu_usage else 0 }}%</span>
</div> </div>
<div class="flex justify-between text-sm"> <div class="flex justify-between text-sm">
<span class="text-slate-600 dark:text-slate-400">RAM:</span> <span class="text-slate-600 dark:text-slate-400">RAM:</span>
<span class="text-slate-900 dark:text-white font-medium">{{ system_info.memory_usage }}%</span> <span class="text-slate-900 dark:text-white font-medium">{{ system_info.memory_usage if system_info.memory_usage else 0 }}%</span>
</div> </div>
</div> </div>
</div> </div>
@@ -510,11 +510,11 @@
<div class="space-y-2"> <div class="space-y-2">
<div class="flex justify-between text-sm"> <div class="flex justify-between text-sm">
<span class="text-slate-600 dark:text-slate-400">Größe:</span> <span class="text-slate-600 dark:text-slate-400">Größe:</span>
<span class="text-slate-900 dark:text-white font-medium">{{ system_info.db_size }}</span> <span class="text-slate-900 dark:text-white font-medium">{{ system_info.db_size if system_info.db_size else 'Unbekannt' }}</span>
</div> </div>
<div class="flex justify-between text-sm"> <div class="flex justify-between text-sm">
<span class="text-slate-600 dark:text-slate-400">Verbindungen:</span> <span class="text-slate-600 dark:text-slate-400">Verbindungen:</span>
<span class="text-slate-900 dark:text-white font-medium">{{ system_info.db_connections }}</span> <span class="text-slate-900 dark:text-white font-medium">{{ system_info.db_connections if system_info.db_connections else 'Unbekannt' }}</span>
</div> </div>
</div> </div>
</div> </div>
@@ -530,11 +530,11 @@
<div class="space-y-2"> <div class="space-y-2">
<div class="flex justify-between text-sm"> <div class="flex justify-between text-sm">
<span class="text-slate-600 dark:text-slate-400">Jobs:</span> <span class="text-slate-600 dark:text-slate-400">Jobs:</span>
<span class="text-slate-900 dark:text-white font-medium">{{ system_info.scheduler_jobs }}</span> <span class="text-slate-900 dark:text-white font-medium">{{ system_info.scheduler_jobs if system_info.scheduler_jobs else 0 }}</span>
</div> </div>
<div class="flex justify-between text-sm"> <div class="flex justify-between text-sm">
<span class="text-slate-600 dark:text-slate-400">Nächster Job:</span> <span class="text-slate-600 dark:text-slate-400">Nächster Job:</span>
<span class="text-slate-900 dark:text-white font-medium">{{ system_info.next_job }}</span> <span class="text-slate-900 dark:text-white font-medium">{{ system_info.next_job if system_info.next_job else 'Keine geplanten Jobs' }}</span>
</div> </div>
</div> </div>
</div> </div>
@@ -545,13 +545,13 @@
<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="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">
<h3 class="text-lg font-semibold text-slate-900 dark:text-white mb-4">Wartung</h3> <h3 class="text-lg font-semibold text-slate-900 dark:text-white mb-4">Wartung</h3>
<div class="space-y-3"> <div class="space-y-3">
<button class="w-full px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors text-sm font-medium"> <button onclick="clearCache()" class="w-full px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors text-sm font-medium">
Cache leeren Cache leeren
</button> </button>
<button class="w-full px-4 py-2 bg-yellow-500 text-white rounded-lg hover:bg-yellow-600 transition-colors text-sm font-medium"> <button onclick="optimizeDatabase()" class="w-full px-4 py-2 bg-yellow-500 text-white rounded-lg hover:bg-yellow-600 transition-colors text-sm font-medium">
Datenbank optimieren Datenbank optimieren
</button> </button>
<button class="w-full px-4 py-2 bg-green-500 text-white rounded-lg hover:bg-green-600 transition-colors text-sm font-medium"> <button onclick="createBackup()" class="w-full px-4 py-2 bg-green-500 text-white rounded-lg hover:bg-green-600 transition-colors text-sm font-medium">
Backup erstellen Backup erstellen
</button> </button>
</div> </div>
@@ -560,13 +560,13 @@
<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="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">
<h3 class="text-lg font-semibold text-slate-900 dark:text-white mb-4">Konfiguration</h3> <h3 class="text-lg font-semibold text-slate-900 dark:text-white mb-4">Konfiguration</h3>
<div class="space-y-3"> <div class="space-y-3">
<button class="w-full px-4 py-2 bg-purple-500 text-white rounded-lg hover:bg-purple-600 transition-colors text-sm font-medium"> <button onclick="editSettings()" class="w-full px-4 py-2 bg-purple-500 text-white rounded-lg hover:bg-purple-600 transition-colors text-sm font-medium">
Einstellungen bearbeiten Einstellungen bearbeiten
</button> </button>
<button class="w-full px-4 py-2 bg-indigo-500 text-white rounded-lg hover:bg-indigo-600 transition-colors text-sm font-medium"> <button onclick="updatePrinters()" class="w-full px-4 py-2 bg-indigo-500 text-white rounded-lg hover:bg-indigo-600 transition-colors text-sm font-medium">
Drucker aktualisieren Drucker aktualisieren
</button> </button>
<button class="w-full px-4 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors text-sm font-medium"> <button onclick="restartSystem()" class="w-full px-4 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors text-sm font-medium">
System neustarten System neustarten
</button> </button>
</div> </div>
@@ -651,28 +651,158 @@
</div> </div>
<script> <script>
// Admin Panel JavaScript // CSRF Token für AJAX-Anfragen
document.addEventListener('DOMContentLoaded', function() { const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
// Auto-refresh für Live-Daten
setInterval(function() { // Hilfsfunktion für API-Aufrufe
// Hier können Sie AJAX-Calls für Live-Updates hinzufügen async function makeApiCall(url, method = 'GET', data = null) {
updateStats(); const options = {
}, 30000); // Alle 30 Sekunden method: method,
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrfToken
}
};
function updateStats() { if (data) {
// Implementierung für Live-Stats Updates options.body = JSON.stringify(data);
console.log('Updating stats...');
} }
// Smooth Scrolling für bessere UX try {
document.querySelectorAll('a[href^="#"]').forEach(anchor => { const response = await fetch(url, options);
anchor.addEventListener('click', function (e) { const result = await response.json();
e.preventDefault();
document.querySelector(this.getAttribute('href')).scrollIntoView({ if (response.ok) {
behavior: 'smooth' 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?')) {
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.')) {
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?')) {
const result = await makeApiCall('/api/admin/backup/create', 'POST');
}
}
// Drucker aktualisieren
async function updatePrinters() {
if (confirm('Möchten Sie alle Drucker-Verbindungen aktualisieren?')) {
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
const elements = {
'cpu_usage': result.cpu_usage + '%',
'memory_usage': result.memory_usage + '%',
'disk_usage': result.disk_usage + '%',
'uptime': result.uptime,
'db_size': result.db_size,
'scheduler_jobs': result.scheduler_jobs,
'next_job': result.next_job
};
Object.keys(elements).forEach(key => {
const element = document.querySelector(`[data-status="${key}"]`);
if (element) {
element.textContent = elements[key];
}
}); });
}); }
}); }
}
// Auto-Update alle 30 Sekunden
setInterval(updateSystemStatus, 30000);
// Initial load
document.addEventListener('DOMContentLoaded', function() {
updateSystemStatus();
}); });
</script> </script>
{% endblock %} {% endblock %}

View File

@@ -446,16 +446,77 @@
} }
} }
// Refresh printers // Refresh printers mit Status-Check
function refreshPrinters() { function refreshPrinters() {
const grid = document.getElementById('printers-grid'); const grid = document.getElementById('printers-grid');
const refreshBtn = document.querySelector('button[onclick="refreshPrinters()"]');
// Button deaktivieren und Loading-State anzeigen
if (refreshBtn) {
refreshBtn.disabled = true;
refreshBtn.innerHTML = `
<svg class="animate-spin h-4 w-4 sm:h-5 sm:w-5 mr-1 sm:mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" class="opacity-25"></circle>
<path fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" class="opacity-75"></path>
</svg>
Überprüfe Status...
`;
}
// Loading-State im Grid anzeigen
grid.innerHTML = ` grid.innerHTML = `
<div class="col-span-full text-center py-6 sm:py-12"> <div class="col-span-full text-center py-6 sm:py-12">
<div class="animate-spin rounded-full h-8 w-8 sm:h-12 sm:w-12 border-b-2 border-indigo-600 dark:border-indigo-400 mx-auto"></div> <div class="animate-spin rounded-full h-8 w-8 sm:h-12 sm:w-12 border-b-2 border-indigo-600 dark:border-indigo-400 mx-auto"></div>
<p class="mt-3 sm:mt-4 text-sm sm:text-base text-slate-600 dark:text-slate-400">Lade Drucker...</p> <p class="mt-3 sm:mt-4 text-sm sm:text-base text-slate-600 dark:text-slate-400">Überprüfe Drucker-Status...</p>
<p class="mt-1 text-xs text-slate-500 dark:text-slate-500">Dies kann bis zu 7 Sekunden dauern</p>
</div> </div>
`; `;
loadPrinters();
// Drucker laden mit Status-Check
loadPrintersWithStatusCheck().finally(() => {
// Button wieder aktivieren
if (refreshBtn) {
refreshBtn.disabled = false;
refreshBtn.innerHTML = `
<svg class="h-4 w-4 sm:h-5 sm:w-5 mr-1 sm:mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
Aktualisieren
`;
}
});
}
// Erweiterte Funktion zum Laden der Drucker mit Status-Check
async function loadPrintersWithStatusCheck() {
try {
const response = await fetch('/api/printers/status');
if (!response.ok) {
throw new Error('Fehler beim Laden der Drucker-Status');
}
const statusData = await response.json();
// Drucker-Daten mit Status-Informationen anreichern
printers = statusData.map(printer => ({
...printer,
status: printer.status === 'online' ? 'available' : 'offline'
}));
renderPrinters();
// Erfolgs-Nachricht anzeigen
const onlineCount = printers.filter(p => p.status === 'available').length;
const totalCount = printers.length;
showStatusMessage(
`Status-Check abgeschlossen: ${onlineCount} von ${totalCount} Drucker online`,
onlineCount === totalCount ? 'success' : 'info'
);
} catch (error) {
console.error('Error loading printer status:', error);
showError('Fehler beim Überprüfen der Drucker-Status');
}
} }
// Initialize // Initialize