Remove deprecated backend files and documentation, including Docker configurations, environment variables, and various scripts, to streamline the project structure and eliminate unused components.

This commit is contained in:
2025-05-24 17:47:05 +02:00
parent d2f23d589a
commit ead75ae451
98 changed files with 3917 additions and 35610 deletions

View File

@ -1,169 +0,0 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}MYP API Tester{% endblock %}</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap.css') }}">
<style>
.sidebar {
min-height: calc(100vh - 56px);
background-color: #f8f9fa;
}
.api-response {
max-height: 300px;
overflow-y: auto;
font-family: monospace;
background-color: #f5f5f5;
padding: 10px;
border-radius: 4px;
}
.nav-link.active {
background-color: #0d6efd;
color: white !important;
}
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="/">MYP API Tester</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link {% if active_page == 'home' %}active{% endif %}" href="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link {% if active_page == 'printers' %}active{% endif %}" href="/admin/printers">Drucker</a>
</li>
<li class="nav-item">
<a class="nav-link {% if active_page == 'jobs' %}active{% endif %}" href="/admin/jobs">Druckaufträge</a>
</li>
<li class="nav-item">
<a class="nav-link {% if active_page == 'users' %}active{% endif %}" href="/admin/users">Benutzer</a>
</li>
<li class="nav-item">
<a class="nav-link {% if active_page == 'stats' %}active{% endif %}" href="/admin/stats">Statistiken</a>
</li>
</ul>
<ul class="navbar-nav ms-auto">
{% if current_user %}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="userDropdown" role="button" data-bs-toggle="dropdown">
{{ current_user.username }}
</a>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="/logout">Abmelden</a></li>
</ul>
</li>
{% else %}
<li class="nav-item">
<a class="nav-link" href="/login">Anmelden</a>
</li>
{% endif %}
</ul>
</div>
</div>
</nav>
<div class="container-fluid py-3">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}" role="alert">
{{ message }}
</div>
{% endfor %}
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
</div>
<script src="{{ url_for('static', filename='js/bootstrap.bundle.js') }}"></script>
<script>
function formatJson(jsonString) {
try {
const obj = JSON.parse(jsonString);
return JSON.stringify(obj, null, 2);
} catch (e) {
return jsonString;
}
}
document.addEventListener('DOMContentLoaded', function() {
// Format all response areas
document.querySelectorAll('.api-response').forEach(function(el) {
if (el.textContent) {
el.textContent = formatJson(el.textContent);
}
});
// Add event listener to show response areas
document.querySelectorAll('.api-form').forEach(function(form) {
form.addEventListener('submit', async function(e) {
e.preventDefault();
const url = this.getAttribute('data-url');
const method = this.getAttribute('data-method') || 'GET';
const responseArea = document.getElementById(this.getAttribute('data-response'));
const formData = new FormData(this);
const data = {};
formData.forEach((value, key) => {
if (value) {
try {
// Try to parse as JSON if it looks like JSON
if (value.trim().startsWith('{') || value.trim().startsWith('[')) {
data[key] = JSON.parse(value);
} else {
data[key] = value;
}
} catch (e) {
data[key] = value;
}
}
});
const options = {
method: method,
headers: {
'Content-Type': 'application/json'
},
credentials: 'same-origin'
};
if (method !== 'GET' && method !== 'HEAD') {
options.body = JSON.stringify(data);
}
try {
responseArea.textContent = 'Sending request...';
const response = await fetch(url, options);
const responseText = await response.text();
try {
const formatted = formatJson(responseText);
responseArea.textContent = formatted;
} catch (e) {
responseArea.textContent = responseText;
}
if (this.hasAttribute('data-reload') && response.ok) {
setTimeout(() => {
window.location.reload();
}, 1000);
}
} catch (err) {
responseArea.textContent = 'Error: ' + err.message;
}
});
});
});
</script>
{% block scripts %}{% endblock %}
</body>
</html>

View File

