M9/M10: Einfach/Experte-Modus + Experte-Tab (generischer RouterOS-Zugriff)

M9: Einrichten-Wizard bekommt einen Einfach/Experte-Modusschalter
(ModeStepView). Einfach überspringt VLAN, erlaubt nur ein LAN-Netzwerk
ohne Isolation, Firewall-Grundschutz fest an.

M10: neuer "Experte"-Tab mit generischem Motor (RouterOSMenuItem,
RouterOSCommand.remove, ConnectionService.fetchMenuItems, freies
"eigener Menüpfad"-Feld) plus kuratierten Formularen mit Tooltips
(RouterOSSchemaCatalog) für Firewall/NAT/Mangle/Raw/Adress-Listen,
Interfaces, IP, VPN, WLAN, Queues, System, Werkzeuge.

Live gegen einen hEX-Testrouter verifiziert (erst per SSH, dann vom
Nutzer selbst in der App), dabei 7 reale Bugs gefunden und gefixt —
der wichtigste: RouterOS' SSH-CLI gibt bei fehlgeschlagenen Befehlen
Exit-Code 0 zurück, wodurch apply() app-weit Fehler verschluckte statt
sie zu melden. Danach ergänzt: Bestätigungsdialog vor Anlegen/Ändern
+ Auto-Backup vor dem ersten Experte-Tab-Schreibvorgang je Sitzung
(Angleichung an den Wizard), sowie ein Dauer-Editor (Tage/Std/Min/Sek)
für Lease-/Ablaufzeit-Felder statt Freitext.

