"feat: Update admin and printer templates with new design"
This commit is contained in:
parent
e122fe0cc9
commit
2406aedf38
@ -1,15 +1,21 @@
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
import threading
|
||||
import time
|
||||
import subprocess
|
||||
import socket
|
||||
import json
|
||||
import secrets
|
||||
import subprocess
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Dict, List, Optional, Tuple, Any, Union
|
||||
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_login import LoginManager, UserMixin, login_user, logout_user, current_user, login_required
|
||||
from flask import Flask, render_template, request, redirect, url_for, flash, jsonify, session, send_file, Response
|
||||
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
|
||||
from PyP100 import PyP110
|
||||
@ -119,6 +125,82 @@ def job_owner_required(f):
|
||||
return f(job_id, *args, **kwargs)
|
||||
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
|
||||
@app.route("/")
|
||||
def index():
|
||||
@ -216,12 +298,18 @@ def admin_page():
|
||||
# Benutzeranzahl
|
||||
stats["total_users"] = db_session.query(User).count()
|
||||
|
||||
# Druckeranzahl
|
||||
stats["total_printers"] = db_session.query(Printer).count()
|
||||
# Druckeranzahl und Online-Status
|
||||
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(
|
||||
Job.status.in_(["scheduled", "running"])
|
||||
Job.status.in_(["printing", "running"])
|
||||
).count()
|
||||
|
||||
stats["queued_jobs"] = db_session.query(Job).filter(
|
||||
Job.status == "scheduled"
|
||||
).count()
|
||||
|
||||
# Erfolgsrate
|
||||
@ -259,11 +347,55 @@ def admin_page():
|
||||
# System-Informationen laden
|
||||
if active_tab == 'system':
|
||||
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 = {
|
||||
"cpu": psutil.cpu_percent(),
|
||||
"memory": psutil.virtual_memory().percent,
|
||||
"disk": psutil.disk_usage('/').percent,
|
||||
"uptime": get_system_uptime_days()
|
||||
"cpu_usage": round(cpu_percent, 1),
|
||||
"memory_usage": round(memory.percent, 1),
|
||||
"disk_usage": round((disk.used / disk.total) * 100, 1),
|
||||
"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
|
||||
@ -586,36 +718,58 @@ def finish_job(job_id):
|
||||
@app.route("/api/printers", methods=["GET"])
|
||||
@login_required
|
||||
def get_printers():
|
||||
"""Gibt alle Drucker mit aktuellem Status zurück (mit Ping-Check und 7-Sekunden-Timeout)."""
|
||||
db_session = get_db_session()
|
||||
|
||||
try:
|
||||
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 = []
|
||||
for printer in printers:
|
||||
# Bestimme Status basierend auf hardkodierten Druckern
|
||||
printer_config = PRINTERS.get(printer.name)
|
||||
if printer_config:
|
||||
status = "available" # Drucker verfügbar
|
||||
active = True
|
||||
if printer.id in status_results:
|
||||
status, active = status_results[printer.id]
|
||||
# Mapping für Frontend-Kompatibilität
|
||||
if status == "online":
|
||||
frontend_status = "available"
|
||||
else:
|
||||
frontend_status = "offline"
|
||||
else:
|
||||
status = "offline"
|
||||
# Fallback falls kein Ergebnis vorliegt
|
||||
frontend_status = "offline"
|
||||
active = False
|
||||
|
||||
# Aktualisiere Status in der Datenbank
|
||||
printer.status = status
|
||||
# Status in der Datenbank aktualisieren
|
||||
printer.status = frontend_status
|
||||
printer.active = active
|
||||
|
||||
printer_data = printer.to_dict()
|
||||
printer_data["status"] = status
|
||||
printer_data["status"] = frontend_status
|
||||
printer_data["active"] = active
|
||||
printer_data["last_checked"] = datetime.now().isoformat()
|
||||
printer_list.append(printer_data)
|
||||
|
||||
# Speichere Updates
|
||||
db_session.commit()
|
||||
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({
|
||||
"printers": printer_list
|
||||
})
|
||||
@ -739,26 +893,35 @@ def get_activity():
|
||||
@app.route("/api/printers/status", methods=["GET"])
|
||||
@login_required
|
||||
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()
|
||||
try:
|
||||
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 = []
|
||||
for printer in printers:
|
||||
# Bestimme Status basierend auf IP-Adresse aus der Konfiguration
|
||||
printer_config = PRINTERS.get(printer.name)
|
||||
if printer_config:
|
||||
# Drucker ist in der Konfiguration -> als online betrachten
|
||||
status = "online"
|
||||
active = True
|
||||
if printer.id in status_results:
|
||||
status, active = status_results[printer.id]
|
||||
else:
|
||||
# Drucker nicht in Konfiguration -> offline
|
||||
status = "offline"
|
||||
active = False
|
||||
# Fallback falls kein Ergebnis vorliegt
|
||||
status, active = "offline", False
|
||||
|
||||
# Aktualisiere den Status in der Datenbank für Konsistenz
|
||||
# Status in der Datenbank aktualisieren
|
||||
printer.status = status
|
||||
printer.active = active
|
||||
|
||||
@ -768,13 +931,16 @@ def get_printers_status():
|
||||
"status": status,
|
||||
"active": active,
|
||||
"ip_address": printer.ip_address,
|
||||
"location": printer.location
|
||||
"location": printer.location,
|
||||
"last_checked": datetime.now().isoformat()
|
||||
})
|
||||
|
||||
# Speichere die aktualisierten Status
|
||||
db_session.commit()
|
||||
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)
|
||||
except Exception as e:
|
||||
db_session.rollback()
|
||||
|
@ -442,7 +442,7 @@
|
||||
{% if job.status == 'queued' %}
|
||||
<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">
|
||||
<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>
|
||||
</button>
|
||||
{% endif %}
|
||||
@ -486,15 +486,15 @@
|
||||
<div class="space-y-2">
|
||||
<div class="flex justify-between text-sm">
|
||||
<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 class="flex justify-between text-sm">
|
||||
<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 class="flex justify-between text-sm">
|
||||
<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>
|
||||
@ -510,11 +510,11 @@
|
||||
<div class="space-y-2">
|
||||
<div class="flex justify-between text-sm">
|
||||
<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 class="flex justify-between text-sm">
|
||||
<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>
|
||||
@ -530,11 +530,11 @@
|
||||
<div class="space-y-2">
|
||||
<div class="flex justify-between text-sm">
|
||||
<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 class="flex justify-between text-sm">
|
||||
<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>
|
||||
@ -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">
|
||||
<h3 class="text-lg font-semibold text-slate-900 dark:text-white mb-4">Wartung</h3>
|
||||
<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
|
||||
</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
|
||||
</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
|
||||
</button>
|
||||
</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">
|
||||
<h3 class="text-lg font-semibold text-slate-900 dark:text-white mb-4">Konfiguration</h3>
|
||||
<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
|
||||
</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
|
||||
</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
|
||||
</button>
|
||||
</div>
|
||||
@ -651,28 +651,158 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Admin Panel JavaScript
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Auto-refresh für Live-Daten
|
||||
setInterval(function() {
|
||||
// Hier können Sie AJAX-Calls für Live-Updates hinzufügen
|
||||
updateStats();
|
||||
}, 30000); // Alle 30 Sekunden
|
||||
// CSRF Token für AJAX-Anfragen
|
||||
const csrfToken = document.querySelector('meta[name="csrf-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': csrfToken
|
||||
}
|
||||
};
|
||||
|
||||
function updateStats() {
|
||||
// Implementierung für Live-Stats Updates
|
||||
console.log('Updating stats...');
|
||||
if (data) {
|
||||
options.body = JSON.stringify(data);
|
||||
}
|
||||
|
||||
// Smooth Scrolling für bessere UX
|
||||
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
||||
anchor.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
document.querySelector(this.getAttribute('href')).scrollIntoView({
|
||||
behavior: 'smooth'
|
||||
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?')) {
|
||||
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>
|
||||
{% endblock %}
|
@ -446,16 +446,77 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh printers
|
||||
// Refresh printers mit Status-Check
|
||||
function refreshPrinters() {
|
||||
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 = `
|
||||
<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>
|
||||
<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>
|
||||
`;
|
||||
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
|
||||
|
Loading…
x
Reference in New Issue
Block a user