Refactoring - Fix2

Dieser Commit ist enthalten in:
2025-06-18 00:07:34 +02:00
Ursprung 0ec0d2c267
Commit a9cfecc699
18 geänderte Dateien mit 1412 neuen und 337 gelöschten Zeilen

Datei anzeigen

@@ -140,87 +140,87 @@
<table class="table table-hover">
<thead>
<tr>
{{ sortable_header('Zeitstempel', 'timestamp', sort, order) }}
{{ sortable_header('Benutzer', 'username', sort, order) }}
{{ sortable_header('Aktion', 'action', sort, order) }}
{{ sortable_header('Entität', 'entity', sort, order) }}
<th>Zeitstempel</th>
<th>Benutzer</th>
<th>Aktion</th>
<th>Entität</th>
<th>Details</th>
{{ sortable_header('IP-Adresse', 'ip', sort, order) }}
<th>IP-Adresse</th>
</tr>
</thead>
<tbody>
{% for log in logs %}
<tr>
<td>{{ log[1].strftime('%d.%m.%Y %H:%M:%S') }}</td>
<td><strong>{{ log[2] }}</strong></td>
<td>{{ log.timestamp.strftime('%d.%m.%Y %H:%M:%S') }}</td>
<td><strong>{{ log.username }}</strong></td>
<td>
<span class="action-{{ log[3] }}">
{% if log[3] == 'CREATE' %} Erstellt
{% elif log[3] == 'UPDATE' %}✏️ Bearbeitet
{% elif log[3] == 'DELETE' %}🗑️ Gelöscht
{% elif log[3] == 'LOGIN' %}🔑 Anmeldung
{% elif log[3] == 'LOGOUT' %}🚪 Abmeldung
{% elif log[3] == 'AUTO_LOGOUT' %}⏰ Auto-Logout
{% elif log[3] == 'EXPORT' %}📥 Export
{% elif log[3] == 'GENERATE_KEY' %}🔑 Key generiert
{% elif log[3] == 'CREATE_BATCH' %}🔑 Batch erstellt
{% elif log[3] == 'BACKUP' %}💾 Backup erstellt
{% elif log[3] == 'LOGIN_2FA_SUCCESS' %}🔐 2FA-Anmeldung
{% elif log[3] == 'LOGIN_2FA_BACKUP' %}🔒 2FA-Backup-Code
{% elif log[3] == 'LOGIN_2FA_FAILED' %}⛔ 2FA-Fehlgeschlagen
{% elif log[3] == 'LOGIN_BLOCKED' %}🚫 Login-Blockiert
{% elif log[3] == 'RESTORE' %}🔄 Wiederhergestellt
{% elif log[3] == 'PASSWORD_CHANGE' %}🔐 Passwort geändert
{% elif log[3] == '2FA_ENABLED' %}✅ 2FA aktiviert
{% elif log[3] == '2FA_DISABLED' %}❌ 2FA deaktiviert
{% else %}{{ log[3] }}
<span class="action-{{ log.action }}">
{% if log.action == 'CREATE' %} Erstellt
{% elif log.action == 'UPDATE' %}✏️ Bearbeitet
{% elif log.action == 'DELETE' %}🗑️ Gelöscht
{% elif log.action == 'LOGIN' %}🔑 Anmeldung
{% elif log.action == 'LOGOUT' %}🚪 Abmeldung
{% elif log.action == 'AUTO_LOGOUT' %}⏰ Auto-Logout
{% elif log.action == 'EXPORT' %}📥 Export
{% elif log.action == 'GENERATE_KEY' %}🔑 Key generiert
{% elif log.action == 'CREATE_BATCH' %}🔑 Batch erstellt
{% elif log.action == 'BACKUP' %}💾 Backup erstellt
{% elif log.action == 'LOGIN_2FA_SUCCESS' %}🔐 2FA-Anmeldung
{% elif log.action == 'LOGIN_2FA_BACKUP' %}🔒 2FA-Backup-Code
{% elif log.action == 'LOGIN_2FA_FAILED' %}⛔ 2FA-Fehlgeschlagen
{% elif log.action == 'LOGIN_BLOCKED' %}🚫 Login-Blockiert
{% elif log.action == 'RESTORE' %}🔄 Wiederhergestellt
{% elif log.action == 'PASSWORD_CHANGE' %}🔐 Passwort geändert
{% elif log.action == '2FA_ENABLED' %}✅ 2FA aktiviert
{% elif log.action == '2FA_DISABLED' %}❌ 2FA deaktiviert
{% else %}{{ log.action }}
{% endif %}
</span>
</td>
<td>
{{ log[4] }}
{% if log[5] %}
<small class="text-muted">#{{ log[5] }}</small>
{{ log.entity_type }}
{% if log.entity_id %}
<small class="text-muted">#{{ log.entity_id }}</small>
{% endif %}
</td>
<td class="audit-details">
{% if log[10] %}
<div class="mb-1"><small class="text-muted">{{ log[10] }}</small></div>
{% if log.additional_info %}
<div class="mb-1"><small class="text-muted">{{ log.additional_info }}</small></div>
{% endif %}
{% if log[6] and log[3] == 'DELETE' %}
{% if log.old_values and log.action == 'DELETE' %}
<details>
<summary>Gelöschte Werte</summary>
<div class="json-display">
{% for key, value in log[6].items() %}
{% for key, value in log.old_values.items() %}
<strong>{{ key }}:</strong> {{ value }}<br>
{% endfor %}
</div>
</details>
{% elif log[6] and log[7] and log[3] == 'UPDATE' %}
{% elif log.old_values and log.new_values and log.action == 'UPDATE' %}
<details>
<summary>Änderungen anzeigen</summary>
<div class="json-display">
<strong>Vorher:</strong><br>
{% for key, value in log[6].items() %}
{% if log[7][key] != value %}
{% for key, value in log.old_values.items() %}
{% if log.new_values[key] != value %}
{{ key }}: {{ value }}<br>
{% endif %}
{% endfor %}
<hr class="my-1">
<strong>Nachher:</strong><br>
{% for key, value in log[7].items() %}
{% if log[6][key] != value %}
{% for key, value in log.new_values.items() %}
{% if log.old_values[key] != value %}
{{ key }}: {{ value }}<br>
{% endif %}
{% endfor %}
</div>
</details>
{% elif log[7] and log[3] == 'CREATE' %}
{% elif log.new_values and log.action == 'CREATE' %}
<details>
<summary>Erstellte Werte</summary>
<div class="json-display">
{% for key, value in log[7].items() %}
{% for key, value in log.new_values.items() %}
<strong>{{ key }}:</strong> {{ value }}<br>
{% endfor %}
</div>
@@ -228,7 +228,7 @@
{% endif %}
</td>
<td>
<small class="text-muted">{{ log[8] or '-' }}</small>
<small class="text-muted">{{ log.ip_address or '-' }}</small>
</td>
</tr>
{% endfor %}

