refactor(app): address code review for #72

- 回环候选清单上收到 ListenerMode.compatibleLoopbackAddresses(Domain
  单点),proxyLoopbackAddress 派生为首项;策略不再复刻 mode→回环映射
- refreshCommandAddresses 更名 refreshTerminalCommands(返回命令的语义)
- 监视创建失败注明为尽力而为增强(进入首页/复制前刷新兜底)
- HTTPExportCapability 注释与首页完整命令的地址选择划清边界
This commit is contained in:
Qiu Yuzhou
2026-10-01 10:16:00 +08:00
parent 5c1781c2b8
commit d647a4a859
7 changed files with 28 additions and 20 deletions
+2 -2
View File
@@ -217,7 +217,7 @@ struct TerminalProxyEnvironmentCard: View {
}
)
.onAppear {
control.refreshCommandAddresses()
control.refreshTerminalCommands()
}
.onDisappear {
feedbackTask?.cancel()
@@ -261,7 +261,7 @@ struct TerminalProxyEnvironmentCard: View {
private func copy(_ shell: TerminalCommandShell) {
// 复制前刷新(issue #72):失效选择回退后的命令即剪贴板内容,与按钮
// 提示保持同一选择。
let commands = control.refreshCommandAddresses()
let commands = control.refreshTerminalCommands()
do {
try clipboard.write(shell.command(from: commands))
feedbackTask?.cancel()
@@ -81,8 +81,8 @@ final class ControllerProxyRuntimeAdapter: ProxyRuntimeAdapting {
}
}
/// HTTP 导出能力的唯一派生点:监听设置 → 本机终端使用的可复制导出行。
/// 环境变量始终指向回环地址,避免将命令误用于其他设备。
/// HTTP 导出能力的唯一派生点:监听设置 → 可复制的 http/https 双导出行。
/// 此导出行始终指向回环地址(与首页完整命令的命令地址选择无关,issue #72)。
extension HTTPExportCapability {
init(listen: SslocalListenSettings) {
let urlHost = listen.proxyLoopbackURLHost
@@ -226,7 +226,7 @@ final class ProxyControlWorkflow: ObservableObject {
/// 事实变化通知自动刷新)。返回刷新后的两种 shell 命令供剪贴板副作用
/// 使用——失效选择已在本次观察中回退,提示投影与返回值一致。
@discardableResult
func refreshCommandAddresses() -> TerminalProxyEnvironmentCommands {
func refreshTerminalCommands() -> TerminalProxyEnvironmentCommands {
republish().terminalProxyEnvironmentCommands
}
@@ -28,6 +28,8 @@ final class SystemInterfaceFactsProvider: LocalInterfaceFactsReading {
}
private func startObserving() {
// 变化通知是尽力而为的增强:监视创建失败(罕见)只失去推送刷新,
// 进入首页与复制前刷新仍保证候选新鲜。
var context = SCDynamicStoreContext(
version: 0,
info: Unmanaged.passUnretained(observation).toOpaque(),
@@ -85,24 +85,19 @@ enum TerminalCommandAddressPolicy {
return loopbacks + interfaceCandidates(mode: mode, interfaces: interfaces)
}
/// 当前监听方式的默认回环地址(与 ListenerMode.proxyLoopbackAddress 一致)。
/// 当前监听方式的默认回环地址(Domain 回环清单首项)。
static func defaultLoopback(for mode: ListenerMode) -> TerminalCommandAddress {
TerminalCommandAddress(bsdName: "lo0", address: mode.proxyLoopbackAddress, displayName: "lo0")
TerminalCommandAddress(
bsdName: "lo0", address: mode.proxyLoopbackAddress, displayName: "lo0")
}
// MARK: - 派生细节
/// 回环候选(置顶)直接来自 Domain 的监听方式回环清单,展示名固定 lo0。
private static func loopbackCandidates(for mode: ListenerMode) -> [TerminalCommandAddress] {
let addresses: [String]
switch mode {
case .localhost, .allIPv4Interfaces:
addresses = ["127.0.0.1"]
case .allIPv4AndIPv6Interfaces:
addresses = ["127.0.0.1", "::1"]
case .allIPv6Interfaces:
addresses = ["::1"]
mode.compatibleLoopbackAddresses.map {
TerminalCommandAddress(bsdName: "lo0", address: $0, displayName: "lo0")
}
return addresses.map { TerminalCommandAddress(bsdName: "lo0", address: $0, displayName: "lo0") }
}
/// 待排序的接口分组:显示名 + BSD 名 + 该接口的地址候选。
@@ -25,8 +25,19 @@ enum ListenerMode: String, Codable, CaseIterable, Equatable, Sendable {
}
}
/// 此监听方式可接受的回环地址(默认地址在前):IPv4/仅本机只接受
/// 127.0.0.1,双栈通配绑定同时接受两个回环族,仅 IPv6 只接受 ::1。
var compatibleLoopbackAddresses: [String] {
switch self {
case .localhost, .allIPv4Interfaces: ["127.0.0.1"]
case .allIPv4AndIPv6Interfaces: ["127.0.0.1", "::1"]
case .allIPv6Interfaces: ["::1"]
}
}
/// 健康探测、系统代理与 HTTP 导出行使用的默认兼容回环地址。
var proxyLoopbackAddress: String {
self == .allIPv6Interfaces ? "::1" : "127.0.0.1"
compatibleLoopbackAddresses[0]
}
var bindingHint: String {
@@ -233,7 +233,7 @@ final class ProxyCommandAddressLifecycleTests: XCTestCase {
let wifi = workflow.snapshot.commandAddressPicker.candidates[1]
_ = workflow.selectCommandAddress(wifi)
_ = workflow.refreshCommandAddresses() // 离开再进入首页
_ = workflow.refreshTerminalCommands() // 离开再进入首页
XCTAssertEqual(
workflow.snapshot.commandAddressPicker.selected.identity, wifi.identity,
@@ -330,7 +330,7 @@ final class ProxyCommandAddressLifecycleTests: XCTestCase {
// 复制前地址消失且变化通知丢失:复制前刷新发现失效。
interfaceFacts.interfaces = []
let prepared = workflow.refreshCommandAddresses()
let prepared = workflow.refreshTerminalCommands()
XCTAssertFalse(
prepared.zshBash.contains("192.168.1.10"), "复制内容不包含已消失的地址")
@@ -348,7 +348,7 @@ final class ProxyCommandAddressLifecycleTests: XCTestCase {
runtime.runtimeFacts = ProxyRuntimeFacts(status: .off, isOn: false)
runtime.emitChange()
let prepared = workflow.refreshCommandAddresses()
let prepared = workflow.refreshTerminalCommands()
XCTAssertTrue(prepared.zshBash.contains("127.0.0.1"), "代理未运行时命令仍可准备")
XCTAssertEqual(runtime.resyncCount, 0, "刷新不触碰运行时生命周期")