Merge branch 'meta-proc' into meta-dev
This commit is contained in:
commit
384de6e3c9
|
@ -7,6 +7,8 @@
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
015F1E91288E42A50052B20A /* ClashMetaConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 015F1E90288E42A50052B20A /* ClashMetaConfig.swift */; };
|
||||||
|
015F1E92288E60D30052B20A /* MetaTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0162E74E2864B819007218A6 /* MetaTask.swift */; };
|
||||||
0162E74F2864B819007218A6 /* MetaTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0162E74E2864B819007218A6 /* MetaTask.swift */; };
|
0162E74F2864B819007218A6 /* MetaTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0162E74E2864B819007218A6 /* MetaTask.swift */; };
|
||||||
018F88F9286DD0CB004DD0F7 /* DualTitleMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 018F88F8286DD0CB004DD0F7 /* DualTitleMenuItem.swift */; };
|
018F88F9286DD0CB004DD0F7 /* DualTitleMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 018F88F8286DD0CB004DD0F7 /* DualTitleMenuItem.swift */; };
|
||||||
01943259287D19BC008CC51A /* ClashRuleProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01943258287D19BC008CC51A /* ClashRuleProvider.swift */; };
|
01943259287D19BC008CC51A /* ClashRuleProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01943258287D19BC008CC51A /* ClashRuleProvider.swift */; };
|
||||||
|
@ -131,6 +133,7 @@
|
||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
015F1E90288E42A50052B20A /* ClashMetaConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashMetaConfig.swift; sourceTree = "<group>"; };
|
||||||
0162E74D2864B818007218A6 /* com.metacubex.ClashX.ProxyConfigHelper-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "com.metacubex.ClashX.ProxyConfigHelper-Bridging-Header.h"; sourceTree = "<group>"; };
|
0162E74D2864B818007218A6 /* com.metacubex.ClashX.ProxyConfigHelper-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "com.metacubex.ClashX.ProxyConfigHelper-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
0162E74E2864B819007218A6 /* MetaTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetaTask.swift; sourceTree = "<group>"; };
|
0162E74E2864B819007218A6 /* MetaTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetaTask.swift; sourceTree = "<group>"; };
|
||||||
018F88F8286DD0CB004DD0F7 /* DualTitleMenuItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DualTitleMenuItem.swift; sourceTree = "<group>"; };
|
018F88F8286DD0CB004DD0F7 /* DualTitleMenuItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DualTitleMenuItem.swift; sourceTree = "<group>"; };
|
||||||
|
@ -325,6 +328,7 @@
|
||||||
491C250121BD561200AB5D44 /* Managers */,
|
491C250121BD561200AB5D44 /* Managers */,
|
||||||
491C250021BD55B900AB5D44 /* Utils */,
|
491C250021BD55B900AB5D44 /* Utils */,
|
||||||
492C4868210EE6B9004554A0 /* ApiRequest.swift */,
|
492C4868210EE6B9004554A0 /* ApiRequest.swift */,
|
||||||
|
015F1E90288E42A50052B20A /* ClashMetaConfig.swift */,
|
||||||
);
|
);
|
||||||
path = General;
|
path = General;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -709,6 +713,7 @@
|
||||||
49722FEF211F338B00650A41 /* FileEvent.swift in Sources */,
|
49722FEF211F338B00650A41 /* FileEvent.swift in Sources */,
|
||||||
49D176A72355FE680093DD7B /* NetworkChangeNotifier.swift in Sources */,
|
49D176A72355FE680093DD7B /* NetworkChangeNotifier.swift in Sources */,
|
||||||
4913C82321157D0200F6B87C /* Notification.swift in Sources */,
|
4913C82321157D0200F6B87C /* Notification.swift in Sources */,
|
||||||
|
015F1E91288E42A50052B20A /* ClashMetaConfig.swift in Sources */,
|
||||||
8ACD21BD27A04ED500BC4632 /* ProxyModeChangeCommand.swift in Sources */,
|
8ACD21BD27A04ED500BC4632 /* ProxyModeChangeCommand.swift in Sources */,
|
||||||
49228457270AADE20027A4B6 /* RemoteConfigUpdateIntervalSettingView.swift in Sources */,
|
49228457270AADE20027A4B6 /* RemoteConfigUpdateIntervalSettingView.swift in Sources */,
|
||||||
F9203A26236342820020D57D /* AppDelegate+..swift in Sources */,
|
F9203A26236342820020D57D /* AppDelegate+..swift in Sources */,
|
||||||
|
@ -729,6 +734,7 @@
|
||||||
F92D0B2A236C759100575E15 /* NSTextField+Vibrancy.swift in Sources */,
|
F92D0B2A236C759100575E15 /* NSTextField+Vibrancy.swift in Sources */,
|
||||||
F910AA24240134AF00116E95 /* ProxyGroupMenu.swift in Sources */,
|
F910AA24240134AF00116E95 /* ProxyGroupMenu.swift in Sources */,
|
||||||
4952C3BF2115C7CA004A4FA8 /* MenuItemFactory.swift in Sources */,
|
4952C3BF2115C7CA004A4FA8 /* MenuItemFactory.swift in Sources */,
|
||||||
|
015F1E92288E60D30052B20A /* MetaTask.swift in Sources */,
|
||||||
F977FAAC2366790500C17F1F /* AutoUpgardeManager.swift in Sources */,
|
F977FAAC2366790500C17F1F /* AutoUpgardeManager.swift in Sources */,
|
||||||
499A485822ED715200F6C675 /* RemoteConfigModel.swift in Sources */,
|
499A485822ED715200F6C675 /* RemoteConfigModel.swift in Sources */,
|
||||||
49D176AB23575BB20093DD7B /* ProxyGroupMenuItemView.swift in Sources */,
|
49D176AB23575BB20093DD7B /* ProxyGroupMenuItemView.swift in Sources */,
|
||||||
|
|
|
@ -12,6 +12,8 @@ import LetsMove
|
||||||
import RxCocoa
|
import RxCocoa
|
||||||
import RxSwift
|
import RxSwift
|
||||||
import SwiftyJSON
|
import SwiftyJSON
|
||||||
|
import Yams
|
||||||
|
import PromiseKit
|
||||||
|
|
||||||
private let statusItemLengthWithSpeed: CGFloat = 72
|
private let statusItemLengthWithSpeed: CGFloat = 72
|
||||||
|
|
||||||
|
@ -45,7 +47,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
@IBOutlet var copyExportCommandExternalMenuItem: NSMenuItem!
|
@IBOutlet var copyExportCommandExternalMenuItem: NSMenuItem!
|
||||||
@IBOutlet var experimentalMenu: NSMenu!
|
@IBOutlet var experimentalMenu: NSMenu!
|
||||||
@IBOutlet var externalControlSeparator: NSMenuItem!
|
@IBOutlet var externalControlSeparator: NSMenuItem!
|
||||||
|
|
||||||
@IBOutlet var hideUnselecableMenuItem: NSMenuItem!
|
@IBOutlet var hideUnselecableMenuItem: NSMenuItem!
|
||||||
@IBOutlet var proxyProvidersMenu: NSMenu!
|
@IBOutlet var proxyProvidersMenu: NSMenu!
|
||||||
@IBOutlet var ruleProvidersMenu: NSMenu!
|
@IBOutlet var ruleProvidersMenu: NSMenu!
|
||||||
|
@ -53,7 +55,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
@IBOutlet var ruleProvidersMenuItem: NSMenuItem!
|
@IBOutlet var ruleProvidersMenuItem: NSMenuItem!
|
||||||
@IBOutlet var snifferMenuItem: NSMenuItem!
|
@IBOutlet var snifferMenuItem: NSMenuItem!
|
||||||
@IBOutlet var flushFakeipCacheMenuItem: NSMenuItem!
|
@IBOutlet var flushFakeipCacheMenuItem: NSMenuItem!
|
||||||
|
|
||||||
@IBOutlet var useAlphaMetaMenuItem: NSMenuItem!
|
@IBOutlet var useAlphaMetaMenuItem: NSMenuItem!
|
||||||
@IBOutlet var alphaMetaVersionMenuItem: NSMenuItem!
|
@IBOutlet var alphaMetaVersionMenuItem: NSMenuItem!
|
||||||
@IBOutlet var updateAlphaMetaMenuItem: NSMenuItem!
|
@IBOutlet var updateAlphaMetaMenuItem: NSMenuItem!
|
||||||
|
@ -65,7 +67,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
var runAfterConfigReload: (() -> Void)?
|
var runAfterConfigReload: (() -> Void)?
|
||||||
|
|
||||||
var dashboardWindowController: ClashWebViewWindowController?
|
var dashboardWindowController: ClashWebViewWindowController?
|
||||||
|
|
||||||
func applicationWillFinishLaunching(_ notification: Notification) {
|
func applicationWillFinishLaunching(_ notification: Notification) {
|
||||||
signal(SIGPIPE, SIG_IGN)
|
signal(SIGPIPE, SIG_IGN)
|
||||||
// crash recorder
|
// crash recorder
|
||||||
|
@ -114,7 +116,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
runAfterConfigReload = { [weak self] in
|
runAfterConfigReload = { [weak self] in
|
||||||
self?.selectAllowLanWithMenory()
|
self?.selectAllowLanWithMenory()
|
||||||
}
|
}
|
||||||
|
|
||||||
updateLoggingLevel()
|
updateLoggingLevel()
|
||||||
|
|
||||||
// start watch config file change
|
// start watch config file change
|
||||||
|
@ -128,7 +130,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
|
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
|
||||||
let group = DispatchGroup()
|
let group = DispatchGroup()
|
||||||
var shouldWait = false
|
var shouldWait = false
|
||||||
|
|
||||||
PrivilegedHelperManager.shared.helper()?.stopMeta()
|
PrivilegedHelperManager.shared.helper()?.stopMeta()
|
||||||
|
|
||||||
if ConfigManager.shared.proxyPortAutoSet && !ConfigManager.shared.isProxySetByOtherVariable.value || NetworkChangeNotifier.isCurrentSystemSetToClash(looser: true) ||
|
if ConfigManager.shared.proxyPortAutoSet && !ConfigManager.shared.isProxySetByOtherVariable.value || NetworkChangeNotifier.isCurrentSystemSetToClash(looser: true) ||
|
||||||
|
@ -211,7 +213,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
}).disposed(by: disposeBag)
|
}).disposed(by: disposeBag)
|
||||||
|
|
||||||
remoteConfigAutoupdateMenuItem.state = RemoteConfigManager.autoUpdateEnable ? .on : .off
|
remoteConfigAutoupdateMenuItem.state = RemoteConfigManager.autoUpdateEnable ? .on : .off
|
||||||
|
|
||||||
hideUnselecableMenuItem.state = .init(rawValue: MenuItemFactory.hideUnselectable)
|
hideUnselecableMenuItem.state = .init(rawValue: MenuItemFactory.hideUnselectable)
|
||||||
useAlphaMetaMenuItem.state = MenuItemFactory.useAlphaCore ? .on : .off
|
useAlphaMetaMenuItem.state = MenuItemFactory.useAlphaCore ? .on : .off
|
||||||
}
|
}
|
||||||
|
@ -289,7 +291,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
self.proxySettingMenuItem.target = self
|
self.proxySettingMenuItem.target = self
|
||||||
}.disposed(by: disposeBag)
|
}.disposed(by: disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
// start proxy
|
// start proxy
|
||||||
if !PrivilegedHelperManager.shared.isHelperCheckFinished.value {
|
if !PrivilegedHelperManager.shared.isHelperCheckFinished.value {
|
||||||
PrivilegedHelperManager.shared.isHelperCheckFinished
|
PrivilegedHelperManager.shared.isHelperCheckFinished
|
||||||
|
@ -298,14 +300,14 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
.observe(on: MainScheduler.instance)
|
.observe(on: MainScheduler.instance)
|
||||||
.bind(onNext: { _ in
|
.bind(onNext: { _ in
|
||||||
self.initMetaCore()
|
self.initMetaCore()
|
||||||
self.updateConfig(showNotification: false)
|
self.startProxy()
|
||||||
}).disposed(by: disposeBag)
|
}).disposed(by: disposeBag)
|
||||||
} else {
|
} else {
|
||||||
initMetaCore()
|
initMetaCore()
|
||||||
updateConfig(showNotification: false)
|
startProxy()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupSystemData() {
|
func setupSystemData() {
|
||||||
if !PrivilegedHelperManager.shared.isHelperCheckFinished.value &&
|
if !PrivilegedHelperManager.shared.isHelperCheckFinished.value &&
|
||||||
ConfigManager.shared.proxyPortAutoSet {
|
ConfigManager.shared.proxyPortAutoSet {
|
||||||
|
@ -419,10 +421,10 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
}
|
}
|
||||||
NotificationCenter.default.post(name: .reloadDashboard, object: nil)
|
NotificationCenter.default.post(name: .reloadDashboard, object: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func initMetaCore() {
|
func initMetaCore() {
|
||||||
Logger.log("initClashCore")
|
Logger.log("initClashCore")
|
||||||
|
|
||||||
let corePath: String = {
|
let corePath: String = {
|
||||||
if let path = Paths.alphaCorePath()?.path,
|
if let path = Paths.alphaCorePath()?.path,
|
||||||
let v = testMetaCore(path) {
|
let v = testMetaCore(path) {
|
||||||
|
@ -433,7 +435,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
} else {
|
} else {
|
||||||
updateAlphaVersion(nil)
|
updateAlphaVersion(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let path = Paths.defaultCorePath(),
|
if let path = Paths.defaultCorePath(),
|
||||||
testMetaCore(path) != nil {
|
testMetaCore(path) != nil {
|
||||||
return path
|
return path
|
||||||
|
@ -443,16 +445,17 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
RemoteConfigManager.shared.verifyConfigTask.setLaunchPath(corePath)
|
||||||
PrivilegedHelperManager.shared.helper()?.initMetaCore(withPath: corePath)
|
PrivilegedHelperManager.shared.helper()?.initMetaCore(withPath: corePath)
|
||||||
Logger.log("initClashCore finish")
|
Logger.log("initClashCore finish")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testMetaCore(_ path: String) -> (version: String, date: Date?)? {
|
func testMetaCore(_ path: String) -> (version: String, date: Date?)? {
|
||||||
guard FileManager.default.fileExists(atPath: path),
|
guard FileManager.default.fileExists(atPath: path),
|
||||||
chmodX(path) else {
|
chmodX(path) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let proc = Process()
|
let proc = Process()
|
||||||
proc.executableURL = .init(fileURLWithPath: path)
|
proc.executableURL = .init(fileURLWithPath: path)
|
||||||
proc.arguments = ["-v"]
|
proc.arguments = ["-v"]
|
||||||
|
@ -466,23 +469,23 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
}
|
}
|
||||||
proc.waitUntilExit()
|
proc.waitUntilExit()
|
||||||
let data = pipe.fileHandleForReading.readDataToEndOfFile()
|
let data = pipe.fileHandleForReading.readDataToEndOfFile()
|
||||||
|
|
||||||
guard proc.terminationStatus == 0,
|
guard proc.terminationStatus == 0,
|
||||||
let out = String(data: data, encoding: .utf8) else {
|
let out = String(data: data, encoding: .utf8) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let outs = out.replacingOccurrences(of: "\n", with: "").split(separator: " ").map(String.init)
|
let outs = out.replacingOccurrences(of: "\n", with: "").split(separator: " ").map(String.init)
|
||||||
|
|
||||||
guard outs.count == 13,
|
guard outs.count == 13,
|
||||||
outs[0] == "Clash",
|
outs[0] == "Clash",
|
||||||
outs[1] == "Meta",
|
outs[1] == "Meta",
|
||||||
outs[3] == "darwin" else {
|
outs[3] == "darwin" else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let version = outs[2]
|
let version = outs[2]
|
||||||
|
|
||||||
let dateString = [outs[7], outs[8], outs[9], outs[10], outs[12]].joined(separator: "-")
|
let dateString = [outs[7], outs[8], outs[9], outs[10], outs[12]].joined(separator: "-")
|
||||||
let f = DateFormatter()
|
let f = DateFormatter()
|
||||||
f.dateFormat = "E-MMM-d-HH:mm:ss-yyyy"
|
f.dateFormat = "E-MMM-d-HH:mm:ss-yyyy"
|
||||||
|
@ -491,7 +494,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
|
|
||||||
return (version: version, date: date)
|
return (version: version, date: date)
|
||||||
}
|
}
|
||||||
|
|
||||||
func chmodX(_ path: String) -> Bool {
|
func chmodX(_ path: String) -> Bool {
|
||||||
let proc = Process()
|
let proc = Process()
|
||||||
proc.executableURL = .init(fileURLWithPath: "/bin/chmod")
|
proc.executableURL = .init(fileURLWithPath: "/bin/chmod")
|
||||||
|
@ -514,30 +517,16 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
let secret: String
|
let secret: String
|
||||||
let log: String?
|
let log: String?
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup ui config first
|
|
||||||
if let htmlPath = Bundle.main.path(forResource: "index", ofType: "html", inDirectory: "dashboard") {
|
|
||||||
let uiPath = URL(fileURLWithPath: htmlPath).deletingLastPathComponent().path
|
|
||||||
PrivilegedHelperManager.shared.helper()?.metaSetUIPath(uiPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.log("Trying start proxy")
|
let config = ClashMetaConfig.generateInitConfig()
|
||||||
var string = ""
|
|
||||||
let queue = DispatchGroup()
|
Logger.log("Trying start meta core")
|
||||||
queue.enter()
|
startMeta(config).map { string -> String in
|
||||||
|
guard let jsonData = string.data(using: .utf8),
|
||||||
PrivilegedHelperManager.shared.helper {
|
let res = try? JSONDecoder().decode(StartProxyResp.self, from: jsonData) else {
|
||||||
string = "Can't connect to helper."
|
return string == "" ? "unknown error" : string
|
||||||
queue.leave()
|
}
|
||||||
}?.startMeta(withConfPath: kConfigFolderPath,
|
|
||||||
confFilePath: "") {
|
|
||||||
string = $0 ?? ""
|
|
||||||
queue.leave()
|
|
||||||
}
|
|
||||||
queue.wait()
|
|
||||||
let jsonData = string.data(using: .utf8) ?? Data()
|
|
||||||
if let res = try? JSONDecoder().decode(StartProxyResp.self, from: jsonData) {
|
|
||||||
|
|
||||||
if let log = res.log {
|
if let log = res.log {
|
||||||
Logger.log("""
|
Logger.log("""
|
||||||
\n######## Clash Meta Start Log #########
|
\n######## Clash Meta Start Log #########
|
||||||
|
@ -545,22 +534,64 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
######## END #########
|
######## END #########
|
||||||
""", level: .info)
|
""", level: .info)
|
||||||
}
|
}
|
||||||
|
|
||||||
let port = res.externalController.components(separatedBy: ":").last ?? "9090"
|
let port = res.externalController.components(separatedBy: ":").last ?? "9090"
|
||||||
ConfigManager.shared.apiPort = port
|
ConfigManager.shared.apiPort = port
|
||||||
ConfigManager.shared.apiSecret = res.secret
|
ConfigManager.shared.apiSecret = res.secret
|
||||||
ConfigManager.shared.isRunning = true
|
ConfigManager.shared.isRunning = true
|
||||||
proxyModeMenuItem.isEnabled = true
|
self.proxyModeMenuItem.isEnabled = true
|
||||||
dashboardMenuItem.isEnabled = true
|
self.dashboardMenuItem.isEnabled = true
|
||||||
|
return ""
|
||||||
setupSystemData()
|
}.then {
|
||||||
} else {
|
self.pushInitConfig($0)
|
||||||
ConfigManager.shared.isRunning = false
|
}.done { s in
|
||||||
proxyModeMenuItem.isEnabled = false
|
if s != "" {
|
||||||
Logger.log(string, level: .error)
|
ConfigManager.shared.isRunning = false
|
||||||
NSUserNotificationCenter.default.postConfigErrorNotice(msg: string)
|
self.proxyModeMenuItem.isEnabled = false
|
||||||
|
Logger.log(s, level: .error)
|
||||||
|
NSUserNotificationCenter.default.postConfigErrorNotice(msg: s)
|
||||||
|
} else {
|
||||||
|
Logger.log("Init config file success.")
|
||||||
|
}
|
||||||
|
}.catch { _ in }
|
||||||
|
}
|
||||||
|
|
||||||
|
func startMeta(_ config: ClashMetaConfig.Config) -> Promise<String> {
|
||||||
|
.init { resolver in
|
||||||
|
PrivilegedHelperManager.shared.helper {
|
||||||
|
resolver.fulfill("Can't connect to helper.")
|
||||||
|
}?.startMeta(withConfPath: kConfigFolderPath,
|
||||||
|
confFilePath: config.path) {
|
||||||
|
resolver.fulfill($0 ?? "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func pushInitConfig(_ s: String) -> Promise<String> {
|
||||||
|
.init { resolver in
|
||||||
|
guard s == "" else {
|
||||||
|
resolver.fulfill(s)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ClashProxy.cleanCache()
|
||||||
|
let configName = ConfigManager.selectConfigName
|
||||||
|
Logger.log("Push init config file: \(configName)")
|
||||||
|
ApiRequest.requestConfigUpdate(configName: configName) { err in
|
||||||
|
if let error = err {
|
||||||
|
resolver.fulfill(error)
|
||||||
|
} else {
|
||||||
|
self.syncConfig()
|
||||||
|
self.resetStreamApi()
|
||||||
|
self.runAfterConfigReload?()
|
||||||
|
self.runAfterConfigReload = nil
|
||||||
|
self.selectProxyGroupWithMemory()
|
||||||
|
self.selectOutBoundModeWithMenory()
|
||||||
|
MenuItemFactory.recreateProxyMenuItems()
|
||||||
|
NotificationCenter.default.post(name: .reloadDashboard, object: nil)
|
||||||
|
resolver.fulfill("")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Logger.log("Start proxy done")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func syncConfig(completeHandler: (() -> Void)? = nil) {
|
func syncConfig(completeHandler: (() -> Void)? = nil) {
|
||||||
|
@ -868,7 +899,6 @@ extension AppDelegate {
|
||||||
@IBAction func actionUpdateProxyGroupMenu(_ sender: Any) {
|
@IBAction func actionUpdateProxyGroupMenu(_ sender: Any) {
|
||||||
ConfigManager.shared.disableShowCurrentProxyInMenu = !ConfigManager.shared.disableShowCurrentProxyInMenu
|
ConfigManager.shared.disableShowCurrentProxyInMenu = !ConfigManager.shared.disableShowCurrentProxyInMenu
|
||||||
updateExperimentalFeatureStatus()
|
updateExperimentalFeatureStatus()
|
||||||
print("211")
|
|
||||||
MenuItemFactory.recreateProxyMenuItems()
|
MenuItemFactory.recreateProxyMenuItems()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -912,7 +942,7 @@ extension AppDelegate {
|
||||||
sender.state = newState
|
sender.state = newState
|
||||||
MenuItemFactory.hideUnselectable = newState.rawValue
|
MenuItemFactory.hideUnselectable = newState.rawValue
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func checkForUpdate(_ sender: NSMenuItem) {
|
@IBAction func checkForUpdate(_ sender: NSMenuItem) {
|
||||||
let unc = NSUserNotificationCenter.default
|
let unc = NSUserNotificationCenter.default
|
||||||
AF.request("https://api.github.com/repos/MetaCubeX/ClashX.Meta/releases/latest").responseString {
|
AF.request("https://api.github.com/repos/MetaCubeX/ClashX.Meta/releases/latest").responseString {
|
||||||
|
@ -922,7 +952,7 @@ extension AppDelegate {
|
||||||
unc.postUpdateNotice(msg: "Some thing failed.")
|
unc.postUpdateNotice(msg: "Some thing failed.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if tagName != AppVersionUtil.currentVersion {
|
if tagName != AppVersionUtil.currentVersion {
|
||||||
let alert = NSAlert()
|
let alert = NSAlert()
|
||||||
alert.messageText = "Open github release page to download \(tagName)"
|
alert.messageText = "Open github release page to download \(tagName)"
|
||||||
|
@ -936,38 +966,38 @@ extension AppDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func updateGEO(_ sender: NSMenuItem) {
|
@IBAction func updateGEO(_ sender: NSMenuItem) {
|
||||||
ApiRequest.updateGEO() { _ in
|
ApiRequest.updateGEO { _ in
|
||||||
NSUserNotificationCenter.default.post(title: "Updating GEO Databases...", info: "Good luck to you 🙃")
|
NSUserNotificationCenter.default.post(title: "Updating GEO Databases...", info: "Good luck to you 🙃")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func flushFakeipCache(_ sender: NSMenuItem) {
|
@IBAction func flushFakeipCache(_ sender: NSMenuItem) {
|
||||||
ApiRequest.flushFakeipCache() {
|
ApiRequest.flushFakeipCache {
|
||||||
NSUserNotificationCenter.default.post(title: "Flush fake-ip cache", info: $0 ? "Success" : "Failed")
|
NSUserNotificationCenter.default.post(title: "Flush fake-ip cache", info: $0 ? "Success" : "Failed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func updateSniffing(_ sender: NSMenuItem) {
|
@IBAction func updateSniffing(_ sender: NSMenuItem) {
|
||||||
let enable = sender.state != .on
|
let enable = sender.state != .on
|
||||||
ApiRequest.updateSniffing(enable: enable) {
|
ApiRequest.updateSniffing(enable: enable) {
|
||||||
sender.state = enable ? .on : .off
|
sender.state = enable ? .on : .off
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func useAlphaMeta(_ sender: NSMenuItem) {
|
@IBAction func useAlphaMeta(_ sender: NSMenuItem) {
|
||||||
let use = sender.state != .on
|
let use = sender.state != .on
|
||||||
MenuItemFactory.useAlphaCore = use
|
MenuItemFactory.useAlphaCore = use
|
||||||
sender.state = use ? .on : .off
|
sender.state = use ? .on : .off
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func showAlphaInFinder(_ sender: NSMenuItem) {
|
@IBAction func showAlphaInFinder(_ sender: NSMenuItem) {
|
||||||
guard let u = Paths.alphaCorePath(),
|
guard let u = Paths.alphaCorePath(),
|
||||||
FileManager.default.fileExists(atPath: u.path) else { return }
|
FileManager.default.fileExists(atPath: u.path) else { return }
|
||||||
NSWorkspace.shared.activateFileViewerSelecting([u])
|
NSWorkspace.shared.activateFileViewerSelecting([u])
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func updateAlphaMeta(_ sender: NSMenuItem) {
|
@IBAction func updateAlphaMeta(_ sender: NSMenuItem) {
|
||||||
guard let helperURL = Paths.alphaCorePath() else {
|
guard let helperURL = Paths.alphaCorePath() else {
|
||||||
return
|
return
|
||||||
|
@ -989,7 +1019,7 @@ extension AppDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetMachineHardwareName() -> String? {
|
func GetMachineHardwareName() -> String? {
|
||||||
var sysInfo = utsname()
|
var sysInfo = utsname()
|
||||||
let retVal = uname(&sysInfo)
|
let retVal = uname(&sysInfo)
|
||||||
|
@ -998,7 +1028,7 @@ extension AppDelegate {
|
||||||
|
|
||||||
return String(cString: &sysInfo.machine.0, encoding: .utf8)
|
return String(cString: &sysInfo.machine.0, encoding: .utf8)
|
||||||
}
|
}
|
||||||
|
|
||||||
let assetName: String? = {
|
let assetName: String? = {
|
||||||
switch GetMachineHardwareName() {
|
switch GetMachineHardwareName() {
|
||||||
case "x86_64":
|
case "x86_64":
|
||||||
|
@ -1010,12 +1040,12 @@ extension AppDelegate {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
let fm = FileManager.default
|
let fm = FileManager.default
|
||||||
|
|
||||||
func dlResult(_ info: String) {
|
func dlResult(_ info: String) {
|
||||||
sender.isEnabled = true
|
sender.isEnabled = true
|
||||||
NSUserNotificationCenter.default.post(title: "Clash Meta Core", info: info)
|
NSUserNotificationCenter.default.post(title: "Clash Meta Core", info: info)
|
||||||
}
|
}
|
||||||
|
|
||||||
AF.request("https://api.github.com/repos/MetaCubeX/Clash.Meta/releases/tags/Prerelease-Alpha").responseDecodable(of: ReleasesResp.self) {
|
AF.request("https://api.github.com/repos/MetaCubeX/Clash.Meta/releases/tags/Prerelease-Alpha").responseDecodable(of: ReleasesResp.self) {
|
||||||
guard let assets = $0.value?.assets,
|
guard let assets = $0.value?.assets,
|
||||||
let assetName = assetName,
|
let assetName = assetName,
|
||||||
|
@ -1027,16 +1057,16 @@ extension AppDelegate {
|
||||||
dlResult("Decode alpha release info failed")
|
dlResult("Decode alpha release info failed")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if let v = self.testMetaCore(helperURL.path),
|
if let v = self.testMetaCore(helperURL.path),
|
||||||
asset.name.contains(v.version) {
|
asset.name.contains(v.version) {
|
||||||
dlResult("Not found update")
|
dlResult("Not found update")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.updateAlphaVersion(nil)
|
self.updateAlphaVersion(nil)
|
||||||
try? fm.removeItem(at: helperURL)
|
try? fm.removeItem(at: helperURL)
|
||||||
|
|
||||||
AF.download(asset.downloadUrl).response {
|
AF.download(asset.downloadUrl).response {
|
||||||
guard let gzPath = $0.fileURL?.path,
|
guard let gzPath = $0.fileURL?.path,
|
||||||
let contentData = fm.contents(atPath: gzPath)
|
let contentData = fm.contents(atPath: gzPath)
|
||||||
|
@ -1059,7 +1089,7 @@ extension AppDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateAlphaVersion(_ version: String?) {
|
func updateAlphaVersion(_ version: String?) {
|
||||||
let enable = version != nil
|
let enable = version != nil
|
||||||
useAlphaMetaMenuItem.isEnabled = enable
|
useAlphaMetaMenuItem.isEnabled = enable
|
||||||
|
|
|
@ -15,7 +15,7 @@ extension DateFormatter {
|
||||||
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SZ"
|
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SZ"
|
||||||
return dateFormatter
|
return dateFormatter
|
||||||
}
|
}
|
||||||
|
|
||||||
static var provider: DateFormatter {
|
static var provider: DateFormatter {
|
||||||
let f = DateFormatter()
|
let f = DateFormatter()
|
||||||
f.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSZZ"
|
f.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSZZ"
|
||||||
|
|
|
@ -136,7 +136,7 @@ extension NSUserNotificationCenter {
|
||||||
post(title: NSLocalizedString("System Proxy Changed", comment: ""),
|
post(title: NSLocalizedString("System Proxy Changed", comment: ""),
|
||||||
info: NSLocalizedString("Proxy settings are changed by another process. ClashX is no longer the default system proxy.", comment: ""), notiOnly: true)
|
info: NSLocalizedString("Proxy settings are changed by another process. ClashX is no longer the default system proxy.", comment: ""), notiOnly: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func postUpdateNotice(msg: String) {
|
func postUpdateNotice(msg: String) {
|
||||||
postNotificationAlert(title: "Update ClashX Meta", info: msg)
|
postNotificationAlert(title: "Update ClashX Meta", info: msg)
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,14 +26,14 @@ class ApiRequest {
|
||||||
private lazy var logQueue = DispatchQueue(label: "com.ClashX.core.log")
|
private lazy var logQueue = DispatchQueue(label: "com.ClashX.core.log")
|
||||||
|
|
||||||
static let clashRequestQueue = DispatchQueue(label: "com.clashx.clashRequestQueue")
|
static let clashRequestQueue = DispatchQueue(label: "com.clashx.clashRequestQueue")
|
||||||
|
|
||||||
@objc enum ProviderType: Int {
|
@objc enum ProviderType: Int {
|
||||||
case rule, proxy
|
case rule, proxy
|
||||||
|
|
||||||
func apiString() -> String {
|
func apiString() -> String {
|
||||||
self == .proxy ? "proxies" : "rules"
|
self == .proxy ? "proxies" : "rules"
|
||||||
}
|
}
|
||||||
|
|
||||||
func logString() -> String {
|
func logString() -> String {
|
||||||
self == .proxy ? "Proxy" : "Rule"
|
self == .proxy ? "Proxy" : "Rule"
|
||||||
}
|
}
|
||||||
|
@ -254,9 +254,9 @@ class ApiRequest {
|
||||||
static func getRules(completeHandler: @escaping ([ClashRule]) -> Void) {
|
static func getRules(completeHandler: @escaping ([ClashRule]) -> Void) {
|
||||||
req("/rules").responseData { res in
|
req("/rules").responseData { res in
|
||||||
guard let data = try? res.result.get() else { return }
|
guard let data = try? res.result.get() else { return }
|
||||||
|
|
||||||
ClashRuleProviderResp.init()
|
ClashRuleProviderResp.init()
|
||||||
|
|
||||||
let rule = ClashRuleResponse.fromData(data)
|
let rule = ClashRuleResponse.fromData(data)
|
||||||
completeHandler(rule.rules ?? [])
|
completeHandler(rule.rules ?? [])
|
||||||
}
|
}
|
||||||
|
@ -304,10 +304,10 @@ extension ApiRequest {
|
||||||
extension ApiRequest {
|
extension ApiRequest {
|
||||||
static func updateAllProviders(for type: ProviderType, completeHandler: ((Int) -> Void)? = nil) {
|
static func updateAllProviders(for type: ProviderType, completeHandler: ((Int) -> Void)? = nil) {
|
||||||
var failuresCount = 0
|
var failuresCount = 0
|
||||||
|
|
||||||
let group = DispatchGroup()
|
let group = DispatchGroup()
|
||||||
group.enter()
|
group.enter()
|
||||||
|
|
||||||
if type == .proxy {
|
if type == .proxy {
|
||||||
requestProxyProviderList { resp in
|
requestProxyProviderList { resp in
|
||||||
resp.allProviders.filter {
|
resp.allProviders.filter {
|
||||||
|
@ -337,15 +337,15 @@ extension ApiRequest {
|
||||||
group.leave()
|
group.leave()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
group.notify(queue: .main) {
|
group.notify(queue: .main) {
|
||||||
completeHandler?(failuresCount)
|
completeHandler?(failuresCount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func updateProvider(for type: ProviderType, name: String, completeHandler: ((Bool) -> Void)? = nil) {
|
static func updateProvider(for type: ProviderType, name: String, completeHandler: ((Bool) -> Void)? = nil) {
|
||||||
let s = "Update \(type.logString()) Provider"
|
let s = "Update \(type.logString()) Provider"
|
||||||
|
|
||||||
Logger.log("\(s) \(name)")
|
Logger.log("\(s) \(name)")
|
||||||
req("/providers/\(type.apiString())/\(name)", method: .put).response {
|
req("/providers/\(type.apiString())/\(name)", method: .put).response {
|
||||||
let re = $0.response?.statusCode == 204
|
let re = $0.response?.statusCode == 204
|
||||||
|
@ -353,7 +353,7 @@ extension ApiRequest {
|
||||||
completeHandler?(re)
|
completeHandler?(re)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func requestRuleProviderList(completeHandler: @escaping (ClashRuleProviderResp) -> Void) {
|
static func requestRuleProviderList(completeHandler: @escaping (ClashRuleProviderResp) -> Void) {
|
||||||
req("/providers/rules")
|
req("/providers/rules")
|
||||||
.responseDecodable(of: ClashRuleProviderResp.self, decoder: ClashProviderResp.decoder) { resp in
|
.responseDecodable(of: ClashRuleProviderResp.self, decoder: ClashProviderResp.decoder) { resp in
|
||||||
|
@ -366,18 +366,18 @@ extension ApiRequest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func updateGEO(completeHandler: ((Bool) -> Void)? = nil) {
|
static func updateGEO(completeHandler: ((Bool) -> Void)? = nil) {
|
||||||
Logger.log("UpdateGEO")
|
Logger.log("UpdateGEO")
|
||||||
req("/configs/geo", method: .post).response {
|
req("/configs/geo", method: .post).response {
|
||||||
let re = $0.response?.statusCode == 204
|
let re = $0.response?.statusCode == 204
|
||||||
|
|
||||||
completeHandler?(re)
|
completeHandler?(re)
|
||||||
// Logger.log("UpdateGEO \(re ? "success" : "failed")")
|
// Logger.log("UpdateGEO \(re ? "success" : "failed")")
|
||||||
Logger.log("Updating GEO Databases...")
|
Logger.log("Updating GEO Databases...")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func updateSniffing(enable: Bool, completeHandler: (() -> Void)? = nil) {
|
static func updateSniffing(enable: Bool, completeHandler: (() -> Void)? = nil) {
|
||||||
Logger.log("update sniffing:\(enable)", level: .debug)
|
Logger.log("update sniffing:\(enable)", level: .debug)
|
||||||
req("/configs",
|
req("/configs",
|
||||||
|
@ -388,7 +388,7 @@ extension ApiRequest {
|
||||||
completeHandler?()
|
completeHandler?()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func flushFakeipCache(completeHandler: ((Bool) -> Void)? = nil) {
|
static func flushFakeipCache(completeHandler: ((Bool) -> Void)? = nil) {
|
||||||
Logger.log("FlushFakeipCache")
|
Logger.log("FlushFakeipCache")
|
||||||
req("/cache/fakeip/flush",
|
req("/cache/fakeip/flush",
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
//
|
||||||
|
// ClashMetaConfig.swift
|
||||||
|
// ClashX Meta
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Cocoa
|
||||||
|
import Yams
|
||||||
|
|
||||||
|
class ClashMetaConfig: NSObject {
|
||||||
|
|
||||||
|
struct Config: Codable {
|
||||||
|
var externalUI: String? = {
|
||||||
|
guard let htmlPath = Bundle.main.path(forResource: "index", ofType: "html", inDirectory: "dashboard") else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return URL(fileURLWithPath: htmlPath).deletingLastPathComponent().path
|
||||||
|
}()
|
||||||
|
|
||||||
|
var externalController = "127.0.0.1:9090"
|
||||||
|
var secret: String?
|
||||||
|
|
||||||
|
var port: Int?
|
||||||
|
var socksPort: Int?
|
||||||
|
var mixedPort: Int?
|
||||||
|
|
||||||
|
var logLevel = ConfigManager.selectLoggingApiLevel.rawValue
|
||||||
|
|
||||||
|
var path: String {
|
||||||
|
get {
|
||||||
|
guard let s = try? YAMLEncoder().encode(self),
|
||||||
|
let path = RemoteConfigManager.createCacheConfig(string: s) else {
|
||||||
|
assertionFailure("Create init config file failed.")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CodingKeys: String, CodingKey {
|
||||||
|
case externalController = "external-controller",
|
||||||
|
externalUI = "external-ui",
|
||||||
|
mixedPort = "mixed-port",
|
||||||
|
port,
|
||||||
|
socksPort = "socks-port",
|
||||||
|
logLevel = "log-level",
|
||||||
|
secret
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func loadDefaultConfigFile() {
|
||||||
|
let fm = FileManager.default
|
||||||
|
guard let data = fm.contents(atPath: kDefaultConfigFilePath),
|
||||||
|
let string = String(data: data, encoding: .utf8),
|
||||||
|
let yaml = try? Yams.load(yaml: string) as? [String: Any] else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let keys = Config.CodingKeys.self
|
||||||
|
if let ec = yaml[keys.externalController.rawValue] as? String {
|
||||||
|
externalController = ec
|
||||||
|
}
|
||||||
|
|
||||||
|
if let s = yaml[keys.secret.rawValue] as? String {
|
||||||
|
secret = s
|
||||||
|
}
|
||||||
|
|
||||||
|
if let port = yaml[keys.mixedPort.rawValue] as? Int {
|
||||||
|
mixedPort = port
|
||||||
|
} else {
|
||||||
|
if let p = yaml[keys.port.rawValue] as? Int {
|
||||||
|
port = p
|
||||||
|
}
|
||||||
|
if let sp = yaml[keys.socksPort.rawValue] as? Int {
|
||||||
|
socksPort = sp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if port == nil && mixedPort == nil {
|
||||||
|
mixedPort = 7890
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func generateInitConfig() -> Config {
|
||||||
|
var config = Config()
|
||||||
|
config.loadDefaultConfigFile()
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -7,7 +7,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
//import Sparkle
|
// import Sparkle
|
||||||
|
|
||||||
class AutoUpgardeManager: NSObject {
|
class AutoUpgardeManager: NSObject {
|
||||||
static let shared = AutoUpgardeManager()
|
static let shared = AutoUpgardeManager()
|
||||||
|
@ -72,7 +72,7 @@ extension AutoUpgardeManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//extension AutoUpgardeManager: SUUpdaterDelegate {
|
// extension AutoUpgardeManager: SUUpdaterDelegate {
|
||||||
// func feedURLString(for updater: SUUpdater) -> String? {
|
// func feedURLString(for updater: SUUpdater) -> String? {
|
||||||
// return current.urlString
|
// return current.urlString
|
||||||
// }
|
// }
|
||||||
|
@ -80,7 +80,7 @@ extension AutoUpgardeManager {
|
||||||
// func updaterWillRelaunchApplication(_ updater: SUUpdater) {
|
// func updaterWillRelaunchApplication(_ updater: SUUpdater) {
|
||||||
// SystemProxyManager.shared.disableProxy(port: 0, socksPort: 0, forceDisable: true)
|
// SystemProxyManager.shared.disableProxy(port: 0, socksPort: 0, forceDisable: true)
|
||||||
// }
|
// }
|
||||||
//}
|
// }
|
||||||
|
|
||||||
// MARK: - Channel Enum
|
// MARK: - Channel Enum
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ class ClashResourceManager {
|
||||||
case geosite = "geosite.dat"
|
case geosite = "geosite.dat"
|
||||||
case geoip = "geoip.dat"
|
case geoip = "geoip.dat"
|
||||||
}
|
}
|
||||||
|
|
||||||
static func check() -> Bool {
|
static func check() -> Bool {
|
||||||
checkConfigDir()
|
checkConfigDir()
|
||||||
checkMMDB()
|
checkMMDB()
|
||||||
|
@ -34,7 +34,7 @@ class ClashResourceManager {
|
||||||
checkRule(.geoip)
|
checkRule(.geoip)
|
||||||
checkRule(.geosite)
|
checkRule(.geosite)
|
||||||
}
|
}
|
||||||
|
|
||||||
static func checkRule(_ file: RuleFiles) {
|
static func checkRule(_ file: RuleFiles) {
|
||||||
let fileManage = FileManager.default
|
let fileManage = FileManager.default
|
||||||
let destPath = "\(kConfigFolderPath)/\(file.rawValue)"
|
let destPath = "\(kConfigFolderPath)/\(file.rawValue)"
|
||||||
|
|
|
@ -18,20 +18,20 @@ class MenuItemFactory {
|
||||||
UserDefaults.standard.set(useViewToRenderProxy, forKey: "useViewToRenderProxy")
|
UserDefaults.standard.set(useViewToRenderProxy, forKey: "useViewToRenderProxy")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static var hideUnselectable: Int = UserDefaults.standard.object(forKey: "hideUnselectable") as? Int ?? NSControl.StateValue.off.rawValue {
|
static var hideUnselectable: Int = UserDefaults.standard.object(forKey: "hideUnselectable") as? Int ?? NSControl.StateValue.off.rawValue {
|
||||||
didSet {
|
didSet {
|
||||||
UserDefaults.standard.set(hideUnselectable, forKey: "hideUnselectable")
|
UserDefaults.standard.set(hideUnselectable, forKey: "hideUnselectable")
|
||||||
recreateProxyMenuItems()
|
recreateProxyMenuItems()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static var useAlphaCore: Bool = UserDefaults.standard.object(forKey: "useAlphaCore") as? Bool ?? false {
|
static var useAlphaCore: Bool = UserDefaults.standard.object(forKey: "useAlphaCore") as? Bool ?? false {
|
||||||
didSet {
|
didSet {
|
||||||
UserDefaults.standard.set(useAlphaCore, forKey: "useAlphaCore")
|
UserDefaults.standard.set(useAlphaCore, forKey: "useAlphaCore")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static let updateAllProvidersTitle = "Update All Providers"
|
static let updateAllProvidersTitle = "Update All Providers"
|
||||||
|
|
||||||
// MARK: - Public
|
// MARK: - Public
|
||||||
|
@ -58,7 +58,7 @@ class MenuItemFactory {
|
||||||
refreshMenuItems(mergedData: proxyInfo)
|
refreshMenuItems(mergedData: proxyInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func recreateRuleProvidersMenuItems() {
|
static func recreateRuleProvidersMenuItems() {
|
||||||
ApiRequest.requestRuleProviderList {
|
ApiRequest.requestRuleProviderList {
|
||||||
refreshRuleProviderMenuItems($0.allProviders.map({ $0.value }))
|
refreshRuleProviderMenuItems($0.allProviders.map({ $0.value }))
|
||||||
|
@ -68,9 +68,9 @@ class MenuItemFactory {
|
||||||
static func refreshMenuItems(mergedData proxyInfo: ClashProxyResp?) {
|
static func refreshMenuItems(mergedData proxyInfo: ClashProxyResp?) {
|
||||||
let leftPadding = AppDelegate.shared.hasMenuSelected()
|
let leftPadding = AppDelegate.shared.hasMenuSelected()
|
||||||
guard let proxyInfo = proxyInfo else { return }
|
guard let proxyInfo = proxyInfo else { return }
|
||||||
|
|
||||||
let hideState = NSControl.StateValue(rawValue: hideUnselectable)
|
let hideState = NSControl.StateValue(rawValue: hideUnselectable)
|
||||||
|
|
||||||
var menuItems = [NSMenuItem]()
|
var menuItems = [NSMenuItem]()
|
||||||
var collapsedItems = [NSMenuItem]()
|
var collapsedItems = [NSMenuItem]()
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ class MenuItemFactory {
|
||||||
guard let menu = menu else {
|
guard let menu = menu else {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
switch hideState {
|
switch hideState {
|
||||||
case .mixed where [.urltest, .fallback, .loadBalance, .relay].contains(proxy.type):
|
case .mixed where [.urltest, .fallback, .loadBalance, .relay].contains(proxy.type):
|
||||||
collapsedItems.append(menu)
|
collapsedItems.append(menu)
|
||||||
|
@ -101,19 +101,19 @@ class MenuItemFactory {
|
||||||
menu.isEnabled = true
|
menu.isEnabled = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if hideState == .mixed {
|
if hideState == .mixed {
|
||||||
let collapsedItem = NSMenuItem(title: "Collapsed", action: nil, keyEquivalent: "")
|
let collapsedItem = NSMenuItem(title: "Collapsed", action: nil, keyEquivalent: "")
|
||||||
collapsedItem.isEnabled = true
|
collapsedItem.isEnabled = true
|
||||||
collapsedItem.submenu = .init(title: "")
|
collapsedItem.submenu = .init(title: "")
|
||||||
collapsedItem.submenu?.items = collapsedItems
|
collapsedItem.submenu?.items = collapsedItems
|
||||||
|
|
||||||
menuItems.append(collapsedItem)
|
menuItems.append(collapsedItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
let items = Array(menuItems.reversed())
|
let items = Array(menuItems.reversed())
|
||||||
updateProxyList(withMenus: items)
|
updateProxyList(withMenus: items)
|
||||||
|
|
||||||
refreshProxyProviderMenuItems(mergedData: proxyInfo)
|
refreshProxyProviderMenuItems(mergedData: proxyInfo)
|
||||||
recreateRuleProvidersMenuItems()
|
recreateRuleProvidersMenuItems()
|
||||||
}
|
}
|
||||||
|
@ -301,11 +301,10 @@ extension MenuItemFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: - Meta
|
// MARK: - Meta
|
||||||
|
|
||||||
extension MenuItemFactory {
|
extension MenuItemFactory {
|
||||||
|
|
||||||
static func refreshProxyProviderMenuItems(mergedData proxyInfo: ClashProxyResp?) {
|
static func refreshProxyProviderMenuItems(mergedData proxyInfo: ClashProxyResp?) {
|
||||||
let app = AppDelegate.shared
|
let app = AppDelegate.shared
|
||||||
guard let proxyInfo = proxyInfo,
|
guard let proxyInfo = proxyInfo,
|
||||||
|
@ -316,7 +315,7 @@ extension MenuItemFactory {
|
||||||
let proxyProviders = providers.allProviders.filter {
|
let proxyProviders = providers.allProviders.filter {
|
||||||
$0.value.vehicleType == .HTTP
|
$0.value.vehicleType == .HTTP
|
||||||
}.values.sorted(by: { $0.name < $1.name })
|
}.values.sorted(by: { $0.name < $1.name })
|
||||||
|
|
||||||
let isEmpty = proxyProviders.count == 0
|
let isEmpty = proxyProviders.count == 0
|
||||||
app.proxyProvidersMenuItem.isEnabled = !isEmpty
|
app.proxyProvidersMenuItem.isEnabled = !isEmpty
|
||||||
guard !isEmpty else { return }
|
guard !isEmpty else { return }
|
||||||
|
@ -334,16 +333,16 @@ extension MenuItemFactory {
|
||||||
menu.addItem(item)
|
menu.addItem(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func refreshRuleProviderMenuItems(_ ruleProviders: [ClashRuleProvider]) {
|
static func refreshRuleProviderMenuItems(_ ruleProviders: [ClashRuleProvider]) {
|
||||||
let app = AppDelegate.shared
|
let app = AppDelegate.shared
|
||||||
let isEmpty = ruleProviders.count == 0
|
let isEmpty = ruleProviders.count == 0
|
||||||
app.ruleProvidersMenuItem.isEnabled = !isEmpty
|
app.ruleProvidersMenuItem.isEnabled = !isEmpty
|
||||||
|
|
||||||
guard !isEmpty,
|
guard !isEmpty,
|
||||||
let menu = app.ruleProvidersMenu
|
let menu = app.ruleProvidersMenu
|
||||||
else { return }
|
else { return }
|
||||||
|
|
||||||
initUpdateAllProvidersMenuItem(for: menu, type: .rule)
|
initUpdateAllProvidersMenuItem(for: menu, type: .rule)
|
||||||
let maxNameLength = maxProvidersLength(for: ruleProviders.map({ $0.name }))
|
let maxNameLength = maxProvidersLength(for: ruleProviders.map({ $0.name }))
|
||||||
ruleProviders.sorted(by: { $0.name < $1.name })
|
ruleProviders.sorted(by: { $0.name < $1.name })
|
||||||
|
@ -358,7 +357,7 @@ extension MenuItemFactory {
|
||||||
menu.addItem(item)
|
menu.addItem(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func initUpdateAllProvidersMenuItem(for menu: NSMenu, type: ApiRequest.ProviderType) {
|
static func initUpdateAllProvidersMenuItem(for menu: NSMenu, type: ApiRequest.ProviderType) {
|
||||||
if menu.items.count > 1 {
|
if menu.items.count > 1 {
|
||||||
menu.items.enumerated().filter {
|
menu.items.enumerated().filter {
|
||||||
|
@ -374,7 +373,7 @@ extension MenuItemFactory {
|
||||||
menu.addItem(.separator())
|
menu.addItem(.separator())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func maxProvidersLength(for names: [String]) -> CGFloat {
|
static func maxProvidersLength(for names: [String]) -> CGFloat {
|
||||||
func getLength(_ string: String) -> CGFloat {
|
func getLength(_ string: String) -> CGFloat {
|
||||||
let rect = CGSize(width: CGFloat.greatestFiniteMagnitude, height: 20)
|
let rect = CGSize(width: CGFloat.greatestFiniteMagnitude, height: 20)
|
||||||
|
@ -385,30 +384,30 @@ extension MenuItemFactory {
|
||||||
attributes: attr).width
|
attributes: attr).width
|
||||||
return length
|
return length
|
||||||
}
|
}
|
||||||
|
|
||||||
var lengths = names.map {
|
var lengths = names.map {
|
||||||
getLength($0) + 65
|
getLength($0) + 65
|
||||||
}
|
}
|
||||||
lengths.append(getLength(updateAllProvidersTitle))
|
lengths.append(getLength(updateAllProvidersTitle))
|
||||||
return lengths.max() ?? 0
|
return lengths.max() ?? 0
|
||||||
}
|
}
|
||||||
|
|
||||||
static func providerUpdateTitle(_ updatedAt: String?) -> String? {
|
static func providerUpdateTitle(_ updatedAt: String?) -> String? {
|
||||||
let dateCF = DateComponentsFormatter()
|
let dateCF = DateComponentsFormatter()
|
||||||
dateCF.allowedUnits = [.day, .hour, .minute]
|
dateCF.allowedUnits = [.day, .hour, .minute]
|
||||||
dateCF.maximumUnitCount = 1
|
dateCF.maximumUnitCount = 1
|
||||||
dateCF.unitsStyle = .abbreviated
|
dateCF.unitsStyle = .abbreviated
|
||||||
dateCF.zeroFormattingBehavior = .dropAll
|
dateCF.zeroFormattingBehavior = .dropAll
|
||||||
|
|
||||||
guard let dateStr = updatedAt,
|
guard let dateStr = updatedAt,
|
||||||
let date = DateFormatter.provider.date(from: dateStr),
|
let date = DateFormatter.provider.date(from: dateStr),
|
||||||
!date.timeIntervalSinceNow.isNaN,
|
!date.timeIntervalSinceNow.isNaN,
|
||||||
!date.timeIntervalSinceNow.isInfinite,
|
!date.timeIntervalSinceNow.isInfinite,
|
||||||
let re = dateCF.string(from: abs(date.timeIntervalSinceNow)) else { return nil }
|
let re = dateCF.string(from: abs(date.timeIntervalSinceNow)) else { return nil }
|
||||||
|
|
||||||
return "\(re) ago"
|
return "\(re) ago"
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc static func actionUpdateAllProviders(sender: NSMenuItem) {
|
@objc static func actionUpdateAllProviders(sender: NSMenuItem) {
|
||||||
let type = ApiRequest.ProviderType(rawValue: sender.tag)!
|
let type = ApiRequest.ProviderType(rawValue: sender.tag)!
|
||||||
let s = "Update All \(type.logString()) Providers"
|
let s = "Update All \(type.logString()) Providers"
|
||||||
|
@ -420,11 +419,11 @@ extension MenuItemFactory {
|
||||||
recreateProxyMenuItems()
|
recreateProxyMenuItems()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc static func actionUpdateSelectProvider(sender: DualTitleMenuItem) {
|
@objc static func actionUpdateSelectProvider(sender: DualTitleMenuItem) {
|
||||||
let name = sender.originTitle
|
let name = sender.originTitle
|
||||||
let type = ApiRequest.ProviderType(rawValue: sender.tag)!
|
let type = ApiRequest.ProviderType(rawValue: sender.tag)!
|
||||||
|
|
||||||
let log = "Update \(type.logString()) Provider \(name)"
|
let log = "Update \(type.logString()) Provider \(name)"
|
||||||
Logger.log(log)
|
Logger.log(log)
|
||||||
ApiRequest.updateProvider(for: type, name: name) {
|
ApiRequest.updateProvider(for: type, name: name) {
|
||||||
|
|
|
@ -14,7 +14,7 @@ import RxCocoa
|
||||||
class PrivilegedHelperManager {
|
class PrivilegedHelperManager {
|
||||||
let isHelperCheckFinished = BehaviorRelay<Bool>(value: false)
|
let isHelperCheckFinished = BehaviorRelay<Bool>(value: false)
|
||||||
private var cancelInstallCheck = false
|
private var cancelInstallCheck = false
|
||||||
|
|
||||||
private let useLegacyInstall = true
|
private let useLegacyInstall = true
|
||||||
|
|
||||||
private var authRef: AuthorizationRef?
|
private var authRef: AuthorizationRef?
|
||||||
|
|
|
@ -13,6 +13,8 @@ class RemoteConfigManager {
|
||||||
var configs: [RemoteConfigModel] = []
|
var configs: [RemoteConfigModel] = []
|
||||||
var refreshActivity: NSBackgroundActivityScheduler?
|
var refreshActivity: NSBackgroundActivityScheduler?
|
||||||
|
|
||||||
|
let verifyConfigTask = MetaTask()
|
||||||
|
|
||||||
static let shared = RemoteConfigManager()
|
static let shared = RemoteConfigManager()
|
||||||
|
|
||||||
private init() {
|
private init() {
|
||||||
|
@ -205,29 +207,28 @@ class RemoteConfigManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func verifyConfig(string: String) -> ErrorString? {
|
static func createCacheConfig(string: String) -> String? {
|
||||||
var res: String?
|
|
||||||
let path = NSTemporaryDirectory().appending("com.MetaCubeX.ClashX.meta")
|
let path = NSTemporaryDirectory().appending("com.MetaCubeX.ClashX.meta")
|
||||||
let confPath = path + "/\(UUID().uuidString).yaml"
|
let confPath = path + "/\(UUID().uuidString).yaml"
|
||||||
|
|
||||||
let fm = FileManager.default
|
let fm = FileManager.default
|
||||||
try? fm.createDirectory(atPath: path, withIntermediateDirectories: true)
|
try? fm.createDirectory(atPath: path, withIntermediateDirectories: true)
|
||||||
|
|
||||||
|
if fm.fileExists(atPath: confPath) {
|
||||||
|
try? fm.removeItem(atPath: confPath)
|
||||||
|
}
|
||||||
|
|
||||||
guard fm.createFile(atPath: confPath, contents: string.data(using: .utf8)) else {
|
guard fm.createFile(atPath: confPath, contents: string.data(using: .utf8)) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return confPath
|
||||||
|
}
|
||||||
|
|
||||||
|
static func verifyConfig(string: String) -> ErrorString? {
|
||||||
|
guard let confPath = createCacheConfig(string: string) else {
|
||||||
return "Create verify config file failed"
|
return "Create verify config file failed"
|
||||||
}
|
}
|
||||||
|
return RemoteConfigManager.shared.verifyConfigTask.test(kConfigFolderPath, confFilePath: confPath)
|
||||||
let queue = DispatchGroup()
|
|
||||||
queue.enter()
|
|
||||||
PrivilegedHelperManager.shared.helper {
|
|
||||||
res = "unknown error"
|
|
||||||
queue.leave()
|
|
||||||
}?.verifyMeta(withConfPath: kConfigFolderPath, confFilePath: "", result: {
|
|
||||||
res = $0
|
|
||||||
queue.leave()
|
|
||||||
})
|
|
||||||
|
|
||||||
queue.wait()
|
|
||||||
return res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static func showAdd() {
|
static func showAdd() {
|
||||||
|
|
|
@ -19,11 +19,11 @@ struct Paths {
|
||||||
static func configFileName(for name: String) -> String {
|
static func configFileName(for name: String) -> String {
|
||||||
return "\(name).yaml"
|
return "\(name).yaml"
|
||||||
}
|
}
|
||||||
|
|
||||||
static func defaultCorePath() -> String? {
|
static func defaultCorePath() -> String? {
|
||||||
Bundle.main.path(forResource: "com.metacubex.ClashX.ProxyConfigHelper.meta", ofType: nil)
|
Bundle.main.path(forResource: "com.metacubex.ClashX.ProxyConfigHelper.meta", ofType: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
static func alphaCorePath() -> URL? {
|
static func alphaCorePath() -> URL? {
|
||||||
FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
|
FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
|
||||||
.first?
|
.first?
|
||||||
|
|
|
@ -39,7 +39,7 @@ class ClashConfig: Codable {
|
||||||
var mixedPort: Int
|
var mixedPort: Int
|
||||||
var mode: ClashProxyMode
|
var mode: ClashProxyMode
|
||||||
var logLevel: ClashLogLevel
|
var logLevel: ClashLogLevel
|
||||||
|
|
||||||
var sniffing: Bool
|
var sniffing: Bool
|
||||||
|
|
||||||
var usedHttpPort: Int {
|
var usedHttpPort: Int {
|
||||||
|
|
|
@ -2,9 +2,11 @@
|
||||||
## 配置文件需要放置在 $HOME/.config/clash/*.yaml
|
## 配置文件需要放置在 $HOME/.config/clash/*.yaml
|
||||||
|
|
||||||
## 这份文件是clashX的基础配置文件,请尽量新建配置文件进行修改。
|
## 这份文件是clashX的基础配置文件,请尽量新建配置文件进行修改。
|
||||||
## !!!只有这份文件的端口设置会随ClashX启动生效
|
|
||||||
|
|
||||||
## 如果您不知道如何操作,请参阅 官方Github文档 https://github.com/Dreamacro/clash/blob/dev/README.md
|
## 如果您不知道如何操作,请参阅
|
||||||
|
## Clash Meta wiki https://docs.metacubex.one
|
||||||
|
## Clash Meta Github文档 https://github.com/MetaCubeX/Clash.Meta/blob/Alpha/README.md
|
||||||
|
|
||||||
#---------------------------------------------------#
|
#---------------------------------------------------#
|
||||||
|
|
||||||
# (HTTP and SOCKS5 in one port)
|
# (HTTP and SOCKS5 in one port)
|
||||||
|
@ -14,11 +16,18 @@ external-controller: 127.0.0.1:9090
|
||||||
allow-lan: false
|
allow-lan: false
|
||||||
mode: rule
|
mode: rule
|
||||||
log-level: warning
|
log-level: warning
|
||||||
|
ipv6: false
|
||||||
|
|
||||||
|
|
||||||
proxies:
|
proxies:
|
||||||
|
|
||||||
proxy-groups:
|
proxy-groups:
|
||||||
|
|
||||||
|
proxy-providers:
|
||||||
|
|
||||||
|
rule-providers:
|
||||||
|
|
||||||
|
|
||||||
rules:
|
rules:
|
||||||
- DOMAIN-SUFFIX,google.com,DIRECT
|
- DOMAIN-SUFFIX,google.com,DIRECT
|
||||||
- DOMAIN-KEYWORD,google,DIRECT
|
- DOMAIN-KEYWORD,google,DIRECT
|
||||||
|
|
|
@ -2,11 +2,10 @@
|
||||||
// DualTitleMenuItem.swift
|
// DualTitleMenuItem.swift
|
||||||
// ClashX
|
// ClashX
|
||||||
|
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class DualTitleMenuItem: NSMenuItem {
|
class DualTitleMenuItem: NSMenuItem {
|
||||||
|
|
||||||
var originTitle: String = ""
|
var originTitle: String = ""
|
||||||
|
|
||||||
convenience init(_ title: String,
|
convenience init(_ title: String,
|
||||||
|
@ -17,7 +16,7 @@ class DualTitleMenuItem: NSMenuItem {
|
||||||
originTitle = title
|
originTitle = title
|
||||||
setAttributedTitle(name: title, secondLabel: subTitle, maxLength: maxLength)
|
setAttributedTitle(name: title, secondLabel: subTitle, maxLength: maxLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setAttributedTitle(name: String, secondLabel: String?, maxLength: CGFloat = 0) {
|
func setAttributedTitle(name: String, secondLabel: String?, maxLength: CGFloat = 0) {
|
||||||
let paragraph = NSMutableParagraphStyle()
|
let paragraph = NSMutableParagraphStyle()
|
||||||
paragraph.tabStops = [
|
paragraph.tabStops = [
|
||||||
|
|
2
Podfile
2
Podfile
|
@ -28,5 +28,7 @@ target 'ClashX Meta' do
|
||||||
pod 'Starscream','3.1.1'
|
pod 'Starscream','3.1.1'
|
||||||
pod "FlexibleDiff"
|
pod "FlexibleDiff"
|
||||||
pod 'GzipSwift'
|
pod 'GzipSwift'
|
||||||
|
pod 'Yams'
|
||||||
|
pod "PromiseKit", "~> 6.8"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
16
Podfile.lock
16
Podfile.lock
|
@ -6,6 +6,13 @@ PODS:
|
||||||
- FlexibleDiff (0.0.9)
|
- FlexibleDiff (0.0.9)
|
||||||
- GzipSwift (5.1.1)
|
- GzipSwift (5.1.1)
|
||||||
- LetsMove (1.25)
|
- LetsMove (1.25)
|
||||||
|
- PromiseKit (6.18.0):
|
||||||
|
- PromiseKit/CorePromise (= 6.18.0)
|
||||||
|
- PromiseKit/Foundation (= 6.18.0)
|
||||||
|
- PromiseKit/UIKit (= 6.18.0)
|
||||||
|
- PromiseKit/CorePromise (6.18.0)
|
||||||
|
- PromiseKit/Foundation (6.18.0):
|
||||||
|
- PromiseKit/CorePromise
|
||||||
- RxCocoa (6.5.0):
|
- RxCocoa (6.5.0):
|
||||||
- RxRelay (= 6.5.0)
|
- RxRelay (= 6.5.0)
|
||||||
- RxSwift (= 6.5.0)
|
- RxSwift (= 6.5.0)
|
||||||
|
@ -15,6 +22,7 @@ PODS:
|
||||||
- Starscream (3.1.1)
|
- Starscream (3.1.1)
|
||||||
- SwiftyJSON (5.0.1)
|
- SwiftyJSON (5.0.1)
|
||||||
- WebViewJavascriptBridge (6.0.3)
|
- WebViewJavascriptBridge (6.0.3)
|
||||||
|
- Yams (5.0.0)
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- Alamofire (~> 5.0)
|
- Alamofire (~> 5.0)
|
||||||
|
@ -22,11 +30,13 @@ DEPENDENCIES:
|
||||||
- FlexibleDiff
|
- FlexibleDiff
|
||||||
- GzipSwift
|
- GzipSwift
|
||||||
- LetsMove
|
- LetsMove
|
||||||
|
- PromiseKit (~> 6.8)
|
||||||
- RxCocoa
|
- RxCocoa
|
||||||
- RxSwift
|
- RxSwift
|
||||||
- Starscream (= 3.1.1)
|
- Starscream (= 3.1.1)
|
||||||
- SwiftyJSON
|
- SwiftyJSON
|
||||||
- WebViewJavascriptBridge
|
- WebViewJavascriptBridge
|
||||||
|
- Yams
|
||||||
|
|
||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
trunk:
|
trunk:
|
||||||
|
@ -35,12 +45,14 @@ SPEC REPOS:
|
||||||
- FlexibleDiff
|
- FlexibleDiff
|
||||||
- GzipSwift
|
- GzipSwift
|
||||||
- LetsMove
|
- LetsMove
|
||||||
|
- PromiseKit
|
||||||
- RxCocoa
|
- RxCocoa
|
||||||
- RxRelay
|
- RxRelay
|
||||||
- RxSwift
|
- RxSwift
|
||||||
- Starscream
|
- Starscream
|
||||||
- SwiftyJSON
|
- SwiftyJSON
|
||||||
- WebViewJavascriptBridge
|
- WebViewJavascriptBridge
|
||||||
|
- Yams
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
Alamofire: 87bd8c952f9a4454320fce00d9cc3de57bcadaf5
|
Alamofire: 87bd8c952f9a4454320fce00d9cc3de57bcadaf5
|
||||||
|
@ -48,13 +60,15 @@ SPEC CHECKSUMS:
|
||||||
FlexibleDiff: b9ee9b8305b42c784f5dd40589203c97c55bbaa0
|
FlexibleDiff: b9ee9b8305b42c784f5dd40589203c97c55bbaa0
|
||||||
GzipSwift: 893f3e48e597a1a4f62fafcb6514220fcf8287fa
|
GzipSwift: 893f3e48e597a1a4f62fafcb6514220fcf8287fa
|
||||||
LetsMove: 7b9fe44737707d984fbd3f47af46609a9a07b461
|
LetsMove: 7b9fe44737707d984fbd3f47af46609a9a07b461
|
||||||
|
PromiseKit: 879fa89b55fcb8f11ade9aad0c9721850bb71b9f
|
||||||
RxCocoa: 94f817b71c07517321eb4f9ad299112ca8af743b
|
RxCocoa: 94f817b71c07517321eb4f9ad299112ca8af743b
|
||||||
RxRelay: 1de1523e604c72b6c68feadedd1af3b1b4d0ecbd
|
RxRelay: 1de1523e604c72b6c68feadedd1af3b1b4d0ecbd
|
||||||
RxSwift: 5710a9e6b17f3c3d6e40d6e559b9fa1e813b2ef8
|
RxSwift: 5710a9e6b17f3c3d6e40d6e559b9fa1e813b2ef8
|
||||||
Starscream: 4bb2f9942274833f7b4d296a55504dcfc7edb7b0
|
Starscream: 4bb2f9942274833f7b4d296a55504dcfc7edb7b0
|
||||||
SwiftyJSON: 2f33a42c6fbc52764d96f13368585094bfd8aa5e
|
SwiftyJSON: 2f33a42c6fbc52764d96f13368585094bfd8aa5e
|
||||||
WebViewJavascriptBridge: 7f5bc4d3581e672e8f32bd0f812d54bc69bb8e29
|
WebViewJavascriptBridge: 7f5bc4d3581e672e8f32bd0f812d54bc69bb8e29
|
||||||
|
Yams: b5c3e188ceeb8321a9830b1d0e791ccea9af3201
|
||||||
|
|
||||||
PODFILE CHECKSUM: f4571fd419858d0946fa886653353d5b9e10a822
|
PODFILE CHECKSUM: 57cda85d5fd198c1d1f83f6a71b69a86b1884bb5
|
||||||
|
|
||||||
COCOAPODS: 1.11.3
|
COCOAPODS: 1.11.3
|
||||||
|
|
|
@ -9,9 +9,9 @@
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
<string>com.metacubex.ClashX.ProxyConfigHelper</string>
|
<string>com.metacubex.ClashX.ProxyConfigHelper</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.3</string>
|
<string>1.4</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>4</string>
|
<string>5</string>
|
||||||
<key>SMAuthorizedClients</key>
|
<key>SMAuthorizedClients</key>
|
||||||
<array>
|
<array>
|
||||||
<string>anchor apple generic and identifier "com.metacubex.ClashX.ProxyConfigHelper" and (certificate leaf[field.1.2.840.113635.100.6.1.9] /* exists */ or certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = MEWHFZ92DY)</string>
|
<string>anchor apple generic and identifier "com.metacubex.ClashX.ProxyConfigHelper" and (certificate leaf[field.1.2.840.113635.100.6.1.9] /* exists */ or certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = MEWHFZ92DY)</string>
|
||||||
|
|
|
@ -8,7 +8,7 @@ import Cocoa
|
||||||
class MetaTask: NSObject {
|
class MetaTask: NSObject {
|
||||||
|
|
||||||
struct MetaServer: Encodable {
|
struct MetaServer: Encodable {
|
||||||
let externalController: String
|
var externalController: String
|
||||||
let secret: String
|
let secret: String
|
||||||
var log: String = ""
|
var log: String = ""
|
||||||
|
|
||||||
|
@ -29,7 +29,6 @@ class MetaTask: NSObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
let proc = Process()
|
let proc = Process()
|
||||||
var uiPath: String?
|
|
||||||
let procQueue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".MetaProcess")
|
let procQueue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".MetaProcess")
|
||||||
|
|
||||||
var timer: DispatchSourceTimer?
|
var timer: DispatchSourceTimer?
|
||||||
|
@ -39,10 +38,6 @@ class MetaTask: NSObject {
|
||||||
proc.executableURL = .init(fileURLWithPath: path)
|
proc.executableURL = .init(fileURLWithPath: path)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func setUIPath(_ path: String) {
|
|
||||||
uiPath = path
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func start(_ confPath: String,
|
@objc func start(_ confPath: String,
|
||||||
confFilePath: String,
|
confFilePath: String,
|
||||||
result: @escaping stringReplyBlock) {
|
result: @escaping stringReplyBlock) {
|
||||||
|
@ -71,13 +66,6 @@ class MetaTask: NSObject {
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
if let uiPath = uiPath {
|
|
||||||
args.append(contentsOf: [
|
|
||||||
"-ext-ui",
|
|
||||||
uiPath
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
killOldProc()
|
killOldProc()
|
||||||
|
|
||||||
procQueue.async {
|
procQueue.async {
|
||||||
|
@ -94,6 +82,17 @@ class MetaTask: NSObject {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let port = serverResult.externalController.components(separatedBy: ":").last ?? "9090"
|
||||||
|
if let p = Int(port) {
|
||||||
|
let newPort = self.updateExternalControllerPort(p)
|
||||||
|
let ec = "127.0.0.1:\(newPort)"
|
||||||
|
args.append(contentsOf: [
|
||||||
|
"-ext-ctl",
|
||||||
|
ec
|
||||||
|
])
|
||||||
|
serverResult.externalController = ec
|
||||||
|
}
|
||||||
|
|
||||||
self.proc.arguments = args
|
self.proc.arguments = args
|
||||||
let pipe = Pipe()
|
let pipe = Pipe()
|
||||||
var logs = [String]()
|
var logs = [String]()
|
||||||
|
@ -276,7 +275,7 @@ class MetaTask: NSObject {
|
||||||
return (Int32(pid) ?? 0, addr)
|
return (Int32(pid) ?? 0, addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func findExternalControllerPort(_ port: Int) -> Int {
|
func updateExternalControllerPort(_ port: Int) -> Int {
|
||||||
let proc = Process()
|
let proc = Process()
|
||||||
let pipe = Pipe()
|
let pipe = Pipe()
|
||||||
proc.standardOutput = pipe
|
proc.standardOutput = pipe
|
||||||
|
|
|
@ -79,6 +79,7 @@ ProxyConfigRemoteProcessProtocol
|
||||||
[weakSelf.connections removeObject:weakConnection];
|
[weakSelf.connections removeObject:weakConnection];
|
||||||
if (weakSelf.connections.count == 0) {
|
if (weakSelf.connections.count == 0) {
|
||||||
weakSelf.shouldQuit = YES;
|
weakSelf.shouldQuit = YES;
|
||||||
|
[weakSelf.metaTask stop];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
[self.connections addObject:newConnection];
|
[self.connections addObject:newConnection];
|
||||||
|
@ -140,10 +141,6 @@ ProxyConfigRemoteProcessProtocol
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)metaSetUIPath:(NSString *)path {
|
|
||||||
[self.metaTask setUIPath:path];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)startMetaWithConfPath:(NSString *)confPath ConfFilePath:(NSString *)confFilePath result:(stringReplyBlock)reply {
|
- (void)startMetaWithConfPath:(NSString *)confPath ConfFilePath:(NSString *)confFilePath result:(stringReplyBlock)reply {
|
||||||
[self.metaTask start:confPath confFilePath:confFilePath result:reply];
|
[self.metaTask start:confPath confFilePath:confFilePath result:reply];
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,6 @@ typedef void(^dictReplyBlock)(NSDictionary *);
|
||||||
|
|
||||||
- (void)initMetaCoreWithPath:(NSString *)path;
|
- (void)initMetaCoreWithPath:(NSString *)path;
|
||||||
|
|
||||||
- (void)metaSetUIPath:(NSString *)path;
|
|
||||||
|
|
||||||
- (void)startMetaWithConfPath:(NSString *)confPath
|
- (void)startMetaWithConfPath:(NSString *)confPath
|
||||||
ConfFilePath:(NSString *)confFilePath
|
ConfFilePath:(NSString *)confFilePath
|
||||||
result:(stringReplyBlock)reply;
|
result:(stringReplyBlock)reply;
|
||||||
|
|
Loading…
Reference in New Issue