Datei anzeigen

@@ -26,9 +26,9 @@
<div class="card-body">
<h5 class="card-title">📅 Letztes erfolgreiches Backup</h5>
{% if last_backup %}
<p class="mb-1"><strong>Zeitpunkt:</strong> {{ last_backup[0].strftime('%d.%m.%Y %H:%M:%S') }}</p>
<p class="mb-1"><strong>Größe:</strong> {{ (last_backup[1] / 1024 / 1024)|round(2) }} MB</p>
<p class="mb-0"><strong>Dauer:</strong> {{ last_backup[2]|round(1) }} Sekunden</p>
<p class="mb-1"><strong>Zeitpunkt:</strong> {{ last_backup.id.strftime('%d.%m.%Y %H:%M:%S') }}</p>
<p class="mb-1"><strong>Größe:</strong> {{ (last_backup.filename / 1024 / 1024)|round(2) }} MB</p>
<p class="mb-0"><strong>Dauer:</strong> {{ last_backup.filesize|round(1) }} Sekunden</p>
{% else %}
<p class="text-muted mb-0">Noch kein Backup vorhanden</p>
{% endif %}
@@ -73,44 +73,44 @@
<tbody>
{% for backup in backups %}
<tr>
<td>{{ backup[6].strftime('%d.%m.%Y %H:%M:%S') }}</td>
<td>{{ backup.created_at.strftime('%d.%m.%Y %H:%M:%S') }}</td>
<td>
<small>{{ backup[1] }}</small>
{% if backup[11] %}
<small>{{ backup.filename }}</small>
{% if backup.is_encrypted %}
<span class="badge bg-info ms-1">🔒 Verschlüsselt</span>
{% endif %}
</td>
<td>
{% if backup[2] %}
{{ (backup[2] / 1024 / 1024)|round(2) }} MB
{% if backup.filesize %}
{{ (backup.filesize / 1024 / 1024)|round(2) }} MB
{% else %}
-
{% endif %}
</td>
<td>
{% if backup[3] == 'manual' %}
{% if backup.backup_type == 'manual' %}
<span class="badge bg-primary">Manuell</span>
{% else %}
<span class="badge bg-secondary">Automatisch</span>
{% endif %}
</td>
<td>
{% if backup[4] == 'success' %}
{% if backup.status == 'success' %}
<span class="status-success">✅ Erfolgreich</span>
{% elif backup[4] == 'failed' %}
<span class="status-failed" title="{{ backup[5] }}">❌ Fehlgeschlagen</span>
{% elif backup.status == 'failed' %}
<span class="status-failed" title="{{ backup.error_message }}">❌ Fehlgeschlagen</span>
{% else %}
<span class="status-in_progress">⏳ In Bearbeitung</span>
{% endif %}
</td>
<td>{{ backup[7] }}</td>
<td>{{ backup.created_by }}</td>
<td>
{% if backup[8] and backup[9] %}
{% if backup.tables_count and backup.records_count %}
<small>
{{ backup[8] }} Tabellen<br>
{{ backup[9] }} Datensätze<br>
{% if backup[10] %}
{{ backup[10]|round(1) }}s
{{ backup.tables_count }} Tabellen<br>
{{ backup.records_count }} Datensätze<br>
{% if backup.duration_seconds %}
{{ backup.duration_seconds|round(1) }}s
{% endif %}
</small>
{% else %}
@@ -118,20 +118,20 @@
{% endif %}
</td>
<td class="backup-actions">
{% if backup[4] == 'success' %}
{% if backup.status == 'success' %}
<div class="btn-group btn-group-sm" role="group">
<a href="/backup/download/{{ backup[0] }}"
<a href="/backup/download/{{ backup.id }}"
class="btn btn-outline-primary"
title="Backup herunterladen">
📥 Download
</a>
<button class="btn btn-outline-success"
onclick="restoreBackup({{ backup[0] }}, '{{ backup[1] }}')"
onclick="restoreBackup({{ backup.id }}, '{{ backup.filename }}')"
title="Backup wiederherstellen">
🔄 Wiederherstellen
</button>
<button class="btn btn-outline-danger"
onclick="deleteBackup({{ backup[0] }}, '{{ backup[1] }}')"
onclick="deleteBackup({{ backup.id }}, '{{ backup.filename }}')"
title="Backup löschen">
🗑️ Löschen
</button>

