refactor(paths): App Support 目录命名权归 RuntimePaths

六个 GUI 配置文件位置(catalog/settings/listen-settings/custom-rules/
activation 与 silent-launch 遗留迁移源)自拼 applicationSupport 目录的
写法收敛为 RuntimePaths 的具名函数,各 store 的 defaultFileURL 改一行
委托。RuntimePaths 宪章从「跨进程运行时契约」扩为「App Support 目录
全部文件清单」——目录里有过哪些文件从此一处可答。

评审候选⑥ 6c:目录改名从动六个文件缩为动一处。
This commit is contained in:
Qiu Yuzhou
2026-10-04 13:02:31 +08:00
parent c1e229c455
commit 4b2ddae99e
7 changed files with 44 additions and 22 deletions
@@ -21,9 +21,7 @@ struct ActivationStateFileStore {
/// 默认位置:`~/Library/Application Support/ShadowsocksX-NG2/activation.json`。
static func defaultFileURL() -> URL {
let support = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[
0]
return support.appendingPathComponent("ShadowsocksX-NG2/activation.json")
RuntimePaths.activationStateFileURL()
}
/// 文件缺失/损坏/版本未知 → `nil`(安全侧:无活动目标)。
@@ -27,9 +27,7 @@ struct CatalogFileStore {
/// 默认位置:`~/Library/Application Support/ShadowsocksX-NG2/catalog.json`。
static func defaultFileURL() -> URL {
let support = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[
0]
return support.appendingPathComponent("ShadowsocksX-NG2/catalog.json")
RuntimePaths.catalogFileURL()
}
/// 文件缺失 → 全新空文档;存在但损坏/版本未知/结构不一致 → `.corrupt`。
@@ -10,9 +10,7 @@ struct CustomRuleStore {
}
static func defaultFileURL() -> URL {
let support = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[
0]
return support.appendingPathComponent("ShadowsocksX-NG2/custom-rules.json")
RuntimePaths.customRulesFileURL()
}
/// 读取规则集合;文件不存在视为无自定义规则。
@@ -39,9 +39,7 @@ struct ListenSettingsFileStore: ListenSettingsStoring {
let fileURL: URL
static func defaultFileURL() -> URL {
let support = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[
0]
return support.appendingPathComponent("ShadowsocksX-NG2/listen-settings.json")
RuntimePaths.listenSettingsFileURL()
}
init(fileURL: URL = ListenSettingsFileStore.defaultFileURL()) {
@@ -15,9 +15,7 @@ struct ProxySettingsFileStore: ProxySettingsStoring {
}
static func defaultFileURL() -> URL {
let support = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[
0]
return support.appendingPathComponent("ShadowsocksX-NG2/settings.json")
RuntimePaths.settingsFileURL()
}
func load() throws -> ProxySettings {
+38 -2
View File
@@ -1,7 +1,9 @@
import Foundation
/// 跨进程运行时路径契约(spec #21 D5,issue #27):GUI 写、wrapper 读的固定
/// 位置。目录 0700、文件 0600,由写入方与 wrapper 共同维护。
/// App Support 目录全部文件的路径清单:GUI 配置与跨进程运行时契约共用同一
/// 目录。运行时契约子集(spec #21 D5,issue #27)是 GUI 写、wrapper 读的固定
/// 位置;GUI 配置文件 wrapper 不经手。目录 0700、文件 0600,由写入方与
/// wrapper 共同维护。
enum RuntimePaths {
static func runtimeDirectory() -> URL {
FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]
@@ -42,6 +44,40 @@ enum RuntimePaths {
// 旧版端点签名/所有权文件(system-proxy-endpoint-signature.json、
// system-proxy-ownership.json)按 issue #71 就地遗留:不读取、不迁移、
// 不删除。
// MARK: - GUI 配置文件(GUI 私有读写,wrapper 不经手)
/// 服务器目录(CatalogFileStore)。
static func catalogFileURL() -> URL {
runtimeDirectory().appendingPathComponent("catalog.json")
}
/// 代理偏好(ProxySettingsFileStore,#33)。
static func settingsFileURL() -> URL {
runtimeDirectory().appendingPathComponent("settings.json")
}
/// 监听设置(ListenSettingsFileStore;settings.json 未建立时的 legacy 回退
/// 读取源)。
static func listenSettingsFileURL() -> URL {
runtimeDirectory().appendingPathComponent("listen-settings.json")
}
/// 自定义规则文档(CustomRuleStore,issue #66)。
static func customRulesFileURL() -> URL {
runtimeDirectory().appendingPathComponent("custom-rules.json")
}
/// 活动目标(ActivationStateFileStore,spec #21 D3/D5)。
static func activationStateFileURL() -> URL {
runtimeDirectory().appendingPathComponent("activation.json")
}
/// 静默启动偏好的 ADR 0017 初版文件形态,仅遗留迁移路径使用
/// (SilentLaunchStore.migrateLegacyFileIfPresent)。
static func silentLaunchLegacyFileURL() -> URL {
runtimeDirectory().appendingPathComponent("silent-launch.json")
}
}
/// ACL 活动链接的防逃逸校验(ADR-0011):解析 symlink 后必须仍是运行目录
@@ -9,11 +9,7 @@ import Foundation
struct SilentLaunchStore {
private static let key = "silentLaunch"
/// ADR 0017 初版(文件形态)的落盘位置,仅迁移路径使用。
static let legacyFileURL: URL = {
let support = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[
0]
return support.appendingPathComponent("ShadowsocksX-NG2/silent-launch.json")
}()
static let legacyFileURL = RuntimePaths.silentLaunchLegacyFileURL()
private static let currentVersion = 1