Files
tesm/srv/poe_manager/templates/settings.html
T
alientimandClaude Sonnet 5 6803929140 Hostname-Einstellung, DHCP-Range-Pflicht, Topbar-Ampel, Text-Straffung, Quelltext-Huerde
Hostname (Systemeinstellungen, neben Pruefintervall):
- hostnamectl set-hostname ueber die App, Validierung (RFC-1123-artiges
  Label), kein Revert-Timer noetig (kappt die Erreichbarkeit nicht wie
  eine IP-Aenderung). Live getestet inkl. Validierung und Revert.

DHCP: Range ist jetzt Pflicht, kein geratener Default mehr:
- dhcp_range_start/end/dns starten leer (Platzhaltertext statt fake-
  echt aussehendem Default) - ein Zufalls-Range haette den Dienst sonst
  unbemerkt mit einer zum echten Netz nicht passenden Konfiguration
  starten lassen koennen.
- write_dhcp_file und dhcp_service_action=enable_restart verweigern sich
  ohne eingetragene Range; _render_kea_config laesst "pools" ohne Range
  komplett weg statt einen kaputten Pool-String zu erzeugen.
- Neues, separates dhcp_gateway-Feld (optional) fuer einen vom
  automatisch erkannten Gateway abweichenden Router fuer die Clients.

Topbar-Ampel fuer den Kea-Dienst:
- Gruener/roter Punkt neben dem Pruefintervall-Timer, nur sichtbar mit
  settings_dhcp.view (ein einzelner, kurzer systemctl-Aufruf pro Request,
  nicht die volle Status-Erkennung). Live verifiziert (rot wenn gestoppt,
  gruen wenn gestartet).

Reservierungstabelle zeigt jetzt pro Client, welche eigene DHCP-Option
greift (global oder Client-Override, mit Wert im Tooltip).

Text-Straffung: die laengsten Hint-Texte und Code-Kommentare in
Templates/app.py gekuerzt (u.a. groups.html, settings_dhcp.html,
account.html, devices/switches/users/credentials.html, zwei grosse
Migrations-/DHCP-Kommentarbloecke in app.py) - Kernaussagen erhalten,
Redundanz entfernt.

Quelltext-Huerde (KEINE echte Sicherheit, nur Abschreckung): Rechtsklick
und DevTools-/Quelltext-Shortcuts per JS blockiert. Klar dokumentiert
in Kommentar + README, dass der Browser HTML/CSS/JS immer vollstaendig
ausliefert und das in Sekunden umgehbar ist - echte Absicherung bleiben
ausschliesslich die serverseitigen Rechteprüfungen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-11 11:28:29 +02:00

156 lines
8.0 KiB
HTML

