"feat: Implement SSL configuration for frontend and backend"

This commit is contained in:
Till Tomczak 2025-05-26 10:54:17 +02:00
parent f063d07232
commit 7893e1b904
7 changed files with 1386 additions and 192 deletions

View File

@ -11,11 +11,10 @@
{% block content %} {% block content %}
<div class="admin-container"> <div class="admin-container">
<h1 class="text-3xl font-bold mb-6 text-slate-900 dark:text-white">Admin Panel</h1> <h1 class="text-4xl font-extrabold mb-10 text-slate-900 dark:text-white tracking-tight">Admin Panel</h1>
<!-- Admin Stats --> <!-- Admin Stats -->
<div class="admin-stats"> <div class="admin-stats">
<div class="stat-card"> <div class="stat-card flex flex-col gap-2">
<div class="stat-icon"> <div class="stat-icon">
<svg class="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" /> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" />
@ -25,8 +24,7 @@
<div class="stat-value">{{ stats.total_users }}</div> <div class="stat-value">{{ stats.total_users }}</div>
<div class="stat-desc">Registrierte Benutzer</div> <div class="stat-desc">Registrierte Benutzer</div>
</div> </div>
<div class="stat-card flex flex-col gap-2">
<div class="stat-card">
<div class="stat-icon"> <div class="stat-icon">
<svg class="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z" /> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z" />
@ -36,8 +34,7 @@
<div class="stat-value">{{ stats.total_printers }}</div> <div class="stat-value">{{ stats.total_printers }}</div>
<div class="stat-desc">Verbundene Drucker</div> <div class="stat-desc">Verbundene Drucker</div>
</div> </div>
<div class="stat-card flex flex-col gap-2">
<div class="stat-card">
<div class="stat-icon"> <div class="stat-icon">
<svg class="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" /> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
@ -47,8 +44,7 @@
<div class="stat-value">{{ stats.active_jobs }}</div> <div class="stat-value">{{ stats.active_jobs }}</div>
<div class="stat-desc">Laufende Druckaufträge</div> <div class="stat-desc">Laufende Druckaufträge</div>
</div> </div>
<div class="stat-card flex flex-col gap-2">
<div class="stat-card">
<div class="stat-icon"> <div class="stat-icon">
<svg class="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" /> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
@ -59,9 +55,8 @@
<div class="stat-desc">Erfolgreiche Druckaufträge</div> <div class="stat-desc">Erfolgreiche Druckaufträge</div>
</div> </div>
</div> </div>
<!-- Tabs Navigation --> <!-- Tabs Navigation -->
<div class="nav-tabs"> <div class="nav-tabs mt-8">
<a href="{{ url_for('admin_page', tab='users') }}" class="nav-tab {{ 'active' if active_tab == 'users' else '' }}"> <a href="{{ url_for('admin_page', tab='users') }}" class="nav-tab {{ 'active' if active_tab == 'users' else '' }}">
<svg class="inline-block w-5 h-5 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="inline-block w-5 h-5 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" /> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" />
@ -93,17 +88,15 @@
Logs Logs
</a> </a>
</div> </div>
<!-- Tab Content --> <!-- Tab Content -->
<div class="tab-content mt-8"> <div class="tab-content mt-8">
<!-- Users Tab -->
{% if active_tab == 'users' %} {% if active_tab == 'users' %}
<div id="users-tab" class="tab-pane active"> <div id="users-tab" class="tab-pane active">
<div class="bg-white dark:bg-dark-card rounded-xl shadow-lg p-6 transition-colors"> <div class="bg-white dark:bg-dark-card rounded-xl shadow-lg p-8 transition-colors">
<div class="flex justify-between items-center mb-6"> <div class="flex flex-col md:flex-row md:justify-between md:items-center mb-8 gap-4">
<h3 class="text-xl font-bold text-slate-900 dark:text-white">Benutzer</h3> <h3 class="text-2xl font-bold text-slate-900 dark:text-white">Benutzer</h3>
<form action="{{ url_for('admin_page', tab='users') }}" method="get"> <form action="{{ url_for('admin_page', tab='users') }}" method="get">
<button type="submit" class="btn btn-primary"> <button type="submit" class="btn btn-primary flex items-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<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"/> <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> </svg>
@ -111,41 +104,41 @@
</button> </button>
</form> </form>
</div> </div>
<div class="overflow-x-auto rounded-lg border border-light-border dark:border-dark-border"> <div class="overflow-x-auto rounded-lg border border-light-border dark:border-dark-border mb-8">
<table class="min-w-full divide-y divide-light-border dark:divide-dark-border"> <table class="admin-table">
<thead class="bg-slate-50 dark:bg-slate-800 transition-colors"> <thead>
<tr> <tr>
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 dark:text-slate-400 uppercase tracking-wider">Benutzer</th> <th>Benutzer</th>
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 dark:text-slate-400 uppercase tracking-wider">Rolle</th> <th>Rolle</th>
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 dark:text-slate-400 uppercase tracking-wider">Status</th> <th>Status</th>
<th class="px-6 py-3 text-right text-xs font-medium text-slate-500 dark:text-slate-400 uppercase tracking-wider">Aktionen</th> <th class="text-right">Aktionen</th>
</tr> </tr>
</thead> </thead>
<tbody class="bg-white dark:bg-dark-card divide-y divide-light-border dark:divide-dark-border transition-colors"> <tbody>
{% for user in users %} {% for user in users %}
<tr class="transition-colors hover:bg-slate-50 dark:hover:bg-slate-700"> <tr>
<td class="px-6 py-4 whitespace-nowrap"> <td>
<div class="flex items-center"> <div class="flex items-center gap-4">
<div class="flex-shrink-0 h-10 w-10 rounded-full bg-blue-100 dark:bg-blue-900 flex items-center justify-center text-blue-700 dark:text-blue-300 font-medium transition-colors"> <div class="flex-shrink-0 h-10 w-10 rounded-full bg-blue-100 dark:bg-blue-900 flex items-center justify-center text-blue-700 dark:text-blue-300 font-medium">
{{ user.email[0]|upper if user.email else 'U' }} {{ user.email[0]|upper if user.email else 'U' }}
</div> </div>
<div class="ml-4"> <div>
<div class="text-sm font-medium text-slate-900 dark:text-white transition-colors">{{ user.name }}</div> <div class="text-base font-medium text-slate-900 dark:text-white">{{ user.name }}</div>
<div class="text-sm text-slate-500 dark:text-slate-400 transition-colors">{{ user.email }}</div> <div class="text-sm text-slate-500 dark:text-slate-400">{{ user.email }}</div>
</div> </div>
</div> </div>
</td> </td>
<td class="px-6 py-4 whitespace-nowrap"> <td>
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full {{ 'bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200' if user.is_admin else 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-200' }} transition-colors"> <span class="badge {{ 'badge-info' if user.is_admin else 'badge' }}">
{{ 'Administrator' if user.is_admin else 'Benutzer' }} {{ 'Administrator' if user.is_admin else 'Benutzer' }}
</span> </span>
</td> </td>
<td class="px-6 py-4 whitespace-nowrap"> <td>
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full {{ 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200' if user.active else 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200' }} transition-colors"> <span class="badge {{ 'badge-success' if user.active else 'badge-error' }}">
{{ 'Aktiv' if user.active else 'Inaktiv' }} {{ 'Aktiv' if user.active else 'Inaktiv' }}
</span> </span>
</td> </td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> <td class="text-right">
<form method="post" action="{{ url_for('delete_user', user_id=user.id) }}" class="inline" onsubmit="return confirm('Benutzer wirklich löschen?');"> <form method="post" action="{{ url_for('delete_user', user_id=user.id) }}" class="inline" onsubmit="return confirm('Benutzer wirklich löschen?');">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="text-red-600 hover:text-red-900 dark:text-red-400 dark:hover:text-red-300 transition-colors"> <button type="submit" class="text-red-600 hover:text-red-900 dark:text-red-400 dark:hover:text-red-300 transition-colors">
@ -160,13 +153,11 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<!-- Add User Form --> <!-- Add User Form -->
<div class="mt-8 bg-slate-50 dark:bg-slate-800 rounded-lg p-6 transition-colors"> <div class="mt-8 bg-slate-50 dark:bg-slate-800 rounded-lg p-8">
<h4 class="text-lg font-semibold text-slate-900 dark:text-white mb-4 transition-colors">Neuen Benutzer hinzufügen</h4> <h4 class="text-xl font-semibold text-slate-900 dark:text-white mb-4">Neuen Benutzer hinzufügen</h4>
<form action="{{ url_for('create_user') }}" method="post" class="space-y-4"> <form action="{{ url_for('create_user') }}" method="post" class="grid grid-cols-1 md:grid-cols-2 gap-6">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="form-group"> <div class="form-group">
<label for="user-email" class="form-label">Email</label> <label for="user-email" class="form-label">Email</label>
<input type="email" id="user-email" name="email" class="form-input" required> <input type="email" id="user-email" name="email" class="form-input" required>
@ -186,8 +177,7 @@
<option value="admin">Administrator</option> <option value="admin">Administrator</option>
</select> </select>
</div> </div>
</div> <div class="col-span-1 md:col-span-2 flex justify-end">
<div class="flex justify-end">
<button type="submit" class="btn btn-primary">Benutzer hinzufügen</button> <button type="submit" class="btn btn-primary">Benutzer hinzufügen</button>
</div> </div>
</form> </form>
@ -195,15 +185,14 @@
</div> </div>
</div> </div>
{% endif %} {% endif %}
<!-- Printers Tab --> <!-- Printers Tab -->
{% if active_tab == 'printers' %} {% if active_tab == 'printers' %}
<div id="printers-tab" class="tab-pane active"> <div id="printers-tab" class="tab-pane active">
<div class="bg-white dark:bg-dark-card rounded-xl shadow-lg p-6 transition-colors"> <div class="bg-white dark:bg-dark-card rounded-xl shadow-lg p-8 transition-colors">
<div class="flex justify-between items-center mb-6"> <div class="flex flex-col md:flex-row md:justify-between md:items-center mb-8 gap-4">
<h3 class="text-xl font-bold text-slate-900 dark:text-white transition-colors">Drucker</h3> <h3 class="text-2xl font-bold text-slate-900 dark:text-white">Drucker</h3>
<form action="{{ url_for('admin_page', tab='printers') }}" method="get"> <form action="{{ url_for('admin_page', tab='printers') }}" method="get">
<button type="submit" class="btn btn-primary"> <button type="submit" class="btn btn-primary flex items-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<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"/> <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> </svg>
@ -211,7 +200,7 @@
</button> </button>
</form> </form>
</div> </div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{% for printer in printers %} {% for printer in printers %}
<div class="printer-card transition-colors"> <div class="printer-card transition-colors">
<div class="printer-header"> <div class="printer-header">
@ -229,62 +218,59 @@
</div> </div>
<div class="printer-info"> <div class="printer-info">
<div> <div>
<span class="text-sm text-slate-500 dark:text-slate-400 transition-colors">Modell:</span> <span class="text-sm text-slate-500 dark:text-slate-400">Modell:</span>
<span class="text-sm font-medium text-slate-900 dark:text-white transition-colors">{{ printer.model }}</span> <span class="text-sm font-medium text-slate-900 dark:text-white">{{ printer.model }}</span>
</div> </div>
<div> <div>
<span class="text-sm text-slate-500 dark:text-slate-400 transition-colors">IP-Adresse:</span> <span class="text-sm text-slate-500 dark:text-slate-400">IP-Adresse:</span>
<span class="text-sm font-medium text-slate-900 dark:text-white transition-colors">{{ printer.ip_address }}</span> <span class="text-sm font-medium text-slate-900 dark:text-white">{{ printer.ip_address }}</span>
</div> </div>
<div> <div>
<span class="text-sm text-slate-500 dark:text-slate-400 transition-colors">Standort:</span> <span class="text-sm text-slate-500 dark:text-slate-400">Standort:</span>
<span class="text-sm font-medium text-slate-900 dark:text-white transition-colors">{{ printer.location or 'Nicht angegeben' }}</span> <span class="text-sm font-medium text-slate-900 dark:text-white">{{ printer.location or 'Nicht angegeben' }}</span>
</div> </div>
</div> </div>
<div class="printer-status"> <div class="printer-status">
<div class="status-indicator {{ 'status-running' if printer.status == 'online' else 'status-stopped' }} transition-colors"></div> <div class="status-indicator {{ 'status-running' if printer.status == 'online' else 'status-stopped' }}"></div>
<span class="status-text transition-colors">{{ 'Online' if printer.status == 'online' else 'Offline' }}</span> <span class="status-text">{{ 'Online' if printer.status == 'online' else 'Offline' }}</span>
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
<!-- Add Printer Form --> <!-- Add Printer Form -->
<div class="mt-8 bg-slate-50 dark:bg-slate-800 rounded-lg p-6 transition-colors"> <div class="mt-8 bg-slate-50 dark:bg-slate-800 rounded-lg p-8">
<h4 class="text-lg font-semibold text-slate-900 dark:text-white mb-4 transition-colors">Neuen Drucker hinzufügen</h4> <h4 class="text-xl font-semibold text-slate-900 dark:text-white mb-4">Neuen Drucker hinzufügen</h4>
<form action="{{ url_for('create_printer') }}" method="post" class="space-y-4"> <form action="{{ url_for('create_printer') }}" method="post" class="grid grid-cols-1 md:grid-cols-2 gap-6">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="form-group"> <div class="form-group">
<label for="printer-name" class="form-label transition-colors">Name</label> <label for="printer-name" class="form-label">Name</label>
<input type="text" id="printer-name" name="name" class="form-input transition-colors" required> <input type="text" id="printer-name" name="name" class="form-input" required>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="printer-model" class="form-label transition-colors">Modell</label> <label for="printer-model" class="form-label">Modell</label>
<input type="text" id="printer-model" name="model" class="form-input transition-colors" required> <input type="text" id="printer-model" name="model" class="form-input" required>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="printer-mac" class="form-label transition-colors">MAC-Adresse</label> <label for="printer-mac" class="form-label">MAC-Adresse</label>
<input type="text" id="printer-mac" name="mac_address" class="form-input transition-colors" required> <input type="text" id="printer-mac" name="mac_address" class="form-input" required>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="printer-location" class="form-label transition-colors">Standort</label> <label for="printer-location" class="form-label">Standort</label>
<input type="text" id="printer-location" name="location" class="form-input transition-colors"> <input type="text" id="printer-location" name="location" class="form-input">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="printer-plug-ip" class="form-label transition-colors">Steckdosen-IP</label> <label for="printer-plug-ip" class="form-label">Steckdosen-IP</label>
<input type="text" id="printer-plug-ip" name="plug_ip" class="form-input transition-colors"> <input type="text" id="printer-plug-ip" name="plug_ip" class="form-input">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="printer-plug-username" class="form-label transition-colors">Steckdosen-Benutzername</label> <label for="printer-plug-username" class="form-label">Steckdosen-Benutzername</label>
<input type="text" id="printer-plug-username" name="plug_username" class="form-input transition-colors"> <input type="text" id="printer-plug-username" name="plug_username" class="form-input">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="printer-plug-password" class="form-label transition-colors">Steckdosen-Passwort</label> <label for="printer-plug-password" class="form-label">Steckdosen-Passwort</label>
<input type="password" id="printer-plug-password" name="plug_password" class="form-input transition-colors"> <input type="password" id="printer-plug-password" name="plug_password" class="form-input">
</div> </div>
</div> <div class="col-span-1 md:col-span-2 flex justify-end">
<div class="flex justify-end">
<button type="submit" class="btn btn-primary">Drucker hinzufügen</button> <button type="submit" class="btn btn-primary">Drucker hinzufügen</button>
</div> </div>
</form> </form>
@ -292,17 +278,16 @@
</div> </div>
</div> </div>
{% endif %} {% endif %}
<!-- Scheduler Tab --> <!-- Scheduler Tab -->
{% if active_tab == 'scheduler' %} {% if active_tab == 'scheduler' %}
<div id="scheduler-tab" class="tab-pane active"> <div id="scheduler-tab" class="tab-pane active">
<div class="bg-white dark:bg-dark-card rounded-xl shadow-lg p-6 transition-colors"> <div class="bg-white dark:bg-dark-card rounded-xl shadow-lg p-8 transition-colors">
<div class="flex justify-between items-center mb-6"> <div class="flex flex-col md:flex-row md:justify-between md:items-center mb-8 gap-4">
<h3 class="text-xl font-bold text-slate-900 dark:text-white transition-colors">Scheduler Status</h3> <h3 class="text-2xl font-bold text-slate-900 dark:text-white">Scheduler Status</h3>
<div class="flex space-x-4"> <div class="flex space-x-4">
<form action="{{ url_for('start_scheduler') }}" method="post" class="inline"> <form action="{{ url_for('start_scheduler') }}" method="post" class="inline">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="btn btn-success"> <button type="submit" class="btn btn-success flex items-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" /> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
@ -312,7 +297,7 @@
</form> </form>
<form action="{{ url_for('stop_scheduler') }}" method="post" class="inline"> <form action="{{ url_for('stop_scheduler') }}" method="post" class="inline">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="btn btn-error"> <button type="submit" class="btn btn-error flex items-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 10a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1h-4a1 1 0 01-1-1v-4z" /> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 10a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1h-4a1 1 0 01-1-1v-4z" />
@ -323,121 +308,103 @@
</div> </div>
</div> </div>
<div class="scheduler-status"> <div class="scheduler-status">
<div class="bg-slate-50 dark:bg-slate-800 rounded-lg p-6 flex items-center justify-between transition-colors"> <div class="bg-slate-50 dark:bg-slate-800 rounded-lg p-6 flex flex-col md:flex-row items-center justify-between gap-4 transition-colors">
<div class="flex items-center"> <div class="flex items-center">
<div class="status-indicator {{ 'status-running pulse' if scheduler_status.running else 'status-stopped' }}"></div> <div class="status-indicator {{ 'status-running pulse' if scheduler_status.running else 'status-stopped' }}"></div>
<span class="text-xl font-medium ml-3 {{ 'text-green-600 dark:text-green-400' if scheduler_status.running else 'text-red-600 dark:text-red-400' }} transition-colors"> <span class="text-xl font-medium ml-3 {{ 'text-green-600 dark:text-green-400' if scheduler_status.running else 'text-red-600 dark:text-red-400' }}">{{ 'Aktiv' if scheduler_status.running else 'Inaktiv' }}</span>
{{ 'Aktiv' if scheduler_status.running else 'Inaktiv' }}
</span>
</div> </div>
<div class="text-slate-700 dark:text-slate-300 transition-colors"> <div class="text-slate-700 dark:text-slate-300">{{ scheduler_status.message }}</div>
{{ scheduler_status.message }}
</div> </div>
</div> <div class="mt-6 bg-slate-50 dark:bg-slate-800 rounded-lg p-6">
<h4 class="text-lg font-semibold text-slate-900 dark:text-white mb-4">Scheduler Informationen</h4>
<div class="mt-6 bg-slate-50 dark:bg-slate-800 rounded-lg p-6 transition-colors">
<h4 class="text-lg font-semibold text-slate-900 dark:text-white mb-4 transition-colors">Scheduler Informationen</h4>
<div class="space-y-4"> <div class="space-y-4">
<p class="text-slate-700 dark:text-slate-300 transition-colors"> <p class="text-slate-700 dark:text-slate-300">
Der Scheduler ist verantwortlich für die automatische Zuweisung und Ausführung von Druckaufträgen. Der Scheduler ist verantwortlich für die automatische Zuweisung und Ausführung von Druckaufträgen. Er überwacht kontinuierlich den Status der Drucker und der anstehenden Jobs.
Er überwacht kontinuierlich den Status der Drucker und der anstehenden Jobs.
</p> </p>
<p class="text-slate-700 dark:text-slate-300 transition-colors"> <ul class="list-disc list-inside mt-2 space-y-1 text-slate-700 dark:text-slate-300">
<strong>Wichtige Hinweise:</strong>
<ul class="list-disc list-inside mt-2 space-y-1">
<li>Der Scheduler sollte während des normalen Betriebs immer aktiv sein</li> <li>Der Scheduler sollte während des normalen Betriebs immer aktiv sein</li>
<li>Bei Wartungsarbeiten kann der Scheduler vorübergehend deaktiviert werden</li> <li>Bei Wartungsarbeiten kann der Scheduler vorübergehend deaktiviert werden</li>
<li>Nach einem Neustart des Systems muss der Scheduler manuell gestartet werden</li> <li>Nach einem Neustart des Systems muss der Scheduler manuell gestartet werden</li>
</ul> </ul>
</p>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% endif %} {% endif %}
<!-- System Tab --> <!-- System Tab -->
{% if active_tab == 'system' %} {% if active_tab == 'system' %}
<div id="system-tab" class="tab-pane active"> <div id="system-tab" class="tab-pane active">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6"> <div class="grid grid-cols-1 md:grid-cols-2 gap-8">
<div class="bg-white dark:bg-dark-card rounded-xl shadow-lg p-6 transition-colors"> <div class="bg-white dark:bg-dark-card rounded-xl shadow-lg p-8">
<h3 class="text-xl font-bold text-slate-900 dark:text-white mb-6 transition-colors">System Ressourcen</h3> <h3 class="text-2xl font-bold text-slate-900 dark:text-white mb-6">System Ressourcen</h3>
<div class="space-y-6"> <div class="space-y-6">
<div> <div>
<div class="flex justify-between mb-2"> <div class="flex justify-between mb-2">
<span class="text-slate-700 dark:text-slate-300 transition-colors">CPU Auslastung</span> <span class="text-slate-700 dark:text-slate-300">CPU Auslastung</span>
<span class="text-slate-900 dark:text-white font-medium transition-colors">{{ system_info.cpu }}%</span> <span class="text-slate-900 dark:text-white font-medium">{{ system_info.cpu }}%</span>
</div> </div>
{{ render_progress_bar(system_info.cpu, 'blue')|safe }} {{ render_progress_bar(system_info.cpu, 'blue')|safe }}
</div> </div>
<div> <div>
<div class="flex justify-between mb-2"> <div class="flex justify-between mb-2">
<span class="text-slate-700 dark:text-slate-300 transition-colors">Arbeitsspeicher</span> <span class="text-slate-700 dark:text-slate-300">Arbeitsspeicher</span>
<span class="text-slate-900 dark:text-white font-medium transition-colors">{{ system_info.memory }}%</span> <span class="text-slate-900 dark:text-white font-medium">{{ system_info.memory }}%</span>
</div> </div>
{{ render_progress_bar(system_info.memory, 'green')|safe }} {{ render_progress_bar(system_info.memory, 'green')|safe }}
</div> </div>
<div> <div>
<div class="flex justify-between mb-2"> <div class="flex justify-between mb-2">
<span class="text-slate-700 dark:text-slate-300 transition-colors">Festplattenspeicher</span> <span class="text-slate-700 dark:text-slate-300">Festplattenspeicher</span>
<span class="text-slate-900 dark:text-white font-medium transition-colors">{{ system_info.disk }}%</span> <span class="text-slate-900 dark:text-white font-medium">{{ system_info.disk }}%</span>
</div> </div>
{{ render_progress_bar(system_info.disk, 'purple')|safe }} {{ render_progress_bar(system_info.disk, 'purple')|safe }}
</div> </div>
<div class="pt-4 border-t border-gray-200 dark:border-gray-700">
<div class="pt-4 border-t border-gray-200 dark:border-gray-700 transition-colors">
<div class="flex justify-between items-center"> <div class="flex justify-between items-center">
<span class="text-slate-700 dark:text-slate-300 transition-colors">System Uptime</span> <span class="text-slate-700 dark:text-slate-300">System Uptime</span>
<span class="text-slate-900 dark:text-white font-medium transition-colors">{{ system_info.uptime }} Tage</span> <span class="text-slate-900 dark:text-white font-medium">{{ system_info.uptime }} Tage</span>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="bg-white dark:bg-dark-card rounded-xl shadow-lg p-8">
<div class="bg-white dark:bg-dark-card rounded-xl shadow-lg p-6 transition-colors"> <h3 class="text-2xl font-bold text-slate-900 dark:text-white mb-6">Dienste Status</h3>
<h3 class="text-xl font-bold text-slate-900 dark:text-white mb-6 transition-colors">Dienste Status</h3>
<div class="space-y-4"> <div class="space-y-4">
<div class="flex justify-between items-center p-3 bg-slate-50 dark:bg-slate-800 rounded-lg transition-colors"> <div class="flex justify-between items-center p-3 bg-slate-50 dark:bg-slate-800 rounded-lg">
<div class="flex items-center"> <div class="flex items-center">
<div class="status-indicator status-running"></div> <div class="status-indicator status-running"></div>
<span class="ml-3 text-slate-900 dark:text-white transition-colors">Webserver</span> <span class="ml-3 text-slate-900 dark:text-white">Webserver</span>
</div> </div>
<span class="text-green-600 dark:text-green-400 font-medium transition-colors">Online</span> <span class="text-green-600 dark:text-green-400 font-medium">Online</span>
</div> </div>
<div class="flex justify-between items-center p-3 bg-slate-50 dark:bg-slate-800 rounded-lg">
<div class="flex justify-between items-center p-3 bg-slate-50 dark:bg-slate-800 rounded-lg transition-colors">
<div class="flex items-center"> <div class="flex items-center">
<div class="status-indicator {{ 'status-running' if scheduler_status.running else 'status-stopped' }}"></div> <div class="status-indicator {{ 'status-running' if scheduler_status.running else 'status-stopped' }}"></div>
<span class="ml-3 text-slate-900 dark:text-white transition-colors">Scheduler</span> <span class="ml-3 text-slate-900 dark:text-white">Scheduler</span>
</div> </div>
<span class="{{ 'text-green-600 dark:text-green-400' if scheduler_status.running else 'text-red-600 dark:text-red-400' }} font-medium transition-colors"> <span class="{{ 'text-green-600 dark:text-green-400' if scheduler_status.running else 'text-red-600 dark:text-red-400' }} font-medium">
{{ 'Online' if scheduler_status.running else 'Offline' }} {{ 'Online' if scheduler_status.running else 'Offline' }}
</span> </span>
</div> </div>
<div class="flex justify-between items-center p-3 bg-slate-50 dark:bg-slate-800 rounded-lg">
<div class="flex justify-between items-center p-3 bg-slate-50 dark:bg-slate-800 rounded-lg transition-colors">
<div class="flex items-center"> <div class="flex items-center">
<div class="status-indicator status-running"></div> <div class="status-indicator status-running"></div>
<span class="ml-3 text-slate-900 dark:text-white transition-colors">Datenbank</span> <span class="ml-3 text-slate-900 dark:text-white">Datenbank</span>
</div> </div>
<span class="text-green-600 dark:text-green-400 font-medium transition-colors">Online</span> <span class="text-green-600 dark:text-green-400 font-medium">Online</span>
</div> </div>
<div class="flex justify-between items-center p-3 bg-slate-50 dark:bg-slate-800 rounded-lg">
<div class="flex justify-between items-center p-3 bg-slate-50 dark:bg-slate-800 rounded-lg transition-colors">
<div class="flex items-center"> <div class="flex items-center">
<div class="status-indicator status-running"></div> <div class="status-indicator status-running"></div>
<span class="ml-3 text-slate-900 dark:text-white transition-colors">Drucker-Manager</span> <span class="ml-3 text-slate-900 dark:text-white">Drucker-Manager</span>
</div> </div>
<span class="text-green-600 dark:text-green-400 font-medium transition-colors">Online</span> <span class="text-green-600 dark:text-green-400 font-medium">Online</span>
</div> </div>
</div> </div>
<div class="mt-6"> <div class="mt-6">
<form action="{{ url_for('admin_page', tab='system') }}" method="get"> <form action="{{ url_for('admin_page', tab='system') }}" method="get">
<button type="submit" class="btn btn-primary w-full"> <button type="submit" class="btn btn-primary w-full flex items-center justify-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<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"/> <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> </svg>
@ -449,13 +416,12 @@
</div> </div>
</div> </div>
{% endif %} {% endif %}
<!-- Logs Tab --> <!-- Logs Tab -->
{% if active_tab == 'logs' %} {% if active_tab == 'logs' %}
<div id="logs-tab" class="tab-pane active"> <div id="logs-tab" class="tab-pane active">
<div class="bg-white dark:bg-dark-card rounded-xl shadow-lg p-6 transition-colors"> <div class="bg-white dark:bg-dark-card rounded-xl shadow-lg p-8 transition-colors">
<div class="flex justify-between items-center mb-6"> <div class="flex flex-col md:flex-row md:justify-between md:items-center mb-8 gap-4">
<h3 class="text-xl font-bold text-slate-900 dark:text-white transition-colors">System Logs</h3> <h3 class="text-2xl font-bold text-slate-900 dark:text-white">System Logs</h3>
<div class="flex space-x-4"> <div class="flex space-x-4">
<form action="{{ url_for('admin_page', tab='logs') }}" method="get"> <form action="{{ url_for('admin_page', tab='logs') }}" method="get">
<div class="flex space-x-2"> <div class="flex space-x-2">
@ -466,7 +432,7 @@
<option value="INFO" {{ 'selected' if request.args.get('log_level') == 'INFO' else '' }}>Info</option> <option value="INFO" {{ 'selected' if request.args.get('log_level') == 'INFO' else '' }}>Info</option>
<option value="DEBUG" {{ 'selected' if request.args.get('log_level') == 'DEBUG' else '' }}>Debug</option> <option value="DEBUG" {{ 'selected' if request.args.get('log_level') == 'DEBUG' else '' }}>Debug</option>
</select> </select>
<button type="submit" class="btn btn-secondary"> <button type="submit" class="btn btn-secondary flex items-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<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"/> <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> </svg>
@ -476,18 +442,18 @@
</form> </form>
</div> </div>
</div> </div>
<div class="bg-slate-50 dark:bg-slate-800 rounded-lg p-4 max-h-[600px] overflow-y-auto transition-colors"> <div class="bg-slate-50 dark:bg-slate-800 rounded-lg p-4 max-h-[600px] overflow-y-auto">
{% if logs %} {% if logs %}
{% for log in logs %} {% for log in logs %}
<div class="log-entry log-{{ log.level|lower }}"> <div class="log-entry log-{{ log.level|lower }}">
<div class="flex items-start"> <div class="flex items-start gap-4">
<div class="min-w-[160px] text-xs text-slate-500 dark:text-slate-400">{{ log.timestamp }}</div> <div class="min-w-[160px] text-xs text-slate-500 dark:text-slate-400">{{ log.timestamp }}</div>
<div class="flex-1"> <div class="flex-1">
<div class="flex items-center"> <div class="flex items-center gap-2">
<span class="badge badge-{{ 'success' if log.level == 'INFO' else ('warning' if log.level == 'WARNING' else ('error' if log.level == 'ERROR' else ('info' if log.level == 'DEBUG' else 'error'))) }}"> <span class="badge badge-{{ 'success' if log.level == 'INFO' else ('warning' if log.level == 'WARNING' else ('error' if log.level == 'ERROR' else ('info' if log.level == 'DEBUG' else 'error'))) }}">
{{ log.level }} {{ log.level }}
</span> </span>
<span class="ml-2 text-xs font-medium text-slate-700 dark:text-slate-300">[{{ log.category }}]</span> <span class="text-xs font-medium text-slate-700 dark:text-slate-300">[{{ log.category }}]</span>
</div> </div>
<div class="mt-1 text-sm text-slate-700 dark:text-slate-300">{{ log.message }}</div> <div class="mt-1 text-sm text-slate-700 dark:text-slate-300">{{ log.message }}</div>
</div> </div>

View File

@ -151,7 +151,20 @@ def install_certificate_system():
try: try:
import subprocess import subprocess
ssl_cert_path = os.path.abspath("app/instance/ssl/myp.crt") import os
ssl_cert_path = os.path.abspath(os.path.join("app", "instance", "ssl", "myp.crt"))
# Prüfen, ob Datei existiert
if not os.path.exists(ssl_cert_path):
print(f"Zertifikat nicht gefunden unter: {ssl_cert_path}")
# Alternativen Pfad versuchen
alt_path = os.path.abspath(os.path.join("backend", "app", "instance", "ssl", "myp.crt"))
if os.path.exists(alt_path):
ssl_cert_path = alt_path
print(f"Verwende alternativen Pfad: {ssl_cert_path}")
else:
print("Zertifikat konnte nicht gefunden werden.")
return False
# Befehle zum Installieren des Zertifikats im Windows-Zertifikatsspeicher # Befehle zum Installieren des Zertifikats im Windows-Zertifikatsspeicher
commands = [ commands = [

View File

@ -17,12 +17,29 @@ const NEXT_CONFIG_PATH = path.join(__dirname, 'next.config.js');
console.log('=== Frontend-SSL-Konfiguration ==='); console.log('=== Frontend-SSL-Konfiguration ===');
// Prüfen, ob SSL-Verzeichnis und Zertifikate existieren // Verzeichnis erstellen, falls es nicht existiert
if (!fs.existsSync(SSL_DIR) || if (!fs.existsSync(SSL_DIR)) {
!fs.existsSync(path.join(SSL_DIR, 'myp.crt')) || console.log(`SSL-Verzeichnis wird erstellt: ${SSL_DIR}`);
!fs.existsSync(path.join(SSL_DIR, 'myp.key'))) { fs.mkdirSync(SSL_DIR, { recursive: true });
}
// Prüfen, ob SSL-Zertifikate existieren
if (!fs.existsSync(path.join(SSL_DIR, 'myp.crt')) || !fs.existsSync(path.join(SSL_DIR, 'myp.key'))) {
console.log('SSL-Zertifikate nicht gefunden. Prüfe Backend-Verzeichnis...');
// Versuche, die Zertifikate aus dem Backend zu kopieren
const backendCertPath = path.join('..', 'backend', 'app', 'instance', 'ssl', 'myp.crt');
const backendKeyPath = path.join('..', 'backend', 'app', 'instance', 'ssl', 'myp.key');
if (fs.existsSync(backendCertPath) && fs.existsSync(backendKeyPath)) {
console.log('Zertifikate im Backend-Verzeichnis gefunden. Kopiere...');
fs.copyFileSync(backendCertPath, path.join(SSL_DIR, 'myp.crt'));
fs.copyFileSync(backendKeyPath, path.join(SSL_DIR, 'myp.key'));
console.log('Zertifikate erfolgreich in das Frontend-Verzeichnis kopiert.');
} else {
console.error('SSL-Zertifikate nicht gefunden. Bitte zuerst das Backend-Skript ausführen.'); console.error('SSL-Zertifikate nicht gefunden. Bitte zuerst das Backend-Skript ausführen.');
process.exit(1); process.exit(1);
}
} }
console.log('SSL-Zertifikate gefunden. Konfiguriere Frontend...'); console.log('SSL-Zertifikate gefunden. Konfiguriere Frontend...');

36
frontend/next.config.js Normal file
View File

@ -0,0 +1,36 @@
/** @type {import('next').NextConfig} */
const fs = require('fs');
const path = require('path');
const nextConfig = {
reactStrictMode: true,
webpack: (config) => {
return config;
},
// HTTPS-Konfiguration für die Entwicklung
devServer: {
https: {
key: fs.readFileSync(path.resolve(__dirname, 'ssl/myp.key')),
cert: fs.readFileSync(path.resolve(__dirname, 'ssl/myp.crt')),
},
},
// Konfiguration für selbstsignierte Zertifikate
serverOptions: {
https: {
key: fs.readFileSync(path.resolve(__dirname, 'ssl/myp.key')),
cert: fs.readFileSync(path.resolve(__dirname, 'ssl/myp.crt')),
},
},
// Zusätzliche Konfigurationen
async rewrites() {
return [
{
source: '/api/:path*',
destination: 'https://raspberrypi:443/api/:path*',
},
]
}
};
module.exports = nextConfig;

File diff suppressed because it is too large Load Diff

View File

@ -75,6 +75,7 @@
"@types/react": "^18.3.11", "@types/react": "^18.3.11",
"@types/react-dom": "^18.3.1", "@types/react-dom": "^18.3.1",
"drizzle-kit": "^0.21.4", "drizzle-kit": "^0.21.4",
"https-localhost": "^4.7.1",
"postcss": "^8.4.47", "postcss": "^8.4.47",
"tailwindcss": "^3.4.13", "tailwindcss": "^3.4.13",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",

View File

@ -1,3 +1,6 @@
// SSL-Verbindungen akzeptieren (selbstsignierte Zertifikate)
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
// Basis-URL für Backend-API // Basis-URL für Backend-API
// Versucht verschiedene Verbindungsoptionen mit Fallbacks // Versucht verschiedene Verbindungsoptionen mit Fallbacks
const getApiBaseUrl = () => { const getApiBaseUrl = () => {