forked from kay/RouterOS
M17: "Bekannte Router" im Verbinden-Tab (SavedRouter/SavedRoutersStore), Standort-Freitextfeld, Scroll-Cap ab 4 Einträgen. Bugfix: Umbenennen- TextField steckte in einem sich selbst deaktivierenden Button. M18: Live-Traffic-Punkt an Interfaces (InterfaceTrafficMonitor, eigene SSH-Verbindung, monitor-traffic-Polling). Dabei zwei reale CLI-Parser-Bugs gefunden und gefixt: running/disabled-Flags werden als Buchstaben vor dem ersten Feld codiert, nicht als key=value; monitor-traffic liefert "50.7kbps" statt einer reinen Zahl. M19: Übersicht-Tab — animierte Flussrichtung auf allen Verbindungslinien (TimelineView+dashPhase), frei verschiebbare Knoten mit Live-folgenden Linien, Zurücksetzen-Button. Zusätzlich (noch nicht live getestet, nur Build+Unit-Tests grün): LAN-Port-Konflikt-Prüfung im Einrichten-Assistenten mit doppelter Sicherheitsbestätigung, "Fertig"-Button nach erfolgreichem Anwenden. 82 Tests grün. HANDOFF.md/README.md/Manual.md/CHATLOG.md aktualisiert. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YDmUd93KxsYGr2kLTotWnG
66 lines
3.2 KiB
Swift
66 lines
3.2 KiB
Swift
import Foundation
|
|
|
|
/// Pre-existing configuration on a physical port that the Setup wizard's LAN step would silently
|
|
/// override if the user picks that port for a new LAN/DHCP network — surfaced instead so the user
|
|
/// can either choose a different, actually-free port or explicitly (twice-confirmed) opt in to
|
|
/// clearing it. `DhcpServerCommandBuilder` already unconditionally detaches a port from any bridge
|
|
/// as a safety net (see its own doc comment) — this check runs *before* that, to make the same
|
|
/// fact visible up front instead of only implicitly, and additionally covers cases that builder
|
|
/// doesn't touch at all (an existing IP address, or the port already being used as a WAN dial-up).
|
|
struct PortConflict: Equatable {
|
|
enum Reason: Equatable {
|
|
case bridgeMember(bridgeName: String)
|
|
case hasAddresses([String])
|
|
case dhcpClient
|
|
case pppoeClient
|
|
|
|
var description: String {
|
|
switch self {
|
|
case .bridgeMember(let bridgeName):
|
|
return "Ist Mitglied der Bridge \"\(bridgeName)\""
|
|
case .hasAddresses(let addresses):
|
|
return "Trägt bereits die IP-Adresse\(addresses.count == 1 ? "" : "n") \(addresses.joined(separator: ", "))"
|
|
case .dhcpClient:
|
|
return "Ist als WAN-DHCP-Client konfiguriert (bezieht selbst eine Adresse aus dem Internet)"
|
|
case .pppoeClient:
|
|
return "Wird von einer PPPoE-Einwahl verwendet"
|
|
}
|
|
}
|
|
}
|
|
|
|
let interfaceName: String
|
|
let reasons: [Reason]
|
|
|
|
/// Commands that clear each found conflict so the port is actually free before
|
|
/// `DhcpServerCommandBuilder`'s own commands run. Bridge membership is deliberately excluded
|
|
/// here — `DhcpServerCommandBuilder` already removes it unconditionally regardless of whether
|
|
/// this check ran or was acknowledged, so repeating it would just be a harmless duplicate
|
|
/// `.remove` at best; excluding it keeps this list to exactly what wouldn't otherwise happen.
|
|
func resolutionCommands() -> [RouterOSCommand] {
|
|
reasons.flatMap { reason -> [RouterOSCommand] in
|
|
switch reason {
|
|
case .bridgeMember:
|
|
return []
|
|
case .hasAddresses:
|
|
return [RouterOSCommand.remove(
|
|
menuPath: "/ip address", restPath: "ip/address",
|
|
matchField: "interface", matchValue: interfaceName,
|
|
summary: "Bestehende IP-Adresse(n) auf \(interfaceName) entfernen"
|
|
)]
|
|
case .dhcpClient:
|
|
return [RouterOSCommand.remove(
|
|
menuPath: "/ip dhcp-client", restPath: "ip/dhcp-client",
|
|
matchField: "interface", matchValue: interfaceName,
|
|
summary: "WAN-DHCP-Client auf \(interfaceName) entfernen"
|
|
)]
|
|
case .pppoeClient:
|
|
return [RouterOSCommand.remove(
|
|
menuPath: "/interface pppoe-client", restPath: "interface/pppoe-client",
|
|
matchField: "interface", matchValue: interfaceName,
|
|
summary: "PPPoE-Einwahl auf \(interfaceName) entfernen"
|
|
)]
|
|
}
|
|
}
|
|
}
|
|
}
|