Datei anzeigen

@@ -33,12 +33,21 @@
<div class="card mb-3">
<div class="card-body">
<form method="get" action="/customers" id="customerSearchForm" class="row g-3 align-items-end">
<div class="col-md-10">
<div class="col-md-8">
<label for="search" class="form-label">🔍 Suchen</label>
<input type="text" class="form-control" id="search" name="search"
placeholder="Kundenname oder E-Mail..."
value="{{ search }}" autofocus>
</div>
<div class="col-md-2">
<div class="form-check mt-4">
<input class="form-check-input" type="checkbox" id="show_test" name="show_test" value="true"
{% if show_test %}checked{% endif %} onchange="this.form.submit()">
<label class="form-check-label" for="show_test">
🧪 Testdaten anzeigen
</label>
</div>
</div>
<div class="col-md-2">
<a href="/customers" class="btn btn-outline-secondary w-100">Zurücksetzen</a>
</div>
@@ -69,23 +78,23 @@
<tbody>
{% for customer in customers %}
<tr>
<td>{{ customer[0] }}</td>
<td>{{ customer.id }}</td>
<td>
{{ customer[1] }}
{% if customer[4] %}
{{ customer.name }}
{% if customer.is_test %}
<span class="badge bg-secondary ms-1" title="Testdaten">🧪</span>
{% endif %}
</td>
<td>{{ customer[2] or '-' }}</td>
<td>{{ customer[3].strftime('%d.%m.%Y %H:%M') }}</td>
<td>{{ customer.email or '-' }}</td>
<td>{{ customer.created_at.strftime('%d.%m.%Y %H:%M') }}</td>
<td>
<span class="badge bg-info">{{ customer[6] }}/{{ customer[5] }}</span>
<span class="badge bg-info">{{ customer.active_licenses }}/{{ customer.license_count }}</span>
</td>
<td>
<div class="btn-group btn-group-sm" role="group">
<a href="/customer/edit/{{ customer[0] }}" class="btn btn-outline-primary">✏️ Bearbeiten</a>
{% if customer[5] == 0 %}
<form method="post" action="/customer/delete/{{ customer[0] }}" style="display: inline;" onsubmit="return confirm('Kunde wirklich löschen?');">
<a href="/customer/edit/{{ customer.id }}" class="btn btn-outline-primary">✏️ Bearbeiten</a>
{% if customer.license_count == 0 %}
<form method="post" action="/customer/delete/{{ customer.id }}" style="display: inline;" onsubmit="return confirm('Kunde wirklich löschen?');">
<button type="submit" class="btn btn-outline-danger">🗑️ Löschen</button>
</form>
{% else %}

Datei anzeigen