@ -1,304 +0,0 @@
{% extends "base.html" %}
{% block title %}Dashboard - MYP API Tester{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-12 mb-4">
<div class="card">
<div class="card-header">
<h4 class="mb-0">Willkommen, {{ current_user.display_name }}</h4>
</div>
<div class="card-body">
<p>Benutzerdetails:</p>
<ul>
<li><strong>ID:</strong> {{ current_user.id }}</li>
<li><strong>Benutzername:</strong> {{ current_user.username }}</li>
<li><strong>E-Mail:</strong> {{ current_user.email or "Nicht angegeben" }}</li>
<li><strong>Rolle:</strong> {{ current_user.role }}</li>
</ul>
<div class="mt-3">
<a href="/admin/printers" class="btn btn-primary me-2">Drucker verwalten</a>
<a href="/admin/jobs" class="btn btn-success me-2">Druckaufträge verwalten</a>
{% if current_user.role == 'admin' %}
<a href="/admin/users" class="btn btn-info me-2">Benutzer verwalten</a>
<a href="/admin/stats" class="btn btn-secondary">Statistiken</a>
{% endif %}
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-4">
<div class="card">
<div class="card-header">
<h5 class="mb-0">Aktive Druckaufträge</h5>
</div>
<div class="card-body">
<form class="api-form mb-3" data-url="/api/jobs" data-method="GET" data-response="jobsResponse">
<button type="submit" class="btn btn-primary">Aktualisieren</button>
</form>
<div id="activeJobsContainer">
<div class="alert alert-info">Lade Druckaufträge...</div>
</div>
<div class="d-none">
<h6>API-Antwort:</h6>
<pre class="api-response" id="jobsResponse"></pre>
</div>
</div>
</div>
</div>
<div class="col-md-6 mb-4">
<div class="card">
<div class="card-header">
<h5 class="mb-0">Verfügbare Drucker</h5>
</div>
<div class="card-body">
<form class="api-form mb-3" data-url="/api/printers" data-method="GET" data-response="printersResponse">
<button type="submit" class="btn btn-primary">Aktualisieren</button>
</form>
<div id="availablePrintersContainer">
<div class="alert alert-info">Lade Drucker...</div>
</div>
<div class="d-none">
<h6>API-Antwort:</h6>
<pre class="api-response" id="printersResponse"></pre>
</div>
</div>
</div>
</div>
</div>
<!-- Job freischalten Modal -->
<div class="modal fade" id="approveJobModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Druckauftrag freischalten</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Möchten Sie diesen Druckauftrag jetzt freischalten und starten?</p>
<p><strong>Hinweis:</strong> Der Drucker muss verfügbar sein, damit der Auftrag gestartet werden kann.</p>
<form id="approveJobForm" class="api-form" data-method="POST" data-response="approveJobResponse" data-reload="true">
<input type="hidden" id="approveJobId" name="jobId">
</form>
<div class="mt-3">
<h6>Antwort:</h6>
<pre class="api-response" id="approveJobResponse"></pre>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
<button type="submit" form="approveJobForm" class="btn btn-success">Freischalten</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
document.addEventListener('DOMContentLoaded', function() {
// Aufträge und Drucker laden
document.querySelector('form[data-url="/api/jobs"]').dispatchEvent(new Event('submit'));
document.querySelector('form[data-url="/api/printers"]').dispatchEvent(new Event('submit'));
// Tabellen aktualisieren, wenn Daten geladen werden
const jobsResponse = document.getElementById('jobsResponse');
const printersResponse = document.getElementById('printersResponse');
// Observer für Jobs
const jobsObserver = new MutationObserver(function(mutations) {
try {
const jobs = JSON.parse(jobsResponse.textContent);
updateActiveJobs(jobs);
} catch (e) {
console.error('Fehler beim Parsen der Auftrags-Daten:', e);
}
});
jobsObserver.observe(jobsResponse, { childList: true, characterData: true, subtree: true });
// Observer für Drucker
const printersObserver = new MutationObserver(function(mutations) {
try {
const printers = JSON.parse(printersResponse.textContent);
updateAvailablePrinters(printers);
} catch (e) {
console.error('Fehler beim Parsen der Drucker-Daten:', e);
}
});
printersObserver.observe(printersResponse, { childList: true, characterData: true, subtree: true });
// Approve-Modal vorbereiten
document.getElementById('approveJobModal').addEventListener('show.bs.modal', function(event) {
const button = event.relatedTarget;
const jobId = button.getAttribute('data-job-id');
document.getElementById('approveJobId').value = jobId;
document.getElementById('approveJobForm').setAttribute('data-url', `/api/jobs/${jobId}/approve`);
});
// Automatische Aktualisierung alle 60 Sekunden
setInterval(() => {
document.querySelector('form[data-url="/api/jobs"]').dispatchEvent(new Event('submit'));
document.querySelector('form[data-url="/api/printers"]').dispatchEvent(new Event('submit'));
}, 60000);
});
function updateActiveJobs(jobs) {
const container = document.getElementById('activeJobsContainer');
// Filter für aktive und wartende Jobs
const activeJobs = jobs.filter(job => !job.aborted && job.remainingMinutes > 0 && !job.waitingApproval);
const waitingJobs = jobs.filter(job => !job.aborted && job.waitingApproval);
if (activeJobs.length === 0 && waitingJobs.length === 0) {
container.innerHTML = '<div class="alert alert-info">Keine aktiven Druckaufträge vorhanden.</div>';
return;
}
let html = '';
// Aktive Jobs anzeigen
if (activeJobs.length > 0) {
html += '<h6 class="mt-3">Laufende Aufträge</h6>';
html += '<div class="list-group mb-3">';
activeJobs.forEach(job => {
// Prozentsatz der abgelaufenen Zeit berechnen
const totalDuration = job.durationInMinutes;
const elapsed = totalDuration - job.remainingMinutes;
const percentage = Math.round((elapsed / totalDuration) * 100);
html += `
<div class="list-group-item">
<div class="d-flex justify-content-between">
<div>
<strong>Job ${job.id.substring(0, 8)}...</strong> (${job.durationInMinutes} Min)
<div class="small text-muted">Verbleibend: ${job.remainingMinutes} Min</div>
</div>
<div>
<span class="badge bg-warning">Aktiv</span>
</div>
</div>
<div class="progress mt-2" style="height: 10px;">
<div class="progress-bar progress-bar-striped progress-bar-animated"
role="progressbar"
style="width: ${percentage}%;"
aria-valuenow="${percentage}"
aria-valuemin="0"
aria-valuemax="100">
${percentage}%
</div>
</div>
</div>
`;
});
html += '</div>';
}
// Wartende Jobs anzeigen
if (waitingJobs.length > 0) {
html += '<h6 class="mt-3">Wartende Aufträge</h6>';
html += '<div class="list-group">';
waitingJobs.forEach(job => {
html += `
<div class="list-group-item">
<div class="d-flex justify-content-between">
<div>
<strong>Job ${job.id.substring(0, 8)}...</strong> (${job.durationInMinutes} Min)
<div class="small text-muted">Drucker: ${job.socketId.substring(0, 8)}...</div>
</div>
<div>
<span class="badge bg-info">Wartet</span>
</div>
</div>
<div class="mt-2">
<button type="button" class="btn btn-sm btn-success"
data-bs-toggle="modal"
data-bs-target="#approveJobModal"
data-job-id="${job.id}">
Freischalten
</button>
</div>
</div>
`;
});
html += '</div>';
}
container.innerHTML = html;
}
function updateAvailablePrinters(printers) {
const container = document.getElementById('availablePrintersContainer');
// Filter für verfügbare Drucker
const availablePrinters = printers.filter(printer => printer.status === 0);
if (availablePrinters.length === 0) {
container.innerHTML = '<div class="alert alert-warning">Keine verfügbaren Drucker gefunden.</div>';
return;
}
let html = '<div class="list-group">';
availablePrinters.forEach(printer => {
html += `
<div class="list-group-item">
<div class="d-flex justify-content-between align-items-center">
<div>
<strong>${printer.name}</strong>
<div class="small text-muted">${printer.description}</div>
</div>
<div>
<span class="badge bg-success">Verfügbar</span>
</div>
</div>
<div class="mt-2">
<a href="/admin/jobs" class="btn btn-sm btn-primary">Auftrag erstellen</a>
</div>
</div>
`;
});
html += '</div>';
container.innerHTML = html;
// Gesamtstatistik hinzufügen
const busyPrinters = printers.filter(printer => printer.status === 1).length;
const totalPrinters = printers.length;
if (totalPrinters > 0) {
const statsHtml = `
<div class="mt-3">
<div class="d-flex justify-content-between">
<small>Verfügbar: ${availablePrinters.length} / ${totalPrinters}</small>
<small>Belegt: ${busyPrinters} / ${totalPrinters}</small>
</div>
<div class="progress mt-1" style="height: 5px;">
<div class="progress-bar bg-success" style="width: ${(availablePrinters.length / totalPrinters) * 100}%"></div>
<div class="progress-bar bg-warning" style="width: ${(busyPrinters / totalPrinters) * 100}%"></div>
</div>
</div>
`;
container.innerHTML += statsHtml;
}
}
</script>
{% endblock %}

View File

@ -1,443 +0,0 @@
{% extends "base.html" %}
{% block title %}Druckaufträge - MYP API Tester{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-12 mb-4">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h4 class="mb-0">Druckaufträge verwalten</h4>
<button class="btn btn-primary" type="button" data-bs-toggle="collapse" data-bs-target="#newJobForm">
Neuen Auftrag erstellen
</button>
</div>
<div class="collapse" id="newJobForm">
<div class="card-body border-bottom">
<form class="api-form" data-url="/api/jobs" data-method="POST" data-response="createJobResponse" data-reload="true">
<div class="mb-3">
<label for="jobPrinterId" class="form-label">Drucker</label>
<select class="form-control" id="jobPrinterId" name="printerId" required>
<option value="">Drucker auswählen...</option>
<!-- Wird dynamisch gefüllt -->
</select>
</div>
<div class="mb-3">
<label for="jobDuration" class="form-label">Dauer (Minuten)</label>
<input type="number" class="form-control" id="jobDuration" name="durationInMinutes" min="1" required>
</div>
<div class="mb-3">
<label for="jobComments" class="form-label">Kommentare</label>
<textarea class="form-control" id="jobComments" name="comments" rows="3"></textarea>
</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="allowQueuedJobs" name="allowQueuedJobs" value="true">
<label class="form-check-label" for="allowQueuedJobs">
Auftrag in Warteschlange erlauben (wenn Drucker belegt ist)
</label>
</div>
<button type="submit" class="btn btn-success">Auftrag erstellen</button>
</form>
<div class="mt-3">
<h6>Antwort:</h6>
<pre class="api-response" id="createJobResponse"></pre>
</div>
</div>
</div>
<div class="card-body">
<form class="api-form mb-3" data-url="/api/jobs" data-method="GET" data-response="jobsResponse">
<button type="submit" class="btn btn-primary">Aufträge aktualisieren</button>
</form>
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>ID</th>
<th>Drucker</th>
<th>Benutzer</th>
<th>Start</th>
<th>Dauer (Min)</th>
<th>Verbleibend (Min)</th>
<th>Status</th>
<th>Kommentare</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody id="jobsTableBody">
<!-- Wird dynamisch gefüllt -->
</tbody>
</table>
</div>
<div>
<h6>API-Antwort:</h6>
<pre class="api-response" id="jobsResponse"></pre>
</div>
</div>
</div>
</div>
</div>
<!-- Job abbrechen Modal -->
<div class="modal fade" id="abortJobModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Auftrag abbrechen</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Möchten Sie den Auftrag wirklich abbrechen?</p>
<form id="abortJobForm" class="api-form" data-method="POST" data-response="abortJobResponse" data-reload="true">
<input type="hidden" id="abortJobId" name="jobId">
<div class="mb-3">
<label for="abortReason" class="form-label">Abbruchgrund</label>
<textarea class="form-control" id="abortReason" name="reason" rows="3"></textarea>
</div>
</form>
<div class="mt-3">
<h6>Antwort:</h6>
<pre class="api-response" id="abortJobResponse"></pre>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
<button type="submit" form="abortJobForm" class="btn btn-danger">Auftrag abbrechen</button>
</div>
</div>
</div>
</div>
<!-- Job beenden Modal -->
<div class="modal fade" id="finishJobModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Auftrag beenden</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Möchten Sie den Auftrag als beendet markieren?</p>
<form id="finishJobForm" class="api-form" data-method="POST" data-response="finishJobResponse" data-reload="true">
<input type="hidden" id="finishJobId" name="jobId">
</form>
<div class="mt-3">
<h6>Antwort:</h6>
<pre class="api-response" id="finishJobResponse"></pre>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
<button type="submit" form="finishJobForm" class="btn btn-success">Auftrag beenden</button>
</div>
</div>
</div>
</div>
<!-- Job verlängern Modal -->
<div class="modal fade" id="extendJobModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Auftrag verlängern</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="extendJobForm" class="api-form" data-method="POST" data-response="extendJobResponse" data-reload="true">
<input type="hidden" id="extendJobId" name="jobId">
<div class="mb-3">
<label for="extendHours" class="form-label">Stunden</label>
<input type="number" class="form-control" id="extendHours" name="hours" min="0" value="0">
</div>
<div class="mb-3">
<label for="extendMinutes" class="form-label">Minuten</label>
<input type="number" class="form-control" id="extendMinutes" name="minutes" min="0" max="59" value="30">
</div>
</form>
<div class="mt-3">
<h6>Antwort:</h6>
<pre class="api-response" id="extendJobResponse"></pre>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
<button type="submit" form="extendJobForm" class="btn btn-primary">Auftrag verlängern</button>
</div>
</div>
</div>
</div>
<!-- Job Kommentare bearbeiten Modal -->
<div class="modal fade" id="editCommentsModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Kommentare bearbeiten</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="editCommentsForm" class="api-form" data-method="PUT" data-response="editCommentsResponse" data-reload="true">
<input type="hidden" id="editCommentsJobId" name="jobId">
<div class="mb-3">
<label for="editJobComments" class="form-label">Kommentare</label>
<textarea class="form-control" id="editJobComments" name="comments" rows="3"></textarea>
</div>
</form>
<div class="mt-3">
<h6>Antwort:</h6>
<pre class="api-response" id="editCommentsResponse"></pre>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
<button type="submit" form="editCommentsForm" class="btn btn-primary">Speichern</button>
</div>
</div>
</div>
</div>
<!-- Job freischalten Modal -->
<div class="modal fade" id="approveJobModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Druckauftrag freischalten</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Möchten Sie diesen Druckauftrag jetzt freischalten und starten?</p>
<p><strong>Hinweis:</strong> Der Drucker muss verfügbar sein, damit der Auftrag gestartet werden kann.</p>
<form id="approveJobForm" class="api-form" data-method="POST" data-response="approveJobResponse" data-reload="true">
<input type="hidden" id="approveJobId" name="jobId">
</form>
<div class="mt-3">
<h6>Antwort:</h6>
<pre class="api-response" id="approveJobResponse"></pre>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
<button type="submit" form="approveJobForm" class="btn btn-success">Freischalten</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
document.addEventListener('DOMContentLoaded', function() {
// Drucker für Dropdown laden
loadPrinters();
// Aufträge laden
document.querySelector('form[data-url="/api/jobs"]').dispatchEvent(new Event('submit'));
// Tabelle aktualisieren, wenn Aufträge geladen werden
const jobsResponse = document.getElementById('jobsResponse');
const observer = new MutationObserver(function(mutations) {
try {
const jobs = JSON.parse(jobsResponse.textContent);
updateJobsTable(jobs);
} catch (e) {
console.error('Fehler beim Parsen der Auftrags-Daten:', e);
}
});
observer.observe(jobsResponse, { childList: true, characterData: true, subtree: true });
// Abort-Modal vorbereiten
document.getElementById('abortJobModal').addEventListener('show.bs.modal', function(event) {
const button = event.relatedTarget;
const jobId = button.getAttribute('data-job-id');
document.getElementById('abortJobId').value = jobId;
document.getElementById('abortJobForm').setAttribute('data-url', `/api/jobs/${jobId}/abort`);
});
// Finish-Modal vorbereiten
document.getElementById('finishJobModal').addEventListener('show.bs.modal', function(event) {
const button = event.relatedTarget;
const jobId = button.getAttribute('data-job-id');
document.getElementById('finishJobId').value = jobId;
document.getElementById('finishJobForm').setAttribute('data-url', `/api/jobs/${jobId}/finish`);
});
// Extend-Modal vorbereiten
document.getElementById('extendJobModal').addEventListener('show.bs.modal', function(event) {
const button = event.relatedTarget;
const jobId = button.getAttribute('data-job-id');
document.getElementById('extendJobId').value = jobId;
document.getElementById('extendJobForm').setAttribute('data-url', `/api/jobs/${jobId}/extend`);
});
// Edit-Comments-Modal vorbereiten
document.getElementById('editCommentsModal').addEventListener('show.bs.modal', function(event) {
const button = event.relatedTarget;
const jobId = button.getAttribute('data-job-id');
const comments = button.getAttribute('data-job-comments');
document.getElementById('editCommentsJobId').value = jobId;
document.getElementById('editCommentsForm').setAttribute('data-url', `/api/jobs/${jobId}/comments`);
document.getElementById('editJobComments').value = comments || '';
});
// Approve-Modal vorbereiten
document.getElementById('approveJobModal').addEventListener('show.bs.modal', function(event) {
const button = event.relatedTarget;
const jobId = button.getAttribute('data-job-id');
document.getElementById('approveJobId').value = jobId;
document.getElementById('approveJobForm').setAttribute('data-url', `/api/jobs/${jobId}/approve`);
});
});
async function loadPrinters() {
try {
const response = await fetch('/api/printers');
const printers = await response.json();
const selectElement = document.getElementById('jobPrinterId');
selectElement.innerHTML = '<option value="">Drucker auswählen...</option>';
// Drucker anzeigen (alle, da man jetzt auch für belegte Drucker Jobs erstellen kann)
printers.forEach(printer => {
const option = document.createElement('option');
option.value = printer.id;
// Status-Information zum Drucker hinzufügen
const statusText = printer.status === 0 ? '(Verfügbar)' : '(Belegt)';
option.textContent = `${printer.name} - ${printer.description} ${statusText}`;
// Belegte Drucker visuell unterscheiden
if (printer.status !== 0) {
option.classList.add('text-muted');
}
selectElement.appendChild(option);
});
// Hinweis auf die Checkbox für Warteschlange anzeigen oder verstecken
const allowQueuedJobsCheckbox = document.getElementById('allowQueuedJobs');
const queueCheckboxContainer = allowQueuedJobsCheckbox.closest('.form-check');
// Prüfen, ob es belegte Drucker gibt
const hasBusyPrinters = printers.some(printer => printer.status !== 0);
queueCheckboxContainer.style.display = hasBusyPrinters ? 'block' : 'none';
// Event-Listener für die Druckerauswahl hinzufügen
selectElement.addEventListener('change', function() {
const selectedPrinterId = this.value;
const selectedPrinter = printers.find(p => p.id === selectedPrinterId);
if (selectedPrinter && selectedPrinter.status !== 0) {
// Wenn ein belegter Drucker ausgewählt wird, Checkbox für Warteschlange anzeigen
queueCheckboxContainer.style.display = 'block';
allowQueuedJobsCheckbox.checked = true;
} else if (selectedPrinter && selectedPrinter.status === 0) {
// Wenn ein verfügbarer Drucker ausgewählt wird, Checkbox für Warteschlange verstecken
allowQueuedJobsCheckbox.checked = false;
}
});
} catch (e) {
console.error('Fehler beim Laden der Drucker:', e);
}
}
function updateJobsTable(jobs) {
const tableBody = document.getElementById('jobsTableBody');
tableBody.innerHTML = '';
jobs.forEach(job => {
const row = document.createElement('tr');
const startDate = new Date(job.startAt);
const formattedStart = startDate.toLocaleString();
const isActive = !job.aborted && job.remainingMinutes > 0 && !job.waitingApproval;
const isWaiting = !job.aborted && job.waitingApproval;
let statusText = '';
let statusClass = '';
if (job.aborted) {
statusText = 'Abgebrochen';
statusClass = 'text-danger';
} else if (job.waitingApproval) {
statusText = 'Wartet auf Freischaltung';
statusClass = 'text-info';
} else if (job.remainingMinutes <= 0) {
statusText = 'Abgeschlossen';
statusClass = 'text-success';
} else {
statusText = 'Aktiv';
statusClass = 'text-warning';
}
// Zeige die verbleibende Zeit an
const remainingTime = job.waitingApproval ? '-' : job.remainingMinutes;
row.innerHTML = `
<td>${job.id}</td>
<td>${job.printerId}</td>
<td>${job.userId}</td>
<td>${formattedStart}</td>
<td>${job.durationInMinutes}</td>
<td>${remainingTime}</td>
<td><span class="${statusClass}">${statusText}</span></td>
<td>${job.comments || '-'}</td>
<td>
${isActive ? `
<button type="button" class="btn btn-sm btn-danger mb-1"
data-bs-toggle="modal"
data-bs-target="#abortJobModal"
data-job-id="${job.id}">
Abbrechen
</button>
<button type="button" class="btn btn-sm btn-success mb-1"
data-bs-toggle="modal"
data-bs-target="#finishJobModal"
data-job-id="${job.id}">
Beenden
</button>
<button type="button" class="btn btn-sm btn-primary mb-1"
data-bs-toggle="modal"
data-bs-target="#extendJobModal"
data-job-id="${job.id}">
Verlängern
</button>
` : ''}
${isWaiting ? `
<button type="button" class="btn btn-sm btn-success mb-1"
data-bs-toggle="modal"
data-bs-target="#approveJobModal"
data-job-id="${job.id}">
Freischalten
</button>
<button type="button" class="btn btn-sm btn-danger mb-1"
data-bs-toggle="modal"
data-bs-target="#abortJobModal"
data-job-id="${job.id}">
Abbrechen
</button>
` : ''}
<button type="button" class="btn btn-sm btn-secondary mb-1"
data-bs-toggle="modal"
data-bs-target="#editCommentsModal"
data-job-id="${job.id}"
data-job-comments="${job.comments || ''}">
Kommentare
</button>
</td>
`;
tableBody.appendChild(row);
});
}
</script>
{% endblock %}

View File

@ -1,37 +0,0 @@
{% extends "base.html" %}
{% block title %}Anmelden - MYP API Tester{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h4 class="mb-0">Anmelden</h4>
</div>
<div class="card-body">
<form class="api-form" data-url="/auth/login" data-method="POST" data-response="loginResponse">
<div class="mb-3">
<label for="username" class="form-label">Benutzername</label>
<input type="text" class="form-control" id="username" name="username" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Passwort</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<button type="submit" class="btn btn-primary">Anmelden</button>
</form>
<div class="mt-3">
<p>Noch kein Konto? <a href="/register">Registrieren</a></p>
</div>
<div class="mt-3">
<h5>Antwort:</h5>
<pre class="api-response" id="loginResponse"></pre>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,119 +0,0 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MYP - Netzwerkkonfiguration</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
h1 {
color: #2c3e50;
margin-bottom: 20px;
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input[type="text"] {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
button {
background-color: #3498db;
color: white;
border: none;
padding: 10px 15px;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #2980b9;
}
.message {
padding: 10px;
margin: 10px 0;
border-radius: 4px;
}
.success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.error {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.status-box {
background-color: #f8f9fa;
border: 1px solid #ddd;
padding: 15px;
margin-top: 20px;
border-radius: 4px;
}
h2 {
color: #2c3e50;
font-size: 1.2em;
margin-top: 30px;
}
</style>
</head>
<body>
<h1>MYP - Netzwerkkonfiguration</h1>
{% if message %}
<div class="message {{ message_type }}">
{{ message }}
</div>
{% endif %}
<form method="POST" action="/admin/network-config">
<div class="form-group">
<label for="backend_hostname">Backend Hostname/IP:</label>
<input type="text" id="backend_hostname" name="backend_hostname" value="{{ config.backend_hostname }}" required>
</div>
<div class="form-group">
<label for="backend_port">Backend Port:</label>
<input type="text" id="backend_port" name="backend_port" value="{{ config.backend_port }}" required>
</div>
<div class="form-group">
<label for="frontend_hostname">Frontend Hostname/IP:</label>
<input type="text" id="frontend_hostname" name="frontend_hostname" value="{{ config.frontend_hostname }}" required>
</div>
<div class="form-group">
<label for="frontend_port">Frontend Port:</label>
<input type="text" id="frontend_port" name="frontend_port" value="{{ config.frontend_port }}" required>
</div>
<button type="submit">Konfiguration speichern</button>
</form>
<h2>Aktuelle Verbindungsstatus</h2>
<div class="status-box">
<p><strong>Backend-Status:</strong> {{ backend_status }}</p>
<p><strong>Frontend-Status:</strong> {{ frontend_status }}</p>
<p><strong>Letzte Prüfung:</strong> {{ last_check }}</p>
</div>
<h2>Netzwerkschnittstellen</h2>
<div class="status-box">
{% for interface in network_interfaces %}
<p><strong>{{ interface.name }}:</strong> {{ interface.address }}</p>
{% endfor %}
</div>
</body>
</html>

View File

@ -1,280 +0,0 @@
{% extends "base.html" %}
{% block title %}Drucker - MYP API Tester{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-12 mb-4">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h4 class="mb-0">Drucker verwalten</h4>
<button class="btn btn-primary" type="button" data-bs-toggle="collapse" data-bs-target="#newPrinterForm">
Neuen Drucker hinzufügen
</button>
</div>
<div class="collapse" id="newPrinterForm">
<div class="card-body border-bottom">
<form class="api-form" data-url="/api/printers" data-method="POST" data-response="createPrinterResponse" data-reload="true">
<div class="mb-3">
<label for="printerName" class="form-label">Name</label>
<input type="text" class="form-control" id="printerName" name="name" required>
</div>
<div class="mb-3">
<label for="printerDescription" class="form-label">Beschreibung</label>
<textarea class="form-control" id="printerDescription" name="description" rows="3" required></textarea>
</div>
<div class="mb-3">
<label for="printerStatus" class="form-label">Status</label>
<select class="form-control" id="printerStatus" name="status">
<option value="0">Verfügbar (0)</option>
<option value="1">Besetzt (1)</option>
<option value="2">Wartung (2)</option>
</select>
</div>
<div class="mb-3">
<label for="printerIpAddress" class="form-label">IP-Adresse (Tapo Steckdose)</label>
<input type="text" class="form-control" id="printerIpAddress" name="ipAddress" placeholder="z.B. 192.168.1.100">
</div>
<button type="submit" class="btn btn-success">Drucker erstellen</button>
</form>
<div class="mt-3">
<h6>Antwort:</h6>
<pre class="api-response" id="createPrinterResponse"></pre>
</div>
</div>
</div>
<div class="card-body">
<form class="api-form mb-3" data-url="/api/printers" data-method="GET" data-response="printersResponse">
<button type="submit" class="btn btn-primary">Drucker aktualisieren</button>
</form>
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Beschreibung</th>
<th>Status</th>
<th>IP-Adresse</th>
<th>Aktueller Job</th>
<th>Wartende Jobs</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody id="printersTableBody">
<!-- Wird dynamisch gefüllt -->
</tbody>
</table>
</div>
<div>
<h6>API-Antwort:</h6>
<pre class="api-response" id="printersResponse"></pre>
</div>
</div>
</div>
</div>
</div>
<!-- Drucker bearbeiten Modal -->
<div class="modal fade" id="editPrinterModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Drucker bearbeiten</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="editPrinterForm" class="api-form" data-method="PUT" data-response="editPrinterResponse" data-reload="true">
<input type="hidden" id="editPrinterId" name="printerId">
<div class="mb-3">
<label for="editPrinterName" class="form-label">Name</label>
<input type="text" class="form-control" id="editPrinterName" name="name" required>
</div>
<div class="mb-3">
<label for="editPrinterDescription" class="form-label">Beschreibung</label>
<textarea class="form-control" id="editPrinterDescription" name="description" rows="3" required></textarea>
</div>
<div class="mb-3">
<label for="editPrinterStatus" class="form-label">Status</label>
<select class="form-control" id="editPrinterStatus" name="status">
<option value="0">Verfügbar (0)</option>
<option value="1">Besetzt (1)</option>
<option value="2">Wartung (2)</option>
</select>
</div>
<div class="mb-3">
<label for="editPrinterIpAddress" class="form-label">IP-Adresse (Tapo Steckdose)</label>
<input type="text" class="form-control" id="editPrinterIpAddress" name="ipAddress" placeholder="z.B. 192.168.1.100">
</div>
</form>
<div class="mt-3">
<h6>Antwort:</h6>
<pre class="api-response" id="editPrinterResponse"></pre>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
<button type="submit" form="editPrinterForm" class="btn btn-primary">Änderungen speichern</button>
</div>
</div>
</div>
</div>
<!-- Drucker löschen Modal -->
<div class="modal fade" id="deletePrinterModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Drucker löschen</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Möchten Sie den Drucker <span id="deletePrinterName"></span> wirklich löschen?</p>
<form id="deletePrinterForm" class="api-form" data-method="DELETE" data-response="deletePrinterResponse" data-reload="true">
<input type="hidden" id="deletePrinterId" name="printerId">
</form>
<div class="mt-3">
<h6>Antwort:</h6>
<pre class="api-response" id="deletePrinterResponse"></pre>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
<button type="submit" form="deletePrinterForm" class="btn btn-danger">Löschen</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
document.addEventListener('DOMContentLoaded', function() {
// Drucker laden
document.querySelector('form[data-url="/api/printers"]').dispatchEvent(new Event('submit'));
// Tabelle aktualisieren, wenn Drucker geladen werden
const printersResponse = document.getElementById('printersResponse');
const observer = new MutationObserver(function(mutations) {
try {
const printers = JSON.parse(printersResponse.textContent);
updatePrintersTable(printers);
} catch (e) {
console.error('Fehler beim Parsen der Drucker-Daten:', e);
}
});
observer.observe(printersResponse, { childList: true, characterData: true, subtree: true });
// Edit-Modal vorbereiten
document.getElementById('editPrinterModal').addEventListener('show.bs.modal', function(event) {
const button = event.relatedTarget;
const printerId = button.getAttribute('data-printer-id');
const printerName = button.getAttribute('data-printer-name');
const printerDescription = button.getAttribute('data-printer-description');
const printerStatus = button.getAttribute('data-printer-status');
const printerIpAddress = button.getAttribute('data-printer-ip');
document.getElementById('editPrinterId').value = printerId;
document.getElementById('editPrinterForm').setAttribute('data-url', `/api/printers/${printerId}`);
document.getElementById('editPrinterName').value = printerName;
document.getElementById('editPrinterDescription').value = printerDescription;
document.getElementById('editPrinterStatus').value = printerStatus;
document.getElementById('editPrinterIpAddress').value = printerIpAddress || '';
});
// Delete-Modal vorbereiten
document.getElementById('deletePrinterModal').addEventListener('show.bs.modal', function(event) {
const button = event.relatedTarget;
const printerId = button.getAttribute('data-printer-id');
const printerName = button.getAttribute('data-printer-name');
document.getElementById('deletePrinterId').value = printerId;
document.getElementById('deletePrinterForm').setAttribute('data-url', `/api/printers/${printerId}`);
document.getElementById('deletePrinterName').textContent = printerName;
});
});
function updatePrintersTable(printers) {
const tableBody = document.getElementById('printersTableBody');
tableBody.innerHTML = '';
printers.forEach(printer => {
const row = document.createElement('tr');
const statusText = {
0: 'Verfügbar',
1: 'Besetzt',
2: 'Wartung'
}[printer.status] || 'Unbekannt';
const statusClass = {
0: 'text-success',
1: 'text-warning',
2: 'text-danger'
}[printer.status] || '';
// Informationen zum aktuellen Job
let currentJobInfo = '-';
if (printer.latestJob && printer.status === 1) {
// Verbleibende Zeit berechnen
const remainingTime = printer.latestJob.remainingMinutes || 0;
currentJobInfo = `
<div class="small">
<strong>ID:</strong> ${printer.latestJob.id.substring(0, 8)}...<br>
<strong>Dauer:</strong> ${printer.latestJob.durationInMinutes} Min<br>
<strong>Verbleibend:</strong> ${remainingTime} Min
</div>
`;
}
// Wartende Jobs anzeigen
let waitingJobsInfo = '-';
if (printer.waitingJobs && printer.waitingJobs.length > 0) {
const waitingJobsCount = printer.waitingJobs.length;
waitingJobsInfo = `
<div class="small">
<strong>${waitingJobsCount} Job${waitingJobsCount !== 1 ? 's' : ''} in Warteschlange</strong><br>
${printer.waitingJobs.map((job, index) =>
`<span>${index + 1}. Job ${job.id.substring(0, 8)}... (${job.durationInMinutes} Min)</span>`
).join('<br>')}
</div>
`;
}
row.innerHTML = `
<td>${printer.id}</td>
<td>${printer.name}</td>
<td>${printer.description}</td>
<td><span class="${statusClass}">${statusText} (${printer.status})</span></td>
<td>${printer.ipAddress || '-'}</td>
<td>${currentJobInfo}</td>
<td>${waitingJobsInfo}</td>
<td>
<button type="button" class="btn btn-sm btn-primary"
data-bs-toggle="modal"
data-bs-target="#editPrinterModal"
data-printer-id="${printer.id}"
data-printer-name="${printer.name}"
data-printer-description="${printer.description}"
data-printer-status="${printer.status}"
data-printer-ip="${printer.ipAddress || ''}">
Bearbeiten
</button>
<button type="button" class="btn btn-sm btn-danger"
data-bs-toggle="modal"
data-bs-target="#deletePrinterModal"
data-printer-id="${printer.id}"
data-printer-name="${printer.name}">
Löschen
</button>
</td>
`;
tableBody.appendChild(row);
});
}
</script>
{% endblock %}

View File

@ -1,45 +0,0 @@
{% extends "base.html" %}
{% block title %}Registrieren - MYP API Tester{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h4 class="mb-0">Registrieren</h4>
</div>
<div class="card-body">
<form class="api-form" data-url="/auth/register" data-method="POST" data-response="registerResponse">
<div class="mb-3">
<label for="username" class="form-label">Benutzername</label>
<input type="text" class="form-control" id="username" name="username" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Passwort</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<div class="mb-3">
<label for="displayName" class="form-label">Anzeigename</label>
<input type="text" class="form-control" id="displayName" name="displayName">
</div>
<div class="mb-3">
<label for="email" class="form-label">E-Mail</label>
<input type="email" class="form-control" id="email" name="email">
</div>
<button type="submit" class="btn btn-primary">Registrieren</button>
</form>
<div class="mt-3">
<p>Bereits registriert? <a href="/login">Anmelden</a></p>
</div>
<div class="mt-3">
<h5>Antwort:</h5>
<pre class="api-response" id="registerResponse"></pre>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,395 +0,0 @@
{% extends "base.html" %}
{% block title %}Statistiken - MYP API Tester{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-12 mb-4">
<div class="card">
<div class="card-header">
<h4 class="mb-0">Systemstatistiken</h4>
</div>
<div class="card-body">
<form class="api-form mb-3" data-url="/api/stats" data-method="GET" data-response="statsResponse">
<button type="submit" class="btn btn-primary">Statistiken aktualisieren</button>
</form>
<div class="row" id="statsContainer">
<!-- Wird dynamisch gefüllt -->
</div>
<!-- Problem-Drucker-Bereich -->
<div class="row mt-4">
<div class="col-md-12">
<div class="card">
<div class="card-header bg-warning text-dark">
<h5 class="mb-0">Drucker mit Verbindungsproblemen</h5>
</div>
<div class="card-body" id="problemPrintersContainer">
<div class="alert alert-info">Keine Verbindungsprobleme festgestellt.</div>
</div>
</div>
</div>
</div>
<!-- Uptime-Grafik -->
<div class="row mt-4">
<div class="col-md-12">
<div class="card">
<div class="card-header bg-dark text-white">
<h5 class="mb-0">Steckdosen-Verfügbarkeit</h5>
</div>
<div class="card-body">
<form class="api-form mb-3" data-url="/api/uptime" data-method="GET" data-response="uptimeResponse">
<button type="submit" class="btn btn-primary">Uptime-Daten laden</button>
</form>
<canvas id="uptimeChart" width="100%" height="300"></canvas>
</div>
</div>
</div>
</div>
<!-- API-Antworten -->
<div class="row mt-4">
<div class="col-md-6">
<h6>Stats API-Antwort:</h6>
<pre class="api-response" id="statsResponse"></pre>
</div>
<div class="col-md-6">
<h6>Uptime API-Antwort:</h6>
<pre class="api-response" id="uptimeResponse"></pre>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<!-- Chart.js für Diagramme -->
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
<script>
let uptimeChart;
document.addEventListener('DOMContentLoaded', function() {
// Statistiken laden
document.querySelector('form[data-url="/api/stats"]').dispatchEvent(new Event('submit'));
document.querySelector('form[data-url="/api/uptime"]').dispatchEvent(new Event('submit'));
// Statistiken aktualisieren, wenn API-Antwort geladen wird
const statsResponse = document.getElementById('statsResponse');
const statsObserver = new MutationObserver(function(mutations) {
try {
const stats = JSON.parse(statsResponse.textContent);
updateStatsDisplay(stats);
updateProblemPrinters(stats);
} catch (e) {
console.error('Fehler beim Parsen der Statistik-Daten:', e);
}
});
statsObserver.observe(statsResponse, { childList: true, characterData: true, subtree: true });
// Uptime-Daten aktualisieren, wenn API-Antwort geladen wird
const uptimeResponse = document.getElementById('uptimeResponse');
const uptimeObserver = new MutationObserver(function(mutations) {
try {
const uptime = JSON.parse(uptimeResponse.textContent);
updateUptimeChart(uptime);
} catch (e) {
console.error('Fehler beim Parsen der Uptime-Daten:', e);
}
});
uptimeObserver.observe(uptimeResponse, { childList: true, characterData: true, subtree: true });
// Periodische Aktualisierung
setInterval(function() {
document.querySelector('form[data-url="/api/stats"]').dispatchEvent(new Event('submit'));
document.querySelector('form[data-url="/api/uptime"]').dispatchEvent(new Event('submit'));
}, 60000); // Alle 60 Sekunden aktualisieren
});
function updateStatsDisplay(stats) {
const container = document.getElementById('statsContainer');
container.innerHTML = '';
// Drucker-Statistiken
const printerStats = document.createElement('div');
printerStats.className = 'col-md-4 mb-3';
printerStats.innerHTML = `
<div class="card h-100">
<div class="card-header bg-primary text-white">
<h5 class="mb-0">Drucker</h5>
</div>
<div class="card-body">
<div class="d-flex justify-content-between mb-2">
<span>Gesamt:</span>
<span>${stats.printers.total}</span>
</div>
<div class="d-flex justify-content-between mb-2">
<span>Verfügbar:</span>
<span>${stats.printers.available}</span>
</div>
<div class="d-flex justify-content-between mb-2">
<span>Auslastung:</span>
<span>${Math.round(stats.printers.utilization_rate * 100)}%</span>
</div>
<div class="progress mt-3 mb-3">
<div class="progress-bar" role="progressbar"
style="width: ${Math.round(stats.printers.utilization_rate * 100)}%">
${Math.round(stats.printers.utilization_rate * 100)}%
</div>
</div>
<hr />
<div class="d-flex justify-content-between mb-2">
<span>Online:</span>
<span>${stats.printers.online}</span>
</div>
<div class="d-flex justify-content-between mb-2">
<span>Offline:</span>
<span>${stats.printers.offline}</span>
</div>
<div class="d-flex justify-content-between mb-2">
<span>Verbindungsrate:</span>
<span>${Math.round(stats.printers.connectivity_rate * 100)}%</span>
</div>
<div class="progress mt-3">
<div class="progress-bar bg-success" role="progressbar"
style="width: ${Math.round(stats.printers.connectivity_rate * 100)}%">
${Math.round(stats.printers.connectivity_rate * 100)}%
</div>
</div>
</div>
</div>
`;
// Job-Statistiken
const jobStats = document.createElement('div');
jobStats.className = 'col-md-4 mb-3';
jobStats.innerHTML = `
<div class="card h-100">
<div class="card-header bg-success text-white">
<h5 class="mb-0">Druckaufträge</h5>
</div>
<div class="card-body">
<div class="d-flex justify-content-between mb-2">
<span>Gesamt:</span>
<span>${stats.jobs.total}</span>
</div>
<div class="d-flex justify-content-between mb-2">
<span>Aktiv:</span>
<span>${stats.jobs.active}</span>
</div>
<div class="d-flex justify-content-between mb-2">
<span>Abgeschlossen:</span>
<span>${stats.jobs.completed}</span>
</div>
<div class="d-flex justify-content-between mb-2">
<span>Durchschnittliche Dauer:</span>
<span>${stats.jobs.avg_duration} Minuten</span>
</div>
</div>
</div>
`;
// Benutzer- und Uptime-Statistiken
const userStats = document.createElement('div');
userStats.className = 'col-md-4 mb-3';
userStats.innerHTML = `
<div class="card h-100">
<div class="card-header bg-info text-white">
<h5 class="mb-0">System</h5>
</div>
<div class="card-body">
<div class="d-flex justify-content-between mb-2">
<span>Benutzer:</span>
<span>${stats.users.total}</span>
</div>
<hr />
<div class="d-flex justify-content-between mb-2">
<span>Verbindungsausfälle (7 Tage):</span>
<span>${stats.uptime.outages_last_7_days}</span>
</div>
<div class="d-flex justify-content-between mb-2">
<span>Aktuelle Probleme:</span>
<span>${stats.uptime.problem_printers.length}</span>
</div>
</div>
</div>
`;
container.appendChild(printerStats);
container.appendChild(jobStats);
container.appendChild(userStats);
}
function updateProblemPrinters(stats) {
const container = document.getElementById('problemPrintersContainer');
const problemPrinters = stats.uptime.problem_printers;
if (problemPrinters.length === 0) {
container.innerHTML = '<div class="alert alert-info">Keine Verbindungsprobleme festgestellt.</div>';
return;
}
let html = '<div class="table-responsive"><table class="table table-striped">';
html += '<thead><tr><th>Drucker</th><th>Status</th><th>Offline seit</th><th>Dauer</th></tr></thead>';
html += '<tbody>';
problemPrinters.forEach(printer => {
let offlineSince = 'Unbekannt';
let duration = 'Unbekannt';
if (printer.last_seen) {
try {
const lastSeen = new Date(printer.last_seen);
const now = new Date();
const diffSeconds = Math.floor((now - lastSeen) / 1000);
const hours = Math.floor(diffSeconds / 3600);
const minutes = Math.floor((diffSeconds % 3600) / 60);
offlineSince = lastSeen.toLocaleString();
duration = `${hours}h ${minutes}m`;
} catch (e) {
console.error('Fehler beim Berechnen der Offline-Zeit:', e);
}
}
html += `<tr>
<td>${printer.name}</td>
<td><span class="badge bg-danger">Offline</span></td>
<td>${offlineSince}</td>
<td>${duration}</td>
</tr>`;
});
html += '</tbody></table></div>';
container.innerHTML = html;
}
function updateUptimeChart(uptimeData) {
// Wenn keine Daten vorhanden sind, nichts tun
if (!uptimeData || !uptimeData.sockets || uptimeData.sockets.length === 0) {
return;
}
// Daten für das Diagramm vorbereiten
const socketNames = [];
const datasets = [];
const colors = {
online: 'rgba(40, 167, 69, 0.7)',
offline: 'rgba(220, 53, 69, 0.7)',
unknown: 'rgba(108, 117, 125, 0.7)'
};
// Zeitraum für das Diagramm (letzten 7 Tage)
const endDate = new Date();
const startDate = new Date();
startDate.setDate(startDate.getDate() - 7);
// Für jede Steckdose
uptimeData.sockets.forEach(socket => {
socketNames.push(socket.name);
// Sortiere Ereignisse nach Zeitstempel
if (socket.events) {
socket.events.sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp));
// Erstelle einen Datensatz für diese Steckdose
const data = [];
// Füge Ereignisse zum Datensatz hinzu
socket.events.forEach(event => {
data.push({
x: new Date(event.timestamp),
y: event.status === 'online' ? 1 : 0,
status: event.status,
duration: event.duration_seconds ?
formatDuration(event.duration_seconds) : null
});
});
// Füge aktuellen Status hinzu
if (socket.current_status) {
data.push({
x: new Date(),
y: socket.current_status.connection_status === 'online' ? 1 : 0,
status: socket.current_status.connection_status,
duration: null
});
}
datasets.push({
label: socket.name,
data: data,
stepped: true,
borderColor: colors[socket.current_status?.connection_status || 'unknown'],
backgroundColor: colors[socket.current_status?.connection_status || 'unknown'],
fill: false
});
}
});
// Chart.js Konfiguration
const ctx = document.getElementById('uptimeChart').getContext('2d');
// Wenn Chart bereits existiert, zerstöre ihn
if (uptimeChart) {
uptimeChart.destroy();
}
// Erstelle neuen Chart
uptimeChart = new Chart(ctx, {
type: 'line',
data: {
datasets: datasets
},
options: {
responsive: true,
plugins: {
tooltip: {
callbacks: {
label: function(context) {
const point = context.raw;
let label = context.dataset.label || '';
label += ': ' + (point.status === 'online' ? 'Online' : 'Offline');
if (point.duration) {
label += ' (Dauer: ' + point.duration + ')';
}
return label;
}
}
}
},
scales: {
x: {
type: 'time',
time: {
unit: 'day'
},
min: startDate,
max: endDate
},
y: {
min: -0.1,
max: 1.1,
ticks: {
callback: function(value) {
return value === 0 ? 'Offline' : value === 1 ? 'Online' : '';
}
}
}
}
}
});
}
function formatDuration(seconds) {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
return `${hours}h ${minutes}m`;
}
</script>
{% endblock %}

View File

@ -1,238 +0,0 @@
{% extends "base.html" %}
{% block title %}Benutzer - MYP API Tester{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-12 mb-4">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h4 class="mb-0">Benutzer verwalten</h4>
<button class="btn btn-primary" type="button" data-bs-toggle="collapse" data-bs-target="#newUserForm">
Neuen Benutzer hinzufügen
</button>
</div>
<div class="collapse" id="newUserForm">
<div class="card-body border-bottom">
<form class="api-form" data-url="/auth/register" data-method="POST" data-response="createUserResponse" data-reload="true">
<div class="mb-3">
<label for="userName" class="form-label">Benutzername</label>
<input type="text" class="form-control" id="userName" name="username" required>
</div>
<div class="mb-3">
<label for="userPassword" class="form-label">Passwort</label>
<input type="password" class="form-control" id="userPassword" name="password" required>
</div>
<div class="mb-3">
<label for="userDisplayName" class="form-label">Anzeigename</label>
<input type="text" class="form-control" id="userDisplayName" name="displayName">
</div>
<div class="mb-3">
<label for="userEmail" class="form-label">E-Mail</label>
<input type="email" class="form-control" id="userEmail" name="email">
</div>
<button type="submit" class="btn btn-success">Benutzer erstellen</button>
</form>
<div class="mt-3">
<h6>Antwort:</h6>
<pre class="api-response" id="createUserResponse"></pre>
</div>
</div>
</div>
<div class="card-body">
<form class="api-form mb-3" data-url="/api/users" data-method="GET" data-response="usersResponse">
<button type="submit" class="btn btn-primary">Benutzer aktualisieren</button>
</form>
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>ID</th>
<th>Benutzername</th>
<th>Anzeigename</th>
<th>E-Mail</th>
<th>Rolle</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody id="usersTableBody">
<!-- Wird dynamisch gefüllt -->
</tbody>
</table>
</div>
<div>
<h6>API-Antwort:</h6>
<pre class="api-response" id="usersResponse"></pre>
</div>
</div>
</div>
</div>
</div>
<!-- Benutzer bearbeiten Modal -->
<div class="modal fade" id="editUserModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Benutzer bearbeiten</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="editUserForm" class="api-form" data-method="PUT" data-response="editUserResponse" data-reload="true">
<input type="hidden" id="editUserId" name="userId">
<div class="mb-3">
<label for="editUserName" class="form-label">Benutzername</label>
<input type="text" class="form-control" id="editUserName" name="username" required>
</div>
<div class="mb-3">
<label for="editUserDisplayName" class="form-label">Anzeigename</label>
<input type="text" class="form-control" id="editUserDisplayName" name="displayName">
</div>
<div class="mb-3">
<label for="editUserEmail" class="form-label">E-Mail</label>
<input type="email" class="form-control" id="editUserEmail" name="email">
</div>
<div class="mb-3">
<label for="editUserRole" class="form-label">Rolle</label>
<select class="form-control" id="editUserRole" name="role">
<option value="user">Benutzer</option>
<option value="admin">Administrator</option>
<option value="guest">Gast</option>
</select>
</div>
</form>
<div class="mt-3">
<h6>Antwort:</h6>
<pre class="api-response" id="editUserResponse"></pre>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
<button type="submit" form="editUserForm" class="btn btn-primary">Änderungen speichern</button>
</div>
</div>
</div>
</div>
<!-- Benutzer löschen Modal -->
<div class="modal fade" id="deleteUserModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Benutzer löschen</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Möchten Sie den Benutzer <span id="deleteUserName"></span> wirklich löschen?</p>
<form id="deleteUserForm" class="api-form" data-method="DELETE" data-response="deleteUserResponse" data-reload="true">
<input type="hidden" id="deleteUserId" name="userId">
</form>
<div class="mt-3">
<h6>Antwort:</h6>
<pre class="api-response" id="deleteUserResponse"></pre>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
<button type="submit" form="deleteUserForm" class="btn btn-danger">Löschen</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
document.addEventListener('DOMContentLoaded', function() {
// Benutzer laden
document.querySelector('form[data-url="/api/users"]').dispatchEvent(new Event('submit'));
// Tabelle aktualisieren, wenn Benutzer geladen werden
const usersResponse = document.getElementById('usersResponse');
const observer = new MutationObserver(function(mutations) {
try {
const users = JSON.parse(usersResponse.textContent);
updateUsersTable(users);
} catch (e) {
console.error('Fehler beim Parsen der Benutzer-Daten:', e);
}
});
observer.observe(usersResponse, { childList: true, characterData: true, subtree: true });
// Edit-Modal vorbereiten
document.getElementById('editUserModal').addEventListener('show.bs.modal', function(event) {
const button = event.relatedTarget;
const userId = button.getAttribute('data-user-id');
const userName = button.getAttribute('data-user-name');
const userDisplayName = button.getAttribute('data-user-displayname');
const userEmail = button.getAttribute('data-user-email');
const userRole = button.getAttribute('data-user-role');
document.getElementById('editUserId').value = userId;
document.getElementById('editUserForm').setAttribute('data-url', `/api/users/${userId}`);
document.getElementById('editUserName').value = userName;
document.getElementById('editUserDisplayName').value = userDisplayName || '';
document.getElementById('editUserEmail').value = userEmail || '';
document.getElementById('editUserRole').value = userRole;
});
// Delete-Modal vorbereiten
document.getElementById('deleteUserModal').addEventListener('show.bs.modal', function(event) {
const button = event.relatedTarget;
const userId = button.getAttribute('data-user-id');
const userName = button.getAttribute('data-user-name');
document.getElementById('deleteUserId').value = userId;
document.getElementById('deleteUserForm').setAttribute('data-url', `/api/users/${userId}`);
document.getElementById('deleteUserName').textContent = userName;
});
});
function updateUsersTable(users) {
const tableBody = document.getElementById('usersTableBody');
tableBody.innerHTML = '';
users.forEach(user => {
const row = document.createElement('tr');
const roleClass = {
'admin': 'text-danger',
'user': 'text-primary',
'guest': 'text-secondary'
}[user.role] || '';
row.innerHTML = `
<td>${user.id}</td>
<td>${user.username}</td>
<td>${user.displayName || user.username}</td>
<td>${user.email || '-'}</td>
<td><span class="${roleClass}">${user.role}</span></td>
<td>
<button type="button" class="btn btn-sm btn-primary"
data-bs-toggle="modal"
data-bs-target="#editUserModal"
data-user-id="${user.id}"
data-user-name="${user.username}"
data-user-displayname="${user.displayName || ''}"
data-user-email="${user.email || ''}"
data-user-role="${user.role}">
Bearbeiten
</button>
<button type="button" class="btn btn-sm btn-danger"
data-bs-toggle="modal"
data-bs-target="#deleteUserModal"
data-user-id="${user.id}"
data-user-name="${user.username}">
Löschen
</button>
</td>
`;
tableBody.appendChild(row);
});
}
</script>
{% endblock %}