Details zu allen Bugs/Fixes: HANDOFF.md, CHATLOG.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EW3r6rW1xCf6UT5jNvt6rn
This commit is contained in:
Kay
2026-09-14 00:03:50 +02:00
co-authored by Claude Sonnet 5
parent 97fc216b0c
commit c9ecd3a0a9
21 changed files with 2252 additions and 32 deletions
+305 -9
View File
@@ -6,8 +6,25 @@ teilweise gegen zwei echte physische Mikrotik-Testgeräte verifiziert
kritischer Parser-Bug gefunden und gefixt, siehe unten). Danach noch
mehrere Nutzer-Wünsche umgesetzt: Tooltips, wählbarer Backup-Ordner,
Schnell-Backup im Verbinden-Tab, "Werkseinstellungen wiederherstellen",
eigenes App-Icon. Release-Build liegt unter
`/Applications/RouterOS Assistant.app`.
eigenes App-Icon.
Neu in dieser Session: Einrichten-Wizard hat jetzt einen Einfach/Experte-
Modusschalter (M9), und ein komplett neuer "Experte"-Tab (M10) gibt
Formularzugriff auf einen großen, aber bewusst kuratierten Ausschnitt von
RouterOS (Firewall/NAT/Mangle/Raw/Adress-Listen mit vollen Tooltips,
plus Interfaces/IP/VPN/WLAN/Queues/System/Werkzeuge, plus ein "eigener
Menüpfad"-Feld für alles, was nicht kuratiert ist). Live gegen den
hEX-Testrouter (RouterOS 7.24.2) verifiziert — erst per direktem SSH,
dann zusätzlich vom Nutzer selbst in der laufenden App (VLAN anlegen,
DHCP-Server mit Adress-Pool zuweisen, löschen) — dabei **sieben** echte
Bugs gefunden und gefixt (Bug 713 unten, nach jedem Fund Release neu
gebaut und deployt). **Bug 10 ist der wichtigste Fund dieser Session:**
RouterOS' SSH-CLI gibt bei fehlgeschlagenen Befehlen trotzdem Exit-Code 0
zurück — betraf `apply()` app-weit, nicht nur den Experte-Tab (siehe
unten). Release-Build unter `/Applications/RouterOS Assistant.app` ist
auf aktuellem Arbeitsstand (alle M9/M10-Änderungen + alle Bugfixes
deployt und vom Nutzer bestätigt) — **noch nicht committed** (siehe
`git status`), letzter Commit ist weiterhin `97fc216`.
## Ziel
@@ -57,25 +74,29 @@ RouterOSAssistant/
App/RouterOSAssistantApp.swift — 3 Tabs, teilen sich EINE ConnectionService-Instanz
Core/
Models/
RouterOSCommand.swift — eine Änderung, zwei Operationen (.add/.set), zwei Renderer (CLI-Zeile / REST-JSON)
RouterOSCommand.swift — eine Änderung, drei Operationen (.add/.set/.remove), zwei Renderer (CLI-Zeile / REST-JSON)
WanConfig.swift, LanDhcpConfig.swift, VlanEntry.swift, WifiNetworkConfig.swift, FirewallConfig.swift — bauen je RouterOSCommand-Listen
DhcpServerCommandBuilder.swift — geteilte "Adresse+Pool+Server+Netzwerk"-Logik (LAN + VLAN)
RouterOSModels.swift — Credentials, DeviceInfo, Interface, RouterOSError
RouterOSMenuItem.swift — generisches Item (id + [String:String] fields) für JEDEN RouterOS-Menüpfad (M10)
RouterOSSchema.swift — RouterOSFieldSchema/RouterOSMenuSchema/RouterOSMenuCategory: kuratiertes Formular-Schema fürs Experte-Tool (M10)
RouterOSSchemaCatalog.swift — die eigentliche Menü-Liste: Firewall-Familie voll kuratiert, Rest teils kuratiert/teils generisch (M10)
Networking/
RouterOSTransport.swift — Protocol: connect/fetchDeviceInfo/fetchInterfaces/fetchFirewallRuleCounts/apply/disconnect
RestTransport.swift — REST-API (RouterOS ≥7.1), Zertifikats-TOFU, .set via GET+PATCH (findItemID)
SSHTransport.swift — SSH-Fallback via Citadel, CLI-Text-Parsing, resetToFactoryDefaults(), eigene Hostkey-TOFU
RouterOSCliParser.swift — parst `/system resource print` und `/interface print terse`
RouterOSTransport.swift — Protocol: connect/fetchDeviceInfo/fetchInterfaces/fetchFirewallRuleCounts/fetchMenuItems/apply/disconnect
RestTransport.swift — REST-API (RouterOS ≥7.1), Zertifikats-TOFU, .set via GET+PATCH (findItemID), .remove via GET+DELETE, fetchMenuItems (Array- oder Singleton-Objekt)
SSHTransport.swift — SSH-Fallback via Citadel, CLI-Text-Parsing, resetToFactoryDefaults(), eigene Hostkey-TOFU, fetchMenuItems (generisch, inkl. Singleton-Fallback)
RouterOSCliParser.swift — parst `/system resource print`, `/interface print terse`, und generisch jeden Menüpfad (parseGenericItems/parseSingletonItem, M10)
CertificateTrustStore.swift / CertificateFingerprint.swift — TOFU für REST-Zertifikate
SSHHostKeyTrustStore.swift / SSHHostKeyFingerprint.swift — TOFU für SSH-Hostkeys (M7)
Services/
ConnectionService.swift — zentraler App-State: REST-zuerst-SSH-Fallback, hält credentials/interfaces/deviceInfo
ConnectionService.swift — zentraler App-State: REST-zuerst-SSH-Fallback, hält credentials/interfaces/deviceInfo, plus generisches fetchMenuItems
BackupService.swift — Config-Export (`/export terse`) über eigene SSH-Verbindung, wählbarer Zielordner
FactoryResetService.swift — /system reset-configuration über eigene SSH-Verbindung ("Gefahrenzone")
KeychainService.swift — Passwort-Speicherung
Features/
Wizard/Steps/Connect/ — Verbinden-Tab (inkl. Schnell-Backup-Button nach erfolgreicher Verbindung)
Wizard/Steps/Setup/ — Einrichten-Tab: Wan → Lan → Vlan → Wifi → Firewall → Review/Apply
Wizard/Steps/Setup/ — Einrichten-Tab: Mode → Wan → Lan → (Vlan nur Experte) → Wifi → Firewall → Review/Apply
Expert/ — Experte-Tab (M10): ExpertView (Kategorie-/Menüliste + "eigener Pfad"), ExpertViewModel, ExpertMenuDetailView (Item-Liste + Add/Edit-Sheet)
Backup/ — Sicherungen-Tab (Ordner wählen, Gefahrenzone: Werkseinstellungen wiederherstellen)
Resources/Assets.xcassets/AppIcon.appiconset/ — App-Icon "Signal Router" (16px1024px)
RouterOSAssistantTests/ — reine Unit-Tests (Command-Builder, CLI-Parser, Fallback-Logik via Mock-Transport)
@@ -139,11 +160,112 @@ erreichen (siehe Bug 1 unten).
Auswahllisten gefiltert. Regressionstest mit echten `\r\n`-getrennten
hEX-Daten in `RouterOSCliParserTests`.
7. **`/ip service`: Feld heißt `available-from`, nicht `address`** — beim
Kuratieren des Experte-Schemas geraten statt geprüft. Live gegen den
hEX bestätigt: `/ip service print` zeigt tatsächlich `available-from=`;
ein `set ... address=...` hätte den Zugriffsfilter stillschweigend
nicht gesetzt. Schema korrigiert (`RouterOSSchemaCatalog.swift`).
8. **RouterOS 7.24.2 zeigt `.id` gar nicht in `print terse`, und
Singleton-Menüs verweigern `terse` komplett** — Annahme beim Bau des
generischen Experte-Motors (M10) war, `.id=*N` sei Teil jeder
`print terse`-Zeile (wie bei REST-JSON). Live-Test zeigte: RouterOS
7.24.2 liefert dort gar kein `.id`-Feld; Menüs mit genau einem Eintrag
statt einer Liste (`/ip dns`, `/system identity`, `/system clock`,
`/system ntp client`, `/tool e-mail`) lehnen `terse` sogar komplett ab
(`"bad parameter terse"`). Fix: `:put [<Menüpfad> find]` liefert die
echten internen IDs (z.B. `*C;*1;*2`) — live bestätigt, dass die
Reihenfolge exakt der von `print terse` entspricht, damit per Position
zuordenbar. Für Singleton-Menüs neuer `RouterOSMenuSchema.isSingleton`-
Schalter: Fetch fällt auf normales `print` (Doppelpunkt-Format,
`parseSingletonItem`) zurück, `.set` läuft ohne `[find ...]` direkt
(`matchField: ""` als Sentinel in `RouterOSCommand`/beiden Transports).
Beides live gegen `/system identity` durchgespielt (Name testweise
geändert, Fehlertext exakt geprüft, zurückgesetzt).
9. **Cross-Referenz-Felder (z.B. DHCP-Server → Adress-Pool) waren reiner
Freitext** — Nutzer meldete: "kann den vorher angelegten Adress-Pool
nicht in der Auswahl sehen". Betraf jedes Feld, das den Namen eines
Eintrags aus einem *anderen* Menü referenziert (DHCP-Server→Pool,
PPP-Benutzer→Profil, Scheduler→Skript, Firewall-Filter→Adress-Liste,
WireGuard-Peer→Interface) — alle als `.text` statt als Auswahl gebaut.
Fix: neuer Feldtyp `RouterOSFieldSchema.Kind.menuItemPick(menuPath:
restPath:valueField:)`, lädt beim Öffnen eines Menüs live die
existierenden Namen aus dem referenzierten Menü
(`ExpertViewModel.loadCrossReferenceOptions`) und zeigt sie als Picker.
Auf alle fünf betroffenen Felder angewendet.
10. **Kritisch, app-weit: RouterOS' SSH-CLI gibt bei fehlgeschlagenen
Befehlen Exit-Code 0 zurück** — Nutzer meldete: DHCP-Server-Anlage
zeigte keine Fehlermeldung, wurde aber auch nicht angelegt. Live
reproduziert: `/ip dhcp-server add ...` auf einem Interface mit
bereits bestehendem Server lieferte `"failure: server or relay with
such interface already exists"` **und Exit-Status 0**; ein ungültiger
`action=`-Wert lieferte `"syntax error (...)"`, ebenfalls Exit-Status
0 (per `expect`-Skript mit `catch wait result` geprüft). `SSHTransport.
apply()` warf bisher nur bei einem von Citadel gemeldeten
Nicht-Null-Exit-Code — also nie. **Betraf jeden `.apply()`-Aufruf über
SSH app-weit**, nicht nur den Experte-Tab: der komplette Einrichten-
Wizard (WAN/LAN/VLAN/Firewall) lief bei jedem SSH-Schreibvorgang mit
demselben blinden Fleck, seit M3. Fix: `apply()` behandelt jede
nicht-leere Ausgabe eines add/set/remove-Befehls als Fehler — laut
allen in dieser Session live getesteten Befehlsfamilien (15+) sind
erfolgreiche mutierende Befehle immer still. Nach dem Fix live end-to-
end bestätigt: komplette Kette VLAN→IP-Adresse→Pool→DHCP-Server→
DHCP-Netzwerk auf einem freien Interface angelegt, lief fehlerfrei
durch, danach vollständig wieder entfernt.
11. **Bool-Felder im Experte-Schema als `"true"/"false"` statt `"yes"/"no"`
geschrieben** — Nutzer meldete nach Bug-10-Fix: VLAN-Anlage zeigte
jetzt `"Unerwartete Antwort vom Router: syntax error (line 1 column
30)"`. Live reproduziert mit der exakt von der App erzeugten
Befehlszeile (`disabled=false`) — identischer Fehler; mit
`disabled=no` lief es durch. RouterOS' CLI akzeptiert nur `yes`/`no`
für Bool-Parameter, nicht `true`/`false`. Betraf praktisch jedes
kuratierte Schema (23× `"false"`, 4× `"true"` als `defaultValue`).
Fix zweigleisig: `ExpertViewModel.pendingCommand` normalisiert jeden
`.bool`-Feldwert vor dem Senden auf `yes`/`no` (Schutz unabhängig von
der Quelle), zusätzlich Katalog-Strings selbst bereinigt. Live erneut
end-to-end bestätigt (VLAN→IP→Pool→DHCP-Server mit `disabled=no`).
12. **Lösch-Buttons fehlten komplett im Experte-Tab** — Nutzer: "mir
fehlen komplett die Löschen-Buttons". Ursache: `.swipeActions(...)`
ist eine iOS/iPadOS-Wischgeste ohne Entsprechung auf macOS-Listen —
dort erscheint schlicht nichts, unabhängig vom Code. Fix: sichtbarer
Papierkorb-Icon-Button je Zeile statt Wisch-Geste (nur bei
Nicht-Singleton-Menüs, wo Löschen überhaupt Sinn ergibt). Lösch-Pfad
(ID-Ermittlung + `remove [find .id=...]`) live gegen das vom Nutzer
selbst angelegte VLAN nachvollzogen, funktioniert.
13. **Interface-Auswahl (`.interfacePick`) war einmalig beim Verbinden
eingefroren** — Nutzer legte ein VLAN an, danach fehlte es in der
Interface-Auswahl beim Anlegen eines DHCP-Servers. Ursache:
`.interfacePick` las aus `ConnectionService.interfaces`, nur einmal
bei `finishConnecting()` befüllt, nie danach aktualisiert — anders als
die neueren `.menuItemPick`-Felder (Pool/Profil/Skript), die bei
jedem Menü-Öffnen frisch laden. Fix: neues
`ExpertViewModel.liveInterfaceNames`, per `/interface print terse` bei
jedem `reloadItems()` aktualisiert; `availableInterfaces`-Parameter-
Durchreichung (`ExpertView``ExpertMenuDetailView`
`ExpertItemEditView`) komplett entfernt, da überflüssig. Danach vom
Nutzer selbst in der App bestätigt: neu angelegtes VLAN erscheint
sofort in der DHCP-Server-Interface-Auswahl.
**Lehren:** Citadel/NIOSSH-Fehler immer mit `String(describing:)` loggen,
nie `.localizedDescription`. Jede View, die ein ObservableObject aus einem
ViewModel liest, muss es selbst separat als `@ObservedObject` halten.
Zeilen-Splitting bei SSH-Ausgaben grundsätzlich mit `\.isNewline` statt
festem `"\n"`, da sich das je nach Gerät/Version unterscheiden kann.
Bei generischem RouterOS-Tooling (M10) nichts über CLI-Ausgabeformate
annehmen, was nicht live geprüft ist — `.id`-Verhalten und `terse`-Support
unterscheiden sich zwischen Menütypen und RouterOS-Versionen. **RouterOS'
SSH-Exit-Code ist kein verlässliches Erfolgssignal** — Ausgabetext ist die
einzige verlässliche Fehlerquelle über diesen Transport (Bug 10); REST
ist davon nicht betroffen (prüft HTTP-Statuscodes korrekt). RouterOS'
CLI will `yes`/`no` für Bools, nie `true`/`false` (Bug 11) — an einer
einzigen Stelle normalisieren (`pendingCommand`) statt an jedem
Schema-Eintrag hoffen, es richtig geschrieben zu haben. SwiftUI-
Interaktionsmuster nicht plattform-blind übernehmen — `.swipeActions`
sieht in der Doku harmlos aus, ist aber faktisch iOS/iPadOS-only (Bug 12).
Jeder Zustand, der sich während der Nutzung ändern kann (Interface-Liste
nach Anlegen eines VLANs), gehört live nachgeladen, nicht einmalig beim
Verbinden gecacht (Bug 13) — dasselbe Prinzip wie `crossReferenceOptions`
für Pool/Profil/Skript, nur nicht von Anfang an konsequent auf alle
Auswahllisten angewendet.
## `xcodebuild test` hängt — Gatekeeper, kein Code-Bug
@@ -162,6 +284,15 @@ wiederholen.
## Bekannte Einschränkungen (bewusst, nicht vergessen)
- **Backup-Wiederherstellung fehlt komplett** — Nutzer wies explizit
darauf hin (2026-09-14): `BackupService` kann Backups nur erzeugen
(`createBackup`) und auflisten (`listBackups`), es gibt **keine**
Funktion, ein gespeichertes `.rsc`-Backup zurück auf den Router zu
spielen. Einzige verwandte Funktion ist "Werkseinstellungen
wiederherstellen" (`FactoryResetService`, Gefahrenzone im
Sicherungen-Tab) — das ist RouterOS' eigener Werksreset, kein Zurück-
spielen einer konkreten Sicherung. Noch nicht umgesetzt, für später
vorgemerkt (siehe Nächste Schritte).
- **REST-Pfad ungetestet für Schreibvorgänge** — auf beiden bisherigen
Testgeräten war `www-ssl` (Port 443) aus, jeder Schreibtest lief über
SSH. Der REST-`apply()`-Pfad (`POST`/`PATCH`, `findItemID` für `.set`)
@@ -187,6 +318,20 @@ wiederholen.
- ~~SSH-Hostkey-TOFU ungetestet~~ — **bestätigt gegen echte Hardware**:
erste Verbindung zeigte den "Unbekannter SSH-Schlüssel"-Dialog, nach
Bestätigen + Trennen + erneutem Verbinden lief es ohne Rückfrage durch.
- **Experte-Tool (M10): mehrere kuratierte Bereiche noch nicht gegen
Hardware bestätigt** — WLAN (Legacy- und neuer wifiwave2-Treiber:
Testgerät hat keinen WLAN-Chip), Bonding und PPPoE-Client (beide riskant
für die eigene Erreichbarkeit/brauchen echte ISP-Zugangsdaten) nur gegen
RouterOS-Dokumentation kuratiert, nicht live geprüft. `/ip address`,
`/ip dhcp-client`, `/ip dns`, `/ip service` nur lesend geprüft (Feldnamen
bestätigt), keine Schreibtests — Risiko, sich selbst auszusperren
(SSH/REST-Dienst) bzw. die WAN-Verbindung zu kappen.
- **Experte-Tool: einige RouterOS-Bereiche bewusst nur generisch, nicht
kuratiert** — Hotspot, Logging (beide mehrteilig, kein flaches Schema),
CAPsMAN, L2TP-/OpenVPN-Server, OSPF/BGP-Routing. Grund: keine verifizierte
Quelle für die genauen Feldnamen, lieber generischer Schlüssel/Wert-
Zugriff als geratene Felder (siehe Bug 7 oben, warum das eine berechtigte
Sorge ist).
## M8: Mehrere LAN-Interfaces + Netzwerk-Isolation
@@ -210,6 +355,114 @@ Netzwerk gegen nicht-isoliertes, gegenseitige Isolation, keine Isolation).
**Noch nicht gegen echte Hardware getestet** — bräuchte zwei getrennte
Testnetze am selben Router, um die Drop-Regeln praktisch zu bestätigen.
## M9: Einfach/Experte-Modus im Einrichten-Wizard
Nutzerwunsch: zwei Modi statt einem Wizard-Durchlauf für alle. Neuer
erster Schritt `SetupStep.mode` (`ModeStepView`, Segmented Picker).
- **Einfach**: WAN → LAN (nur ein Netzwerk, kein Hinzufügen/Entfernen,
kein Isolations-Toggle) → WLAN → Firewall (Grundschutz fest an, kein
Toggle) → Review. VLAN-Schritt wird komplett übersprungen.
- **Experte**: unveränderter voller Flow (Multi-LAN, VLAN, Isolation,
Firewall-Toggle) — das, was vorher der einzige Wizard-Pfad war.
Beim Umschalten auf Einfach werden evtl. im Expertenmodus gesetzte Werte
zurückgesetzt (`SetupViewModel.applySimpleModeConstraints`). Navigation
(`goNext`/`goBack`) von linearem `SetupStep.rawValue+1` auf expliziten
`switch` umgebaut (nötig wegen bedingtem VLAN-Skip). Nur gegen Unit-Tests
+ Compile/Test-Build verifiziert, **nicht gegen echte Hardware** (der
Live-Test dieser Session lief komplett über direktes SSH, nicht über die
App-UI).
## M10: Experte-Tab — generischer RouterOS-Konfigurationszugriff
Nutzerwunsch: "Zugriff auf alle Funktionen, was RouterOS bietet ...
alles was du findest, der komplette Umfang". RouterOS hat 300+ Menüs
(siehe `/console inspect` / CLI-Referenz) — statt für jedes einzelne ein
Swift-Modell zu bauen (unrealistisch), zweistufige Architektur:
1. **Generischer Motor** (deckt technisch jeden Menüpfad ab):
`RouterOSMenuItem` + `RouterOSCliParser.parseGenericItems`/
`parseSingletonItem`, `RouterOSCommand.remove` (neu, neben add/set),
`ConnectionService.fetchMenuItems` + Transport-Implementierungen.
Im UI: freies "eigener Menüpfad"-Eingabefeld — jeder RouterOS-Pfad ist
erreichbar, auch ohne kuratiertes Schema, dann als rohes
Schlüssel/Wert-Formular.
2. **Kuratierte Schemas** (`RouterOSSchemaCatalog`) mit echten
Formularfeldern, Tooltips, Erklärtext und Warnhinweisen: die komplette
Firewall-Familie (Filter/NAT/Mangle/Raw/Adress-Listen — der explizit
genannte Schwerpunkt), dazu Interfaces (Bridge, VLAN, WireGuard+Peers,
PPPoE-Client, Bonding), IP (Adressen, Pools, DHCP-Server+Netzwerk,
DHCP-Client, DNS, Verwaltungsdienste, Routen), VPN (PPP-Secret/Profile),
WLAN (Legacy + neuer wifiwave2-Treiber + Sicherheitsprofile), Queues
(Simple/Tree), System (Identity/Clock/NTP/Scheduler/Skripte/Nutzer),
Werkzeuge (Netwatch/E-Mail). Bewusst nicht kuratiert (siehe oben):
Hotspot, Logging, CAPsMAN, L2TP-/OVPN-Server, OSPF/BGP.
**Live gegen den hEX-Testrouter verifiziert** (Add→Print→Set→Remove-
Zyklus je Familie, Router danach nachweislich unverändert — Config-Export
vorher/nachher byte-identisch bis auf Zeitstempel): Firewall
Filter/NAT/Mangle/Raw/Adress-Listen, VLAN, WireGuard+Peers, IP-Pool,
DHCP-Server+Netzwerk, statische Route, PPP-Secret/Profile, Queue Simple,
Scheduler+Skript, Netwatch, sowie der neue Singleton-Pfad
(`/system identity`). Dabei zwei Bugs gefunden und gefixt, siehe Bug 7/8
oben. **Nicht** gegen Hardware getestet: WLAN (kein Chip im Testgerät),
Bonding, PPPoE-Client (beide riskant/brauchen echte Zugangsdaten),
Schreibzugriff auf `/ip address`/`/ip dhcp-client`/`/ip dns`/
`/ip service` (nur lesend geprüft, Risiko Selbstaussperrung/WAN-Ausfall).
**Nachtrag (Nutzer-Feedback-Runde, gleiche Session):** Nutzer meldete zwei
reale Probleme beim gedanklichen/App-Testen des DHCP-Server-Formulars —
Bug 9 (Adress-Pool-Feld ohne Auswahl) und Bug 10 (fehlgeschlagenes Anlegen
ohne jede Fehlermeldung). Beide gefunden und gefixt (siehe oben). Bug 10s
Fix wurde erneut end-to-end live bestätigt: komplette Kette
VLAN → IP-Adresse → Pool → DHCP-Server → DHCP-Netzwerk auf einem freien
Interface (statt des zuvor kollidierenden `ether5`) angelegt, kein
Fehlertext (weil tatsächlich erfolgreich), anschließend vollständig
wieder entfernt.
**Zweiter Nachtrag — jetzt tatsächlich durch die App-UI getestet (Nutzer
selbst, gleiche Session):** Nutzer legte VLAN + DHCP-Server über den
Experte-Tab live an, fand dabei drei weitere reale Bugs (11: yes/no,
12: fehlende Lösch-Buttons, 13: eingefrorene Interface-Auswahl, siehe
oben) — alle gefunden, gefixt, nach jedem Fix Release neu gebaut und
deployt, vom Nutzer direkt in der App erneut getestet. Damit ist die
SwiftUI-Oberfläche des Experte-Tabs (VLAN anlegen, einem DHCP-Server
zuweisen, Adress-Pool wählen, löschen) **erstmals tatsächlich manuell
durchgeklickt und vom Nutzer als funktionierend bestätigt** — nicht mehr
nur die zugrundeliegende RouterOS-Syntax per SSH. Bestätigt: RouterOS
erlaubt nur einen DHCP-Server pro Interface — der Versuch, ihn direkt auf
`ether5` zu setzen, scheiterte zurecht (dort hängt bereits `dhcp5` aus
einer früheren Session), auf dem VLAN-Interface funktioniert es.
**Dritter Nachtrag — Sicherheits-Angleichung ans übrige App-Verhalten:**
Nutzer fragte, ob Experte-Tab-Änderungen sofort geschrieben werden oder
eine Bestätigung braucht — Antwort war "sofort, kein eigener
Bestätigungs-Button" (nur Löschen hatte einen Dialog). Das wich vom
Einrichten-Wizard ab (Übersicht + Pflicht-Backup vor "Jetzt anwenden").
Auf Wunsch angeglichen:
- **Bestätigungsdialog vor Anlegen/Ändern**: "Anlegen"/"Speichern" öffnet
jetzt zuerst einen `.confirmationDialog` mit dem exakten Befehl, erst
ein zweiter Klick führt ihn aus (`ExpertMenuDetailView`).
- **Auto-Backup einmal pro Verbindung**: `ConnectionService.
hasExpertToolBackedUpThisSession` (zurückgesetzt bei `disconnect()`),
`ExpertViewModel.ensureSessionBackup()` zieht vor dem allerersten
Schreibvorgang im Experte-Tab automatisch ein Backup (wie der Wizard),
aber nur einmal pro Sitzung, nicht vor jeder einzelnen Änderung (ein
Experte-Tab-Durchgang sind oft viele kleine Edits hintereinander).
**Vierter Nachtrag — Dauer-Editor:** Nutzer fragte, in welcher Einheit
die DHCP-Server-Lease-Zeit angegeben wird. Live geprüft: RouterOS-
Zeitwerte sind Zahl+Einheit (`s`/`m`/`h`/`d`/`w`, kombinierbar wie
`1d12h30m`), eine bare Zahl gilt als Sekunden (bestätigt: `90` → intern
`1m30s`). Nutzerwunsch danach: "ein sinnvoll gestaltetes Auswahlfenster
... ein Tooltip wäre hilfreich" statt nur Tooltip-Text. Neuer Feldtyp
`RouterOSFieldSchema.Kind.duration` + `DurationFieldEditor` (vier
Stepper: Tage/Std/Min/Sek, parst bestehende Werte beim Öffnen, schreibt
beim Ändern die RouterOS-Suffix-Form zurück) — angewendet auf DHCP-Server
"Lease-Zeit" und Adress-Listen "Ablaufzeit". Nutzer bestätigte danach:
"funktioniert".
## Stand der Milestones
- ✅ M1M4: Projektgerüst, Connect, Backup, WAN/LAN/DHCP, VLAN — gegen
@@ -232,6 +485,22 @@ Testnetze am selben Router, um die Drop-Regeln praktisch zu bestätigen.
- 🔶 M8: Mehrere LAN-Interfaces mit eigenem DHCP + Netzwerk-Isolation
(eigene Firewall-Regeln pro LAN/VLAN) — gebaut, nur Unit-Test-verifiziert,
noch nicht gegen echte Hardware getestet.
- 🔶 M9: Einfach/Experte-Modus im Einrichten-Wizard — gebaut, Compile/
Unit-Test-verifiziert. UI (Modusumschalter selbst) noch nicht manuell
durchgeklickt — nur M10s Experte-Tab wurde das (siehe M10).
- ✅ M10: Experte-Tab (generischer RouterOS-Zugriff + kuratierte Firewall-
und weitere Schemas) — Kern-Logik **live gegen Hardware verifiziert**,
UI (VLAN anlegen/löschen, DHCP-Server mit Adress-Pool zuweisen) **vom
Nutzer selbst in der laufenden App bestätigt funktionierend** — sieben
reale Bugs dabei gefunden und gefixt (Bug 713), danach zusätzlich
Bestätigungsdialog vor Anlegen/Ändern + einmaliges Auto-Backup pro
Sitzung (Angleichung an den Wizard) sowie ein Dauer-Editor (Tage/Std/
Min/Sek statt Freitext) für Lease-/Ablaufzeit-Felder ergänzt, alles vom
Nutzer bestätigt. Weiterhin ungetestet: WLAN (kein Chip im Testgerät),
Bonding, PPPoE-Client (beide riskant/brauchen echte Zugangsdaten),
Schreibzugriff auf `/ip address`/`/ip dhcp-client`/`/ip dns`/
`/ip service` (Risiko Selbstaussperrung/
WAN-Ausfall).
## Nächste Schritte
@@ -247,6 +516,33 @@ Testnetze am selben Router, um die Drop-Regeln praktisch zu bestätigen.
isoliert, anwenden, per `/ip firewall filter print` kontrollieren, dass
die Drop-Regeln greifen und der Rest (Internetzugriff, nicht-isolierte
Netzwerke) unangetastet bleibt.
5. M9 UI (Einfach/Experte-Modusumschalter im Einrichten-Tab selbst) noch
manuell durchklicken — M10s Experte-Tab wurde bereits vom Nutzer
bestätigt (siehe oben), der Moduswechsel im Wizard noch nicht.
6. M10: WLAN-Schemas (an Gerät mit WLAN-Chip), Bonding, PPPoE-Client
(mit echten oder Test-ISP-Zugangsdaten) noch gegen Hardware verifizieren.
7. `dhcp5`/`pool5` auf `ether5` sind Test-Überbleibsel aus einer früheren
Session — mit dem Nutzer klären, ob die noch gebraucht werden oder
aufgeräumt werden können (aktuell blockieren sie `ether5` für einen
neuen direkt-auf-dem-Port angelegten DHCP-Server).
8. **Nicht anrühren:** `test-vlan` (VLAN-ID 20, ether5), `testpool`
(192.168.90.10-250) und `testdhcp` (DHCP-Server auf `test-vlan`) sind
**echte, vom Nutzer selbst über die App angelegte Konfiguration** aus
dieser Session, keine Test-Artefakte zum Aufräumen.
9. Optional, kleinere Politur: den neuen Dauer-Editor
(`RouterOSFieldSchema.Kind.duration`) auch auf weitere Zeitwert-Felder
anwenden, die bisher nur Text mit Beispiel-Tooltip sind — WireGuard-
Peer "Keepalive", Scheduler "Intervall", Netwatch "Prüf-Intervall".
10. **Backup-Wiederherstellung bauen** — vom Nutzer explizit gewünscht,
noch nicht begonnen. Vermutlich: `BackupService` um eine
`restoreBackup(_:for:)`-Funktion erweitern, die den Inhalt eines
gewählten `.rsc`-Backups über SSH auf den Router einspielt (RouterOS
kann `.rsc`-Skripte per `/import file-name=...` ausführen, dazu muss
die Datei vorher aufs Gerät hochgeladen werden — Weg noch nicht
verifiziert, vor dem Bauen live prüfen statt zu raten), plus ein
"Wiederherstellen"-Button pro Eintrag im Sicherungen-Tab
(`BackupListView`) mit Bestätigungsdialog (ähnlich der bestehenden
Werksreset-Gefahrenzone).
Kein Gitea-Remote vorhanden — falls der Nutzer später eine Gitea-Instanz
aufsetzt (z.B. selbst gehostet auf der vorhandenen OMV-NAS, siehe