M24: LAN-Scanner Aktionen-Button, Traffic-Monitor+Sparkline, ARP-Fix

- Rechtsklick-Kontextmenü ersetzt durch sichtbaren "Aktionen"-Button
  je Geräte-Zeile (bisher nicht diskoverbar)
- Live-Traffic pro Port (↓/↑, wiederverwendet InterfaceTrafficMonitor)
  plus kleines Sparkline-Liniendiagramm der letzten 10 Sekunden
  (Swift Charts, neuer TrafficSample-Typ)
- Bug 34: ARP-Tabelle kann mehrere Zeilen für dieselbe MAC halten
  (reachable + stale/failed) — Auflösung bevorzugte bisher blind die
  zuletzt gesehene Zeile statt die erreichbare. Zwei Regressionstests.
- Nebenbefund: dedizierte SSH-Dienste (Backup/NetworkTools/Traffic/
  Update/FactoryReset) haben keinen eigenen Bestätigungspfad für einen
  neuen SSH-Host-Key, scheitern still solange REST verbindet — als
  offener Punkt dokumentiert.

Alles live bestätigt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Kay
2026-09-16 11:46:01 +02:00
co-authored by Claude Sonnet 5
parent 567feed93a
commit 4cc30a2016
7 changed files with 310 additions and 6 deletions
+78
View File
@@ -1418,6 +1418,69 @@ Sektion einklappbar machen"). Zwei Schritte:
`.listStyle(.sidebar)` an der `List` ergänzt, für den nativen
macOS-Sidebar-Look. Live bestätigt ("sehr gut, bin sehr zufrieden").
**M24: LAN-Scanner — "Aktionen"-Button, Traffic-Monitor pro Port +
Sparkline, ARP-Auflösungsbug gefixt** (2026-09-16). Drei Nutzerwünsche
in einer Runde:
1. **"Aktionen"-Button statt Rechtsklick** ("der Rechtsklick ist nicht
eindeutig erkennbar oder intuitiv, verlager bitte alle Funktionen
des Rechtsklicks in den Button") — `.contextMenu` komplett entfernt,
derselbe `deviceMenu(for:)`-Inhalt (Feste IP zuweisen/entfernen,
Netzwerk-Tools, Rohdaten anzeigen) läuft jetzt über einen sichtbaren
`Menu`-Button ("•••", Label "Aktionen") hinter jeder Geräte-Zeile.
2. **Traffic-Monitor pro Port + Sparkline** ("ein Traffic-Monitor im
LAN-Scanner", danach "ein kleines Liniendiagramm der letzten 10
Sekunden ... pro Port") — pro-*Gerät* geht nicht ohne RouterOS-
seitige Queue-Trees pro MAC (Nutzer selbst dagegen entschieden:
"Traffic pro PORT statt pro Gerät"), daher denselben bereits
gebauten `InterfaceTrafficMonitor` (Verbinden-Tab) wiederverwendet —
`DevicesViewModel` bekam eigene `startTrafficPolling`/
`stopTrafficPolling` (3s-Takt, gleiches Muster wie
`ConnectViewModel`). Live-↓/↑-Zahl direkt neben jedem Port-Titel,
plus neuer `TrafficSample`-Typ (`Core/Models/InterfaceTraffic.swift`)
für eine nach echter Zeit (nicht fester Sample-Anzahl) auf 10
Sekunden getrimmte Historie, gerendert als `Chart`/`LineMark`
(Swift Charts, `TrafficSparkline` in `DevicesView.swift`) ohne
Achsen — reiner Trend-Blick, die genaue Zahl steht ja daneben.
3. **Bug 34, dabei live gefunden:** ein Laptop an `ether4` (Traffic
sichtbar aktiv) erschien trotzdem unter "Unbekannter Port". Ursache:
RouterOS' `/ip arp`-Tabelle hielt für dieselbe MAC-Adresse zwei
Zeilen gleichzeitig — eine korrekte (`interface=ether4,
status=reachable`) und eine veraltete (`interface=bridge,
status=failed`, Überbleibsel von vor einem Netzwerkwechsel des
Geräts). `DevicesViewModel.buildDevices` baute `arpInterfaceByMAC`
bisher per blindem Überschreiben der letzten gesehenen Zeile auf —
welche Zeile "gewann" hing von der Router-internen Tabellen-
Reihenfolge ab, nicht von Korrektheit. Fix: `status=reachable`
gewinnt jetzt immer gegen jeden anderen Status, bei Gleichstand ein
konkreter Port gegen "bridge". Zwei neue Regressionstests
(`testReachableArpRowWinsOverStaleFailedRowForSameMAC`/
`...RegardlessOfOrder`, letzterer prüft explizit, dass der Fix nicht
nur für die live beobachtete Reihenfolge zufällig funktioniert).
**Nebenbefund beim Debuggen von Punkt 2, kein Code-Bug, aber ein
Betriebshinweis:** der Traffic-Monitor zeigte zunächst dauerhaft nichts
an — `InterfaceTrafficMonitor.fetchTraffic` scheiterte still (`try?`)
mit `untrustedSSHHostKey`. Ursache: der Router hatte seit dem letzten
Werksreset (2026-09-15) einen neuen SSH-Host-Key, aber da die Haupt-
verbindung dieser Session durchgehend über REST lief (M22-Fix), kam der
bereits vorhandene "Unbekannter SSH-Schlüssel"-Bestätigungsdialog nie
zum Zug — der lief bisher nur im SSH-*Fallback*-Pfad von
`ConnectionService.connect`. Dedizierte SSH-Verbindungen (Backup,
Netzwerk-Tools, Traffic-Monitor, Update, Werksreset) haben *keinen*
eigenen UI-Bestätigungspfad für einen neuen Host-Key, scheitern bei
einem Mismatch einfach still. Behoben durch `www-ssl` kurz deaktiviert
(erzwingt den SSH-Fallback-Pfad in der Haupt-Verbindung), Nutzer hat
den neuen Fingerprint einmalig über den bestehenden Dialog bestätigt
(`SSHHostKeyTrustStore` ist ein einziger, über `UserDefaults.standard`
geteilter Speicher — einmal dort vertraut gilt für jede dedizierte
SSH-Verbindung mit), danach `www-ssl` wieder aktiviert. **Bleibt ein
echter, noch offener Schwachpunkt:** falls der SSH-Host-Key sich künftig
nochmal ändert (z.B. nach einem weiteren Werksreset) UND REST zu diesem
Zeitpunkt bereits verbunden ist, würden dieselben dedizierten Dienste
wieder wortlos nichts tun, ohne Hinweis auf die Ursache — siehe
"Nächste Schritte".
## Nächste Schritte
1. ~~M16: restliche `RouterOSSchemaCatalog.swift`-Sektionen übersetzen~~,
@@ -1576,6 +1639,21 @@ Sektion einklappbar machen"). Zwei Schritte:
DHCP-Netzwerk-Optionen zwei Hops entfernt), Klick auf Pool/Adresse
bleibt bei der 1-Hop-Regel — beides wie erwartet bestätigt
("passt, live bestätigt").
13. **Dedizierte SSH-Dienste scheitern still bei SSH-Host-Key-Mismatch,
solange REST verbindet** (siehe M24-Nebenbefund oben) —
`BackupService`/`NetworkToolsService`/`InterfaceTrafficMonitor`/
`UpdateService`/`FactoryResetService` haben keinen eigenen
UI-Bestätigungspfad für einen neuen SSH-Host-Key, nur
`ConnectionService.connect`s SSH-*Fallback* zeigt den Dialog — und
der wird nie erreicht, solange REST erfolgreich verbindet. Ein
künftiger Host-Key-Wechsel (z.B. nach einem weiteren Werksreset)
würde also wieder zu wortlos leeren Ergebnissen führen (leere
Traffic-Anzeige, fehlschlagende Backups, etc.), ohne erkennbaren
Grund in der UI. Mögliche Fixes: diese Dienste bei
`untrustedSSHHostKey` einen eigenen Bestätigungsdialog zeigen
lassen, oder `ConnectionService` beim Verbinden zusätzlich (nicht
nur im Fallback-Fall) einmal den SSH-Host-Key prüfen/bestätigen
lassen, unabhängig davon, ob REST erfolgreich war.
Gitea-Remote `origin` ist eingerichtet und wird laufend gepusht (siehe
oben) — dieser Hinweis war veraltet, korrigiert am 2026-09-15.