From 973e8dd3b0b1085ddb46e91a2f3679aea56f64f6 Mon Sep 17 00:00:00 2001 From: Qiu Yuzhou Date: Sat, 26 Sep 2026 22:44:24 +0800 Subject: [PATCH] fix(proxy): tolerate legacy PAC preferredMode when decoding settings An old settings.json with preferredMode "pac" made decodeIfPresent throw on ProxyModeKind, which marked the whole snapshot unreadable and refused to deploy. Decode the raw string and fall back to rule mode for unknown values so retained fields (ports, timeout, exceptions) survive. Closes the startup failure reported after #67. --- .../Domain/ProxySettingsFileStore.swift | 11 +++++++++-- .../Tests/ProxySettingsFileStoreTests.swift | 14 ++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/ShadowsocksX-NG2/Domain/ProxySettingsFileStore.swift b/ShadowsocksX-NG2/Domain/ProxySettingsFileStore.swift index f2c495a4..52697238 100644 --- a/ShadowsocksX-NG2/Domain/ProxySettingsFileStore.swift +++ b/ShadowsocksX-NG2/Domain/ProxySettingsFileStore.swift @@ -224,8 +224,15 @@ private struct ProxySettingsRecord: Codable, Equatable, Sendable { proxyExceptions = try container.decodeIfPresent(String.self, forKey: .proxyExceptions) ?? ProxySettings.defaultProxyExceptions - preferredMode = - try container.decodeIfPresent(ProxyModeKind.self, forKey: .preferredMode) ?? .rule + // 旧 NG2 偏好里的 "pac" 等已删除值容错回落到规则模式(issue #67: + // 未发布的 PAC 偏好不做迁移,但解码失败会把全部保留字段连坐丢掉)。 + if let rawMode = try container.decodeIfPresent(String.self, forKey: .preferredMode), + let mode = ProxyModeKind(rawValue: rawMode) + { + preferredMode = mode + } else { + preferredMode = .rule + } // 规则模式子选项(issue #63):出厂「未匹配时代理」。 ruleDefaultAction = try container.decodeIfPresent(RuleDefaultAction.self, forKey: .ruleDefaultAction) diff --git a/ShadowsocksX-NG2/Tests/ProxySettingsFileStoreTests.swift b/ShadowsocksX-NG2/Tests/ProxySettingsFileStoreTests.swift index 35855c85..59c83090 100644 --- a/ShadowsocksX-NG2/Tests/ProxySettingsFileStoreTests.swift +++ b/ShadowsocksX-NG2/Tests/ProxySettingsFileStoreTests.swift @@ -82,6 +82,20 @@ final class ProxySettingsFileStoreTests: XCTestCase { XCTAssertFalse(raw.contains("enabledModes")) } + func testLegacyPACPreferredModeFallsBackToRuleWithoutLosingOtherFields() throws { + try writeRaw( + """ + {"preferredMode":"pac","socksPort":2086,"httpPort":2087,"timeoutSeconds":120, + "pacUserRules":"@@example.com","gfwListURLConfigured":true,"pacPort":2089} + """) + + let loaded = try store.load() + XCTAssertEqual(loaded.preferredMode, .rule) + XCTAssertEqual(loaded.listen.socksPort, 2086) + XCTAssertEqual(loaded.listen.httpPort, 2087) + XCTAssertEqual(loaded.timeoutSeconds, 120) + } + func testInvalidSaveDoesNotTouchExistingSettings() throws { try store.save(ProxySettings()) var invalid = ProxySettings()