{% extends "tesm_core/base.html" %} {% from "tesm_core/macros.html" import alert, pill, stat %} {% block page_title %}Diagnose{% endblock %} {% block page_sub %}Schemastand, Protokollintegrität, Schlüssel und Systemzugriff{% endblock %} {% block content %}
{{ stat('Benutzer', counts['Benutzer']) }} {{ stat('Gruppen', counts['Gruppen']) }} {{ stat('Aktive Sitzungen', counts['Aktive Sitzungen']) }} {{ stat('Protokollzeilen', counts['Protokollzeilen']) }}
{{ icon('shield', size=17) }} Änderungsprotokoll {% if chain.ok %}{{ pill('unversehrt', 'online') }}{% else %}{{ pill('Kette gebrochen', 'danger') }}{% endif %}
{{ chain.checked }} Zeilen geprüft (Hash-Kette)
{% if chain.ok %}

Jede Zeile bindet den Hash ihrer Vorgängerin ein. Eine nachträglich entfernte oder geänderte Zeile würde hier auffallen.

{% else %} {{ alert(chain.reason ~ ' Erster Bruch bei Eintrag ' ~ chain.broken_at ~ '.', 'danger', 'Manipulation oder Datenverlust') }} {% endif %}
{{ icon('key', size=17) }} Schlüssel und Dateirechte {% if key_problems %}{{ pill('Problem', 'danger') }}{% else %}{{ pill('in Ordnung', 'online') }}{% endif %}
Aktiver Datenschlüssel: {{ keystore_active }} (vorhanden: {{ keystore_keys | join(', ') }})
{% if current_user.is_admin %}
{% endif %}
{% if key_problems %}
    {% for problem in key_problems %}
  • {{ problem }}
  • {% endfor %}
{% else %}

Alle Schlüsseldateien liegen mit Modus 0600. Feldverschlüsselung ist AES-256-GCM mit rotierbarem Schlüsselbund; jedes Chiffrat ist an seinen Verwendungszweck gebunden.

{% endif %}
{{ icon('database', size=17) }} Schemastand
{{ db_path }} · {{ db_size | bytes }} · {{ migrations | length }} angewendete Migrationen
{% for entry in migrations %} {% endfor %}
Komponente Version Migration Prüfsumme Angewendet
{{ entry.component }} {{ entry.version }} {{ entry.name }} {{ entry.checksum }} {{ entry.applied_at | dt }}
{{ icon('server', size=17) }} Systemzugriff {% if not sysops_available %}{{ pill('nicht verfügbar', 'warning') }} {% elif sysops_selftest and sysops_selftest.ok %}{{ pill('Helfer antwortet', 'online') }} {% else %}{{ pill('Helfer antwortet nicht', 'danger') }}{% endif %}
{{ sysops_helper or 'kein Pfad konfiguriert' }}

Der Webprozess läuft unprivilegiert. Alles, was Root-Rechte braucht, geht durch genau ein Helferskript mit Positivliste – nicht durch die Anwendung selbst.

{% if not sysops_available %} {{ alert('Ohne Helfer sind Systemaktionen (Dienste, Netzwerk, Zertifikate) deaktiviert. Alles andere funktioniert normal -- das ist der erwartete Zustand in einer Entwicklungsumgebung.', 'info') }} {% elif sysops_selftest and not sysops_selftest.ok %} {# Geprüft wird ein echter Aufruf, nicht die blosse Existenz der Datei. Genau daran lag es einmal: die Seite meldete "erreichbar", während jeder Aufruf scheiterte. #} {{ alert(sysops_selftest.message or 'Der Helfer lässt sich nicht aufrufen.', 'danger', 'Systemaktionen funktionieren nicht') }} {% endif %}
{{ icon('info', size=17) }} Laufzeit
{% if current_user.is_admin %}
{% endif %}
{% for label, value in runtime.items() %}
{{ label }}
{{ value }}
{% endfor %}
Protokollverzeichnis
{{ log_dir }}
{% if license_state %}
{{ icon('key', size=17) }} Lizenzzustand {% if license_state.valid and license_state.activated %}{{ pill('aktiv', 'online') }} {% elif license_state.valid %}{{ pill('nicht aktiviert', 'warning') }} {% else %}{{ pill('ungültig', 'danger') }}{% endif %}
Datei vorhanden
{{ license_state.present | yes_no }}
Signatur gültig
{{ license_state.valid | yes_no }}
Aussteller gepinnt
{{ license_state.issuer_pinned | yes_no }}
Rolle
{{ license_state.role }}
Module
{{ license_state.modules | sort | join(', ') or '–' | safe }}
Letzter Heartbeat
{{ license_state.last_heartbeat_at | dt }}
{% if license_state.error %}
Fehler
{{ license_state.error }}
{% endif %} {% if license_state.warnings %}
Hinweise
{{ license_state.warnings | join('; ') }}
{% endif %}
{% endif %} {% endblock %}