@@ -13,27 +13,27 @@
<div class="card mb-4">
<div class="card-body">
<form method="post" action="/customer/edit/{{ customer[0] }}" accept-charset="UTF-8">
<form method="post" action="/customer/edit/{{ customer.id }}" accept-charset="UTF-8">
{% if request.args.get('show_test') == 'true' %}
<input type="hidden" name="show_test" value="true">
{% endif %}
<div class="row g-3">
<div class="col-md-6">
<label for="name" class="form-label">Kundenname</label>
<input type="text" class="form-control" id="name" name="name" value="{{ customer[1] }}" accept-charset="UTF-8" required>
<input type="text" class="form-control" id="name" name="name" value="{{ customer.name }}" accept-charset="UTF-8" required>
</div>
<div class="col-md-6">
<label for="email" class="form-label">E-Mail</label>
<input type="email" class="form-control" id="email" name="email" value="{{ customer[2] or '' }}" accept-charset="UTF-8">
<input type="email" class="form-control" id="email" name="email" value="{{ customer.email or '' }}" accept-charset="UTF-8">
</div>
<div class="col-12">
<label class="form-label text-muted">Erstellt am</label>
<p class="form-control-plaintext">{{ customer[3].strftime('%d.%m.%Y %H:%M') }}</p>
<p class="form-control-plaintext">{{ customer.created_at.strftime('%d.%m.%Y %H:%M') }}</p>
</div>
</div>
<div class="form-check mt-3">
<input class="form-check-input" type="checkbox" id="isTest" name="is_test" {% if customer[4] %}checked{% endif %}>
<input class="form-check-input" type="checkbox" id="isTest" name="is_test" {% if customer.is_test %}checked{% endif %}>
<label class="form-check-label" for="isTest">
<i class="fas fa-flask"></i> Als Testdaten markieren
<small class="text-muted">(Kunde und seine Lizenzen werden von der Software ignoriert)</small>

Datei anzeigen

@@ -13,42 +13,42 @@
<div class="card">
<div class="card-body">
<form method="post" action="/license/edit/{{ license[0] }}" accept-charset="UTF-8">
<form method="post" action="/license/edit/{{ license.id }}" accept-charset="UTF-8">
{% if request.args.get('show_test') == 'true' %}
<input type="hidden" name="show_test" value="true">
{% endif %}
<div class="row g-3">
<div class="col-md-6">
<label class="form-label">Kunde</label>
<input type="text" class="form-control" value="{{ license[2] }}" disabled>
<input type="text" class="form-control" value="{{ license.customer_name }}" disabled>
<small class="text-muted">Kunde kann nicht geändert werden</small>
</div>
<div class="col-md-6">
<label class="form-label">E-Mail</label>
<input type="email" class="form-control" value="{{ license[3] or '-' }}" disabled>
<input type="email" class="form-control" value="-" disabled>
</div>
<div class="col-md-6">
<label for="licenseKey" class="form-label">Lizenzschlüssel</label>
<input type="text" class="form-control" id="licenseKey" name="license_key" value="{{ license[1] }}" required>
<input type="text" class="form-control" id="licenseKey" name="license_key" value="{{ license.license_key }}" required>
</div>
<div class="col-md-6">
<label for="licenseType" class="form-label">Lizenztyp</label>
<select class="form-select" id="licenseType" name="license_type" required>
<option value="full" {% if license[4] == 'full' %}selected{% endif %}>Vollversion</option>
<option value="test" {% if license[4] == 'test' %}selected{% endif %}>Testversion</option>
<option value="full" {% if license.license_type == 'full' %}selected{% endif %}>Vollversion</option>
<option value="test" {% if license.license_type == 'test' %}selected{% endif %}>Testversion</option>
</select>
</div>
<div class="col-md-4">
<label for="validFrom" class="form-label">Gültig von</label>
<input type="date" class="form-control" id="validFrom" name="valid_from" value="{{ license[5].strftime('%Y-%m-%d') }}" required>
<input type="date" class="form-control" id="validFrom" name="valid_from" value="{{ license.valid_from.strftime('%Y-%m-%d') }}" required>
</div>
<div class="col-md-4">
<label for="validUntil" class="form-label">Gültig bis</label>
<input type="date" class="form-control" id="validUntil" name="valid_until" value="{{ license[6].strftime('%Y-%m-%d') }}" required>
<input type="date" class="form-control" id="validUntil" name="valid_until" value="{{ license.valid_until.strftime('%Y-%m-%d') }}" required>
</div>
<div class="col-md-4 d-flex align-items-end">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="isActive" name="is_active" {% if license[7] %}checked{% endif %}>
<input class="form-check-input" type="checkbox" id="isActive" name="is_active" {% if license.is_active %}checked{% endif %}>
<label class="form-check-label" for="isActive">
Lizenz ist aktiv
</label>
@@ -58,7 +58,7 @@
<label for="deviceLimit" class="form-label">Gerätelimit</label>
<select class="form-select" id="deviceLimit" name="device_limit" required>
{% for i in range(1, 11) %}
<option value="{{ i }}" {% if license[10] == i %}selected{% endif %}>{{ i }} {% if i == 1 %}Gerät{% else %}Geräte{% endif %}</option>
<option value="{{ i }}" {% if license.get('device_limit', 3) == i %}selected{% endif %}>{{ i }} {% if i == 1 %}Gerät{% else %}Geräte{% endif %}</option>
{% endfor %}
</select>
<small class="form-text text-muted">Maximale Anzahl gleichzeitig aktiver Geräte</small>
@@ -66,7 +66,7 @@
</div>
<div class="form-check mt-3">
<input class="form-check-input" type="checkbox" id="isTest" name="is_test" {% if license[9] %}checked{% endif %}>
<input class="form-check-input" type="checkbox" id="isTest" name="is_test" {% if license.is_test %}checked{% endif %}>
<label class="form-check-label" for="isTest">
<i class="fas fa-flask"></i> Als Testdaten markieren
<small class="text-muted">(wird von der Software ignoriert)</small>

