refactor: Fix macOS standalone application

This commit is contained in:
世界
2026-01-05 01:21:24 +08:00
parent ef1c924c64
commit 59317f3c79
66 changed files with 2799 additions and 585 deletions
+78 -29
View File
@@ -1,6 +1,9 @@
import Foundation
import Libbox
import NetworkExtension
import os
private let logger = Logger(category: "ExtensionProfile")
@MainActor
public class ExtensionProfile: ObservableObject {
@@ -24,17 +27,19 @@ public class ExtensionProfile: ObservableObject {
observer = NotificationCenter.default.addObserver(
forName: NSNotification.Name.NEVPNStatusDidChange,
object: manager.connection,
queue: .main
queue: nil
) { [weak self] notification in
guard let self else {
return
}
guard let connection = notification.object as? NEVPNConnection else {
return
}
self.connection = connection
self.status = connection.status
self.connectedDate = connection.connectedDate
Task { @MainActor in
guard let self else {
return
}
self.connection = connection
self.status = connection.status
self.connectedDate = connection.connectedDate
}
}
}
@@ -78,7 +83,7 @@ public class ExtensionProfile: ObservableObject {
}
public func start() async throws {
await fetchProfile()
try await fetchProfile()
manager.isEnabled = true
let alwaysOn = await SharedPreferences.alwaysOn.get()
let onDemandEnabled = await SharedPreferences.onDemandEnabled.get()
@@ -96,29 +101,73 @@ public class ExtensionProfile: ObservableObject {
}
#endif
try await manager.saveToPreferences()
#if os(macOS)
if Variant.useSystemExtension {
try manager.connection.startVPNTunnel(options: [
"username": NSString(string: NSUserName()),
"manualStart": NSNumber(value: true),
])
return
}
#endif
try manager.connection.startVPNTunnel(options: [
"manualStart": NSNumber(value: true),
])
let options = try await prepareStartOptions()
try manager.connection.startVPNTunnel(options: options)
}
public func fetchProfile() async {
do {
if let profile = try await ProfileManager.get(Int64(SharedPreferences.selectedProfileID.get())) {
if profile.type == .icloud {
_ = try profile.read()
public func reloadService() async throws {
let options = try await prepareStartOptions()
let data = try ExtensionStartOptions.encode(options)
guard let session = connection as? NETunnelProviderSession else {
throw NSError(domain: "ExtensionStartOptions", code: -1, userInfo: [
NSLocalizedDescriptionKey: "Tunnel session unavailable",
])
}
let response = try await withCheckedThrowingContinuation { continuation in
do {
try session.sendProviderMessage(data) { response in
continuation.resume(returning: response)
}
} catch {
continuation.resume(throwing: error)
}
}
if let response, !response.isEmpty {
let message = String(data: response, encoding: .utf8) ?? "Unknown error"
throw NSError(domain: "ExtensionStartOptions", code: -1, userInfo: [
NSLocalizedDescriptionKey: message,
])
}
}
private func prepareStartOptions() async throws -> [String: NSObject] {
var options: [String: NSObject] = [
"manualStart": NSNumber(value: true),
]
let profileID = await SharedPreferences.selectedProfileID.get()
guard let profile = try await ProfileManager.get(profileID) else {
throw NSError(domain: "ExtensionProfile", code: -1, userInfo: [
NSLocalizedDescriptionKey: "Missing selected profile",
])
}
let configContent = try profile.read()
options["configContent"] = NSString(string: configContent)
options["ignoreMemoryLimit"] = await NSNumber(value: SharedPreferences.ignoreMemoryLimit.get())
options["systemProxyEnabled"] = await NSNumber(value: SharedPreferences.systemProxyEnabled.get())
options["excludeDefaultRoute"] = await NSNumber(value: SharedPreferences.excludeDefaultRoute.get())
options["autoRouteUseSubRangesByDefault"] = await NSNumber(value: SharedPreferences.autoRouteUseSubRangesByDefault.get())
options["excludeAPNsRoute"] = await NSNumber(value: SharedPreferences.excludeAPNsRoute.get())
#if !os(tvOS)
options["includeAllNetworks"] = await NSNumber(value: SharedPreferences.includeAllNetworks.get())
#endif
#if os(tvOS)
options["commandServerPort"] = await NSNumber(value: SharedPreferences.commandServerPort.get())
options["commandServerSecret"] = await NSString(string: SharedPreferences.commandServerSecret.get())
#endif
return options
}
public func fetchProfile() async throws {
if let profile = try await ProfileManager.get(Int64(SharedPreferences.selectedProfileID.get())) {
if profile.type == .icloud {
_ = try profile.read()
}
} catch {
NSLog("fetchProfile error: \(error.localizedDescription)")
}
}
@@ -130,7 +179,7 @@ public class ExtensionProfile: ObservableObject {
do {
try LibboxNewStandaloneCommandClient()!.serviceClose()
} catch {
NSLog("serviceClose error: \(error.localizedDescription)")
logger.debug("serviceClose error: \(error.localizedDescription)")
}
manager.connection.stopVPNTunnel()
}
@@ -142,7 +191,7 @@ public class ExtensionProfile: ObservableObject {
try await Task.sleep(nanoseconds: NSEC_PER_SEC)
waitSeconds += 1
if waitSeconds >= 5 {
throw NSError(domain: "Restart service timeout", code: 0)
throw NSError(domain: "ExtensionProfile", code: 0, userInfo: [NSLocalizedDescriptionKey: String(localized: "Restart service timeout")])
}
}
try await start()