{% extends "base.html" %}
{% set active_page = "settings_system" %}
{% block page_title %}Systemeinstellungen{% endblock %}
{% block page_sub %}<div class="topbar-sub">Prüfintervall und Netzwerkkonfiguration dieses Hosts</div>{% endblock %}
{% block content %}
<div class="settings-grid">
<div class="card card-pad">
<div class="section-head" style="margin-bottom:16px;">
<div>
<h2 style="font-size:16px;">Prüfintervall</h2>
<div class="hint">Wie oft sollen Geräte auf Erreichbarkeit geprüft werden?</div>
</div>
</div>
{% if current_user.has_permission('settings_system.edit') %}
<form method="post">
<div class="field">
<label for="interval">Intervall (Minuten)</label>
<input type="number" name="interval" id="interval" value="{{ interval }}" min="1" required>
<div class="field-hint">Der Hintergrund-Dienst (rpi-check.service) wird nach dem Speichern automatisch neu gestartet.</div>
</div>
<button type="submit" class="btn btn-primary btn-block">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>
Speichern & Service neustarten
</button>
</form>
{% else %}
<div class="field">
<label>Intervall (Minuten)</label>
<input type="number" value="{{ interval }}" disabled>
<div class="field-hint">Nur Lesezugriff — für Änderungen fehlt das Recht „Systemeinstellungen ändern“.</div>
</div>
{% endif %}
</div>
<div class="card card-pad">
<div class="section-head" style="margin-bottom:16px;">
<div>
<h2 style="font-size:16px;">Hostname</h2>
<div class="hint">Name dieses Hosts im Netzwerk.</div>
</div>
</div>
{% if current_user.has_permission('settings_system.edit') %}
<form method="post">
<div class="field">
<label for="hostname">Hostname</label>
<input type="text" name="hostname" id="hostname" value="{{ current_hostname or '' }}" pattern="[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]?" required>
</div>
<button type="submit" class="btn btn-primary btn-block">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>
Hostname setzen
</button>
</form>
{% else %}
<div class="field">
<label>Hostname</label>
<input type="text" value="{{ current_hostname or '' }}" disabled>
</div>
{% endif %}
</div>
<div class="card card-pad" style="grid-column:1 / -1;">
<div class="section-head" style="margin-bottom:16px;">
<div>
<h2 style="font-size:16px;">Netzwerkeinstellungen</h2>
<div class="hint">IP/DNS/DHCP-Umschaltung dieses Hosts (nicht der DHCP-<em>Server</em> unter „DHCP“).</div>
</div>
</div>
<div class="flex gap-2" style="align-items:center; margin-bottom:16px; flex-wrap:wrap;">
{% if net_backend == 'unknown' %}
<span class="pill unknown">Kein unterstütztes Backend erkannt</span>
{% else %}
<span class="pill online">{{ 'NetworkManager' if net_backend == 'networkmanager' else 'dhcpcd' }}</span>
{% endif %}
{% if net_state %}
<span class="mono text-faint" style="font-size:12px;">
{{ net_interface }}
{% if net_state.ok %}— {{ net_state.ip }}/{{ net_state.prefix }}{% if net_state.gateway %}, Gateway {{ net_state.gateway }}{% endif %}{% endif %}
</span>
<span class="pill {{ 'user' if net_state.mode == 'static' else ('online' if net_state.mode == 'dhcp' else 'unknown') }}">
{{ {'static': 'Statisch', 'dhcp': 'DHCP', 'unknown': 'Modus unbekannt'}[net_state.mode] }}
</span>
{% endif %}
</div>
{% if net_state and net_state.dns %}
<div class="text-faint" style="font-size:12px; margin-bottom:16px;">Aktuelle DNS-Server: <span class="mono">{{ net_state.dns|join(', ') }}</span></div>
{% endif %}
{% if pending_network_token %}
<div class="card-pad" style="background:var(--warning-dim); border-radius:var(--radius-sm); margin-bottom:16px;">
<p style="margin:0 0 12px; font-size:13px;">
Neue Netzwerkkonfiguration wurde angewendet. Wenn diese Seite gerade noch lädt, funktioniert die Verbindung —
bitte bestätigen, bevor automatisch zurückgerollt wird (nach {{ net_revert_seconds }}s ohne Bestätigung).
</p>
<form method="post">
<input type="hidden" name="confirm_network" value="{{ pending_network_token }}">
<button type="submit" class="btn btn-primary">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>
Verbindung funktioniert — bestätigen
</button>
</form>
</div>
{% elif net_backend != 'unknown' and current_user.has_permission('settings_system.edit') %}
<form method="post" data-confirm="Netzwerkkonfiguration wirklich ändern? Falls die Verbindung danach abbricht, wird die vorherige Konfiguration automatisch nach {{ net_revert_seconds }} Sekunden wiederhergestellt.">
<input type="hidden" name="apply_network" value="1">
<div class="field"><label>Interface</label>
<select name="net_interface">
{% for iface in net_interfaces %}
<option value="{{ iface }}" {% if iface == net_interface %}selected{% endif %}>{{ iface }}</option>
{% endfor %}
</select>
</div>
<div class="field"><label>Modus</label>
<select name="net_mode" id="netModeSelect" onchange="document.getElementById('netStaticFields').classList.toggle('hidden', this.value !== 'static')">
<option value="dhcp" {% if net_state.mode != 'static' %}selected{% endif %}>DHCP (automatisch)</option>
<option value="static" {% if net_state.mode == 'static' %}selected{% endif %}>Statisch</option>
</select>
</div>
<div id="netStaticFields" class="{{ 'hidden' if net_state.mode != 'static' }}">
<div class="field"><label>IP-Adresse</label>
<input type="text" name="net_ip" value="{{ net_state.ip if net_state.mode == 'static' else '' }}" placeholder="z.B. 192.168.1.50">
</div>
<div class="field"><label>Prefix (CIDR-Bits)</label>
<input type="number" name="net_prefix" min="1" max="32" value="{{ net_state.prefix if net_state.mode == 'static' else '' }}" placeholder="z.B. 24">
</div>
<div class="field"><label>Gateway</label>
<input type="text" name="net_gateway" value="{{ net_state.gateway if net_state.mode == 'static' else '' }}" placeholder="z.B. 192.168.1.1">
</div>
</div>
<div class="field"><label>DNS-Server</label>
<input type="text" name="net_dns" value="{{ net_state.dns|join(', ') if net_state and net_state.dns else '' }}" placeholder="z.B. 1.1.1.1, 8.8.8.8">
<div class="field-hint">Kommagetrennt. Leer lassen, um die per DHCP zugewiesenen DNS-Server zu verwenden.</div>
</div>
<button type="submit" class="btn btn-primary btn-block">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>
Netzwerkkonfiguration anwenden
</button>
<p class="text-faint" style="font-size:11px; margin-top:10px;">
⚠ Kann die Erreichbarkeit dieses Hosts unterbrechen. Ohne Bestätigung wird automatisch nach {{ net_revert_seconds }}s zurückgerollt.
</p>
</form>
{% elif net_backend == 'unknown' %}
<p class="text-faint" style="font-size:12.5px;">
Weder NetworkManager noch dhcpcd aktiv erkannt — Netzwerkänderungen über diese Seite sind deaktiviert.
Bitte die Netzwerkkonfiguration dieses Hosts manuell vornehmen.
</p>
{% else %}
<p class="text-faint" style="font-size:12.5px;">Für Änderungen fehlt das Recht „Systemeinstellungen ändern“.</p>
{% endif %}
</div>
</div>
{% endblock %}