Datei anzeigen

@@ -106,40 +106,40 @@
{% for license in licenses %}
<tr>
<td class="checkbox-cell">
<input type="checkbox" class="form-check-input form-check-input-custom license-checkbox" value="{{ license[0] }}">
<input type="checkbox" class="form-check-input form-check-input-custom license-checkbox" value="{{ license.id }}">
</td>
<td>{{ license[0] }}</td>
<td>{{ license.id }}</td>
<td>
<div class="d-flex align-items-center">
<code class="me-2">{{ license[1] }}</code>
<button class="btn btn-sm btn-outline-secondary btn-copy" onclick="copyToClipboard('{{ license[1] }}', this)" title="Kopieren">
<code class="me-2">{{ license.license_key }}</code>
<button class="btn btn-sm btn-outline-secondary btn-copy" onclick="copyToClipboard('{{ license.license_key }}', this)" title="Kopieren">
📋
</button>
</div>
</td>
<td>
{{ license[2] }}
{% if license[8] %}
{{ license.customer_name }}
{% if license.is_test %}
<span class="badge bg-secondary ms-1" title="Testdaten">🧪</span>
{% endif %}
</td>
<td>{{ license[3] or '-' }}</td>
<td>-</td>
<td>
{% if license[4] == 'full' %}
{% if license.license_type == 'full' %}
<span class="badge bg-success">Vollversion</span>
{% else %}
<span class="badge bg-warning">Testversion</span>
{% endif %}
</td>
<td>{{ license[5].strftime('%d.%m.%Y') }}</td>
<td>{{ license[6].strftime('%d.%m.%Y') }}</td>
<td>{{ license.valid_from.strftime('%d.%m.%Y') }}</td>
<td>{{ license.valid_until.strftime('%d.%m.%Y') }}</td>
<td>
{% if license[9] == 'abgelaufen' %}
<span class="status-abgelaufen">⚠️ Abgelaufen</span>
{% elif license[9] == 'läuft bald ab' %}
<span class="status-ablaufend">⏰ Läuft bald ab</span>
{% elif license[9] == 'deaktiviert' %}
{% if not license.is_active %}
<span class="status-deaktiviert">❌ Deaktiviert</span>
{% elif license.valid_until < now().date() %}
<span class="status-abgelaufen">⚠️ Abgelaufen</span>
{% elif license.valid_until < (now() + timedelta(days=30)).date() %}
<span class="status-ablaufend">⏰ Läuft bald ab</span>
{% else %}
<span class="status-aktiv">✅ Aktiv</span>
{% endif %}
@@ -147,15 +147,15 @@
<td>
<div class="form-check form-switch form-switch-custom">
<input class="form-check-input" type="checkbox"
id="active_{{ license[0] }}"
{{ 'checked' if license[7] else '' }}
onchange="toggleLicenseStatus({{ license[0] }}, this.checked)">
id="active_{{ license.id }}"
{{ 'checked' if license.is_active else '' }}
onchange="toggleLicenseStatus({{ license.id }}, this.checked)">
</div>
</td>
<td>
<div class="btn-group btn-group-sm" role="group">
<a href="/license/edit/{{ license[0] }}" class="btn btn-outline-primary">✏️ Bearbeiten</a>
<form method="post" action="/license/delete/{{ license[0] }}" style="display: inline;" onsubmit="return confirm('Wirklich löschen?');">
<a href="/license/edit/{{ license.id }}" class="btn btn-outline-primary">✏️ Bearbeiten</a>
<form method="post" action="/license/delete/{{ license.id }}" style="display: inline;" onsubmit="return confirm('Wirklich löschen?');">
<button type="submit" class="btn btn-outline-danger">🗑️ Löschen</button>
</form>
</div>

Datei anzeigen

@@ -320,7 +320,7 @@
</div>
<div class="col-md-2">
<label class="form-label">&nbsp;</label>
<a href="{{ url_for('resources', show_test=show_test) }}" class="btn btn-secondary w-100">
<a href="{{ url_for('resources.resources', show_test=show_test) }}" class="btn btn-secondary w-100">
🔄 Zurücksetzen
</a>
</div>
@@ -361,7 +361,7 @@
<thead>
<tr>
<th width="80">
<a href="{{ url_for('resources', sort='id', order='desc' if sort_by == 'id' and sort_order == 'asc' else 'asc', type=resource_type, status=status_filter, search=search, show_test=show_test) }}"
<a href="{{ url_for('resources.resources', sort='id', order='desc' if sort_by == 'id' and sort_order == 'asc' else 'asc', type=resource_type, status=status_filter, search=search, show_test=show_test) }}"
class="text-decoration-none text-dark sort-link">
ID
{% if sort_by == 'id' %}
@@ -372,7 +372,7 @@
</a>
</th>
<th width="120">
<a href="{{ url_for('resources', sort='type', order='desc' if sort_by == 'type' and sort_order == 'asc' else 'asc', type=resource_type, status=status_filter, search=search, show_test=show_test) }}"
<a href="{{ url_for('resources.resources', sort='type', order='desc' if sort_by == 'type' and sort_order == 'asc' else 'asc', type=resource_type, status=status_filter, search=search, show_test=show_test) }}"
class="text-decoration-none text-dark sort-link">
Typ
{% if sort_by == 'type' %}
@@ -383,7 +383,7 @@
</a>
</th>
<th>
<a href="{{ url_for('resources', sort='resource', order='desc' if sort_by == 'resource' and sort_order == 'asc' else 'asc', type=resource_type, status=status_filter, search=search, show_test=show_test) }}"
<a href="{{ url_for('resources.resources', sort='resource', order='desc' if sort_by == 'resource' and sort_order == 'asc' else 'asc', type=resource_type, status=status_filter, search=search, show_test=show_test) }}"
class="text-decoration-none text-dark sort-link">
Ressource
{% if sort_by == 'resource' %}
@@ -394,7 +394,7 @@
</a>
</th>
<th width="140">
<a href="{{ url_for('resources', sort='status', order='desc' if sort_by == 'status' and sort_order == 'asc' else 'asc', type=resource_type, status=status_filter, search=search, show_test=show_test) }}"
<a href="{{ url_for('resources.resources', sort='status', order='desc' if sort_by == 'status' and sort_order == 'asc' else 'asc', type=resource_type, status=status_filter, search=search, show_test=show_test) }}"
class="text-decoration-none text-dark sort-link">
Status
{% if sort_by == 'status' %}
@@ -405,7 +405,7 @@
</a>
</th>
<th>
<a href="{{ url_for('resources', sort='assigned', order='desc' if sort_by == 'assigned' and sort_order == 'asc' else 'asc', type=resource_type, status=status_filter, search=search, show_test=show_test) }}"
<a href="{{ url_for('resources.resources', sort='assigned', order='desc' if sort_by == 'assigned' and sort_order == 'asc' else 'asc', type=resource_type, status=status_filter, search=search, show_test=show_test) }}"
class="text-decoration-none text-dark sort-link">
Zugewiesen an
{% if sort_by == 'assigned' %}
@@ -416,7 +416,7 @@
</a>
</th>
<th width="180">
<a href="{{ url_for('resources', sort='changed', order='desc' if sort_by == 'changed' and sort_order == 'asc' else 'asc', type=resource_type, status=status_filter, search=search, show_test=show_test) }}"
<a href="{{ url_for('resources.resources', sort='changed', order='desc' if sort_by == 'changed' and sort_order == 'asc' else 'asc', type=resource_type, status=status_filter, search=search, show_test=show_test) }}"
class="text-decoration-none text-dark sort-link">
Letzte Änderung
{% if sort_by == 'changed' %}
@@ -433,13 +433,13 @@
{% for resource in resources %}
<tr>
<td>
<span class="text-muted">#{{ resource[0] }}</span>
<span class="text-muted">#{{ resource.id }}</span>
</td>
<td>
<div class="resource-icon {{ resource[1] }}">
{% if resource[1] == 'domain' %}
<div class="resource-icon {{ resource.resource_type }}">
{% if resource.resource_type == 'domain' %}
🌐
{% elif resource[1] == 'ipv4' %}
{% elif resource.resource_type == 'ipv4' %}
🖥️
{% else %}
📱
@@ -448,19 +448,19 @@
</td>
<td>
<div class="d-flex align-items-center">
<code class="me-2">{{ resource[2] }}</code>
<button class="copy-btn" onclick="copyToClipboard('{{ resource[2] }}', this)"
<code class="me-2">{{ resource.resource_value }}</code>
<button class="copy-btn" onclick="copyToClipboard('{{ resource.resource_value }}', this)"
title="Kopieren">
<i class="bi bi-clipboard"></i>
</button>
</div>
</td>
<td>
{% if resource[3] == 'available' %}
{% if resource.status == 'available' %}
<span class="status-badge status-available">
✅ Verfügbar
</span>
{% elif resource[3] == 'allocated' %}
{% elif resource.status == 'allocated' %}
<span class="status-badge status-allocated">
🔗 Zugeteilt
</span>
@@ -468,23 +468,23 @@
<span class="status-badge status-quarantine">
⚠️ Quarantäne
</span>
{% if resource[8] %}
<div class="small text-muted mt-1">{{ resource[8] }}</div>
{% if resource.status_changed_by %}
<div class="small text-muted mt-1">{{ resource.status_changed_by }}</div>
{% endif %}
{% endif %}
</td>
<td>
{% if resource[5] %}
{% if resource.customer_name %}
<div>
<a href="{{ url_for('customers_licenses', customer_id=resource[10] if resource[10] else '', show_test=show_test) }}"
<a href="{{ url_for('customers.customers_licenses', show_test=show_test) }}"
class="text-decoration-none">
<strong>{{ resource[5] }}</strong>
<strong>{{ resource.customer_name }}</strong>
</a>
</div>
<div class="small text-muted">
<a href="{{ url_for('edit_license', license_id=resource[4]) }}?ref=resources{{ '&show_test=true' if show_test else '' }}"
<a href="{{ url_for('licenses.edit_license', license_id=resource.allocated_to_license) }}?ref=resources{{ '&show_test=true' if show_test else '' }}"
class="text-decoration-none text-muted">
{{ resource[6] }}
{{ resource.allocated_to_license }}
</a>
</div>
{% else %}
@@ -492,21 +492,21 @@
{% endif %}
</td>
<td>
{% if resource[7] %}
{% if resource.status_changed_at %}
<div class="small">
<div>{{ resource[7].strftime('%d.%m.%Y') }}</div>
<div class="text-muted">{{ resource[7].strftime('%H:%M Uhr') }}</div>
<div>{{ resource.status_changed_at.strftime('%d.%m.%Y') }}</div>
<div class="text-muted">{{ resource.status_changed_at.strftime('%H:%M Uhr') }}</div>
</div>
{% else %}
<span class="text-muted">-</span>
{% endif %}
</td>
<td class="text-center">
{% if resource[3] == 'quarantine' %}
{% if resource.status == 'quarantine' %}
<!-- Quick Action für Quarantäne -->
<form method="post" action="/resources/release?show_test={{ show_test }}&type={{ resource_type }}&status={{ status_filter }}&search={{ search }}"
style="display: inline-block; margin-right: 5px;">
<input type="hidden" name="resource_ids" value="{{ resource[0] }}">
<input type="hidden" name="resource_ids" value="{{ resource.id }}">
<input type="hidden" name="show_test" value="{{ show_test }}">
<button type="submit"
class="btn btn-sm btn-success">
@@ -519,54 +519,54 @@
<div class="dropdown" style="display: inline-block;">
<button class="btn btn-sm btn-outline-secondary dropdown-toggle"
type="button"
id="dropdownMenuButton{{ resource[0] }}"
id="dropdownMenuButton{{ resource.id }}"
data-bs-toggle="dropdown"
aria-expanded="false">
<i class="bi bi-three-dots-vertical"></i> Aktionen
</button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton{{ resource[0] }}">
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton{{ resource.id }}">
<!-- Historie immer verfügbar -->
<li>
<a class="dropdown-item"
href="{{ url_for('resource_history', resource_id=resource[0]) }}">
href="{{ url_for('resource_history', resource_id=resource.id) }}">
<i class="bi bi-clock-history text-info"></i> Historie anzeigen
</a>
</li>
<li><hr class="dropdown-divider"></li>
{% if resource[3] == 'available' %}
{% if resource.status == 'available' %}
<!-- Aktionen für verfügbare Ressourcen -->
<li>
<button class="dropdown-item"
onclick="showQuarantineModal({{ resource[0] }})">
onclick="showQuarantineModal({{ resource.id }})">
<i class="bi bi-exclamation-triangle text-warning"></i> In Quarantäne setzen
</button>
</li>
{% elif resource[3] == 'allocated' %}
{% elif resource.status == 'allocated' %}
<!-- Aktionen für zugeteilte Ressourcen -->
{% if resource[4] %}
{% if resource.allocated_to_license %}
<li>
<a class="dropdown-item"
href="{{ url_for('edit_license', license_id=resource[4]) }}?ref=resources{{ '&show_test=true' if show_test else '' }}">
href="{{ url_for('edit_license', license_id=resource.allocated_to_license) }}?ref=resources{{ '&show_test=true' if show_test else '' }}">
<i class="bi bi-file-text text-primary"></i> Lizenz bearbeiten
</a>
</li>
{% endif %}
{% if resource[10] %}
{% if resource.id %}
<li>
<a class="dropdown-item"
href="{{ url_for('customers_licenses', customer_id=resource[10], show_test=show_test) }}">
href="{{ url_for('customers_licenses', customer_id=resource.id, show_test=show_test) }}">
<i class="bi bi-person text-primary"></i> Kunde anzeigen
</a>
</li>
{% endif %}
{% elif resource[3] == 'quarantine' %}
{% elif resource.status == 'quarantine' %}
<!-- Aktionen für Quarantäne-Ressourcen -->
<li>
<form method="post" action="/resources/release?show_test={{ show_test }}&type={{ resource_type }}&status={{ status_filter }}&search={{ search }}"
style="display: contents;">
<input type="hidden" name="resource_ids" value="{{ resource[0] }}">
<input type="hidden" name="resource_ids" value="{{ resource.id }}">
<input type="hidden" name="show_test" value="{{ show_test }}">
<button type="submit" class="dropdown-item">
<i class="bi bi-check-circle text-success"></i> Ressource freigeben
@@ -576,7 +576,7 @@
{% if resource[9] %}
<li>
<button class="dropdown-item"
onclick="extendQuarantine({{ resource[0] }})">
onclick="extendQuarantine({{ resource.id }})">
<i class="bi bi-calendar-plus text-warning"></i> Quarantäne verlängern
</button>
</li>
@@ -588,7 +588,7 @@
<!-- Kopieren immer verfügbar -->
<li>
<button class="dropdown-item"
onclick="copyToClipboard('{{ resource[2] }}', this)">
onclick="copyToClipboard('{{ resource.resource_value }}', this)">
<i class="bi bi-clipboard text-secondary"></i> Ressource kopieren
</button>
</li>
@@ -616,13 +616,13 @@
<ul class="pagination justify-content-center">
<li class="page-item {% if page == 1 %}disabled{% endif %}">
<a class="page-link"
href="{{ url_for('resources', page=1, type=resource_type, status=status_filter, search=search, show_test=show_test, sort=sort_by, order=sort_order) }}">
href="{{ url_for('resources.resources', page=1, type=resource_type, status=status_filter, search=search, show_test=show_test, sort=sort_by, order=sort_order) }}">
<i class="bi bi-chevron-double-left"></i> Erste
</a>
</li>
<li class="page-item {% if page == 1 %}disabled{% endif %}">
<a class="page-link"
href="{{ url_for('resources', page=page-1, type=resource_type, status=status_filter, search=search, show_test=show_test, sort=sort_by, order=sort_order) }}">
href="{{ url_for('resources.resources', page=page-1, type=resource_type, status=status_filter, search=search, show_test=show_test, sort=sort_by, order=sort_order) }}">
<i class="bi bi-chevron-left"></i> Zurück
</a>
</li>
@@ -631,7 +631,7 @@
{% if p == page or (p >= page - 2 and p <= page + 2) %}
<li class="page-item {% if p == page %}active{% endif %}">
<a class="page-link"
href="{{ url_for('resources', page=p, type=resource_type, status=status_filter, search=search, show_test=show_test, sort=sort_by, order=sort_order) }}">
href="{{ url_for('resources.resources', page=p, type=resource_type, status=status_filter, search=search, show_test=show_test, sort=sort_by, order=sort_order) }}">
{{ p }}
</a>
</li>
@@ -640,13 +640,13 @@
<li class="page-item {% if page == total_pages %}disabled{% endif %}">
<a class="page-link"
href="{{ url_for('resources', page=page+1, type=resource_type, status=status_filter, search=search, show_test=show_test, sort=sort_by, order=sort_order) }}">
href="{{ url_for('resources.resources', page=page+1, type=resource_type, status=status_filter, search=search, show_test=show_test, sort=sort_by, order=sort_order) }}">
Weiter <i class="bi bi-chevron-right"></i>
</a>
</li>
<li class="page-item {% if page == total_pages %}disabled{% endif %}">
<a class="page-link"
href="{{ url_for('resources', page=total_pages, type=resource_type, status=status_filter, search=search, show_test=show_test, sort=sort_by, order=sort_order) }}">
href="{{ url_for('resources.resources', page=total_pages, type=resource_type, status=status_filter, search=search, show_test=show_test, sort=sort_by, order=sort_order) }}">
Letzte <i class="bi bi-chevron-double-right"></i>
</a>
</li>

Datei anzeigen

@@ -87,12 +87,12 @@
<table class="table table-hover">
<thead>
<tr>
{{ active_sortable_header('Kunde', 'customer', active_sort, active_order) }}
{{ active_sortable_header('Lizenz', 'license', active_sort, active_order) }}
{{ active_sortable_header('IP-Adresse', 'ip', active_sort, active_order) }}
{{ active_sortable_header('Gestartet', 'started', active_sort, active_order) }}
{{ active_sortable_header('Letzter Heartbeat', 'last_heartbeat', active_sort, active_order) }}
{{ active_sortable_header('Inaktiv seit', 'inactive', active_sort, active_order) }}
<th>Kunde</th>
<th>Lizenz</th>
<th>IP-Adresse</th>
<th>Gestartet</th>
<th>Letzter Heartbeat</th>
<th>Inaktiv seit</th>
<th>Aktion</th>
</tr>
</thead>
@@ -146,12 +146,12 @@
<table class="table table-sm">
<thead>
<tr>
{{ ended_sortable_header('Kunde', 'customer', ended_sort, ended_order) }}
{{ ended_sortable_header('Lizenz', 'license', ended_sort, ended_order) }}
{{ ended_sortable_header('IP-Adresse', 'ip', ended_sort, ended_order) }}
{{ ended_sortable_header('Gestartet', 'started', ended_sort, ended_order) }}
{{ ended_sortable_header('Beendet', 'ended_at', ended_sort, ended_order) }}
{{ ended_sortable_header('Dauer', 'duration', ended_sort, ended_order) }}
<th>Kunde</th>
<th>Lizenz</th>
<th>IP-Adresse</th>
<th>Gestartet</th>
<th>Beendet</th>
<th>Dauer</th>
</tr>
</thead>
<tbody>