mirror of
https://github.com/X1a0He/Adobe-Downloader.git
synced 2025-11-25 03:14:57 +08:00
feat: 调整 Helper
This commit is contained in:
@@ -39,6 +39,7 @@ class ModernPrivilegedHelperManager: NSObject, ObservableObject {
|
|||||||
case connected
|
case connected
|
||||||
case disconnected
|
case disconnected
|
||||||
case connecting
|
case connecting
|
||||||
|
case needsApproval
|
||||||
|
|
||||||
var description: String {
|
var description: String {
|
||||||
switch self {
|
switch self {
|
||||||
@@ -48,6 +49,8 @@ class ModernPrivilegedHelperManager: NSObject, ObservableObject {
|
|||||||
return String(localized: "未连接")
|
return String(localized: "未连接")
|
||||||
case .connecting:
|
case .connecting:
|
||||||
return String(localized: "正在连接")
|
return String(localized: "正在连接")
|
||||||
|
case .needsApproval:
|
||||||
|
return String(localized: "需要批准")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -111,9 +114,10 @@ class ModernPrivilegedHelperManager: NSObject, ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func setupAutoReconnect() {
|
private func setupAutoReconnect() {
|
||||||
Timer.scheduledTimer(withTimeInterval: 3.0, repeats: true) { [weak self] _ in
|
Timer.scheduledTimer(withTimeInterval: 10.0, repeats: true) { [weak self] _ in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
if self.connectionState == .disconnected && self.shouldAutoReconnect {
|
if self.connectionState == .disconnected && self.shouldAutoReconnect && !self.isInitializing {
|
||||||
|
self.logger.info("尝试自动重连Helper...")
|
||||||
Task {
|
Task {
|
||||||
await self.attemptConnection()
|
await self.attemptConnection()
|
||||||
}
|
}
|
||||||
@@ -122,6 +126,7 @@ class ModernPrivilegedHelperManager: NSObject, ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func checkAndInstallHelper() async {
|
func checkAndInstallHelper() async {
|
||||||
|
isInitializing = true
|
||||||
logger.info("开始检查 Helper 状态")
|
logger.info("开始检查 Helper 状态")
|
||||||
|
|
||||||
let status = await getHelperStatus()
|
let status = await getHelperStatus()
|
||||||
@@ -135,6 +140,7 @@ class ModernPrivilegedHelperManager: NSObject, ObservableObject {
|
|||||||
registerHelper()
|
registerHelper()
|
||||||
break
|
break
|
||||||
case .needsApproval:
|
case .needsApproval:
|
||||||
|
self.connectionState = .needsApproval
|
||||||
showApprovalGuidance()
|
showApprovalGuidance()
|
||||||
break
|
break
|
||||||
case .requiresUpdate:
|
case .requiresUpdate:
|
||||||
@@ -142,8 +148,17 @@ class ModernPrivilegedHelperManager: NSObject, ObservableObject {
|
|||||||
break
|
break
|
||||||
case .installed:
|
case .installed:
|
||||||
Task {
|
Task {
|
||||||
await attemptConnection()
|
let connectionResult = await attemptConnection()
|
||||||
connectionSuccessBlock?()
|
if connectionResult {
|
||||||
|
logger.info("Helper 连接成功")
|
||||||
|
connectionSuccessBlock?()
|
||||||
|
} else {
|
||||||
|
logger.warning("Helper 安装成功但连接失败,可能存在权限问题")
|
||||||
|
await MainActor.run {
|
||||||
|
self.connectionState = .disconnected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.isInitializing = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,6 +187,7 @@ class ModernPrivilegedHelperManager: NSObject, ObservableObject {
|
|||||||
return .installed
|
return .installed
|
||||||
|
|
||||||
case .requiresApproval:
|
case .requiresApproval:
|
||||||
|
logger.info("Helper需要用户批准")
|
||||||
return .needsApproval
|
return .needsApproval
|
||||||
|
|
||||||
case .notFound:
|
case .notFound:
|
||||||
@@ -179,7 +195,7 @@ class ModernPrivilegedHelperManager: NSObject, ObservableObject {
|
|||||||
|
|
||||||
@unknown default:
|
@unknown default:
|
||||||
logger.warning("未知的 SMAppService 状态: \(status.rawValue)")
|
logger.warning("未知的 SMAppService 状态: \(status.rawValue)")
|
||||||
return .notInstalled
|
return .needsApproval
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,15 +215,57 @@ class ModernPrivilegedHelperManager: NSObject, ObservableObject {
|
|||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
|
||||||
Task {
|
Task {
|
||||||
await self.attemptConnection()
|
await self.tryEnableHelper()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch {
|
} catch {
|
||||||
logger.error("Helper 注册失败: \(error)")
|
logger.error("Helper 注册失败: \(error)")
|
||||||
handleRegistrationError(error)
|
handleRegistrationError(error)
|
||||||
|
isInitializing = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func tryEnableHelper() async {
|
||||||
|
guard let appService = appService else { return }
|
||||||
|
|
||||||
|
let currentStatus = appService.status
|
||||||
|
logger.info("尝试启用Helper,当前状态: \(currentStatus.rawValue)")
|
||||||
|
|
||||||
|
if currentStatus != .enabled {
|
||||||
|
do {
|
||||||
|
logger.info("尝试重新注册Helper以启用")
|
||||||
|
try appService.register()
|
||||||
|
|
||||||
|
try await Task.sleep(nanoseconds: 2_000_000_000)
|
||||||
|
|
||||||
|
let newStatus = appService.status
|
||||||
|
logger.info("重新注册后状态: \(newStatus.rawValue)")
|
||||||
|
|
||||||
|
if newStatus == .enabled {
|
||||||
|
logger.info("Helper成功启用")
|
||||||
|
let connectionResult = await attemptConnection()
|
||||||
|
if connectionResult {
|
||||||
|
logger.info("Helper连接成功")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
logger.error("重新注册失败: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let connectionResult = await attemptConnection()
|
||||||
|
if !connectionResult {
|
||||||
|
logger.warning("Helper 注册成功但连接失败,需要用户批准")
|
||||||
|
await MainActor.run {
|
||||||
|
self.connectionState = .needsApproval
|
||||||
|
self.showApprovalGuidance()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.isInitializing = false
|
||||||
|
}
|
||||||
|
|
||||||
private func updateHelper() {
|
private func updateHelper() {
|
||||||
registerHelper()
|
registerHelper()
|
||||||
@@ -286,17 +344,33 @@ class ModernPrivilegedHelperManager: NSObject, ObservableObject {
|
|||||||
var isConnected = false
|
var isConnected = false
|
||||||
|
|
||||||
if let helper = newConnection.remoteObjectProxy as? HelperToolProtocol {
|
if let helper = newConnection.remoteObjectProxy as? HelperToolProtocol {
|
||||||
helper.executeCommand(type: .shellCommand, path1: "id -u", path2: "", permissions: 0) { [weak self] result in
|
helper.executeCommand(type: .shellCommand, path1: "whoami", path2: "", permissions: 0) { [weak self] result in
|
||||||
if result.contains("0") || result == "0" {
|
let trimmedResult = result.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
self?.logger.info("Helper 响应: \(trimmedResult)")
|
||||||
|
|
||||||
|
if trimmedResult == "root" {
|
||||||
isConnected = true
|
isConnected = true
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self?.connection = newConnection
|
self?.connection = newConnection
|
||||||
self?.connectionState = .connected
|
self?.connectionState = .connected
|
||||||
|
self?.logger.info("Helper 权限验证成功,当前用户: \(trimmedResult)")
|
||||||
}
|
}
|
||||||
|
} else if !trimmedResult.starts(with: "Error:") && !trimmedResult.isEmpty {
|
||||||
|
isConnected = true
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self?.connection = newConnection
|
||||||
|
self?.connectionState = .connected
|
||||||
|
self?.logger.warning("Helper 连接成功但权限可能不足,当前用户: \(trimmedResult)")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self?.logger.error("Helper 连接失败或权限不足: \(trimmedResult)")
|
||||||
}
|
}
|
||||||
semaphore.signal()
|
semaphore.signal()
|
||||||
}
|
}
|
||||||
_ = semaphore.wait(timeout: .now() + 1.0)
|
let waitResult = semaphore.wait(timeout: .now() + 8.0)
|
||||||
|
if waitResult == .timedOut {
|
||||||
|
logger.warning("Helper 连接超时")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isConnected {
|
if !isConnected {
|
||||||
@@ -335,7 +409,7 @@ class ModernPrivilegedHelperManager: NSObject, ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getHelperProxy() throws -> HelperToolProtocol {
|
func getHelperProxy() throws -> HelperToolProtocol {
|
||||||
if connectionState != .connected {
|
if connectionState != .connected || connection == nil {
|
||||||
guard let newConnection = connectionQueue.sync(execute: { createConnection() }) else {
|
guard let newConnection = connectionQueue.sync(execute: { createConnection() }) else {
|
||||||
throw HelperError.connectionFailed
|
throw HelperError.connectionFailed
|
||||||
}
|
}
|
||||||
@@ -344,7 +418,9 @@ class ModernPrivilegedHelperManager: NSObject, ObservableObject {
|
|||||||
|
|
||||||
guard let helper = connection?.remoteObjectProxyWithErrorHandler({ [weak self] error in
|
guard let helper = connection?.remoteObjectProxyWithErrorHandler({ [weak self] error in
|
||||||
self?.logger.error("XPC 代理错误: \(error)")
|
self?.logger.error("XPC 代理错误: \(error)")
|
||||||
self?.connectionState = .disconnected
|
DispatchQueue.main.async {
|
||||||
|
self?.connectionState = .disconnected
|
||||||
|
}
|
||||||
}) as? HelperToolProtocol else {
|
}) as? HelperToolProtocol else {
|
||||||
throw HelperError.proxyError
|
throw HelperError.proxyError
|
||||||
}
|
}
|
||||||
@@ -375,8 +451,11 @@ class ModernPrivilegedHelperManager: NSObject, ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
connectionState = .disconnected
|
DispatchQueue.main.async { [weak self] in
|
||||||
completion("Error: \(error.localizedDescription)")
|
self?.connectionState = .disconnected
|
||||||
|
self?.logger.error("执行命令失败: \(error.localizedDescription)")
|
||||||
|
completion("Error: \(error.localizedDescription)")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -435,10 +514,13 @@ class ModernPrivilegedHelperManager: NSObject, ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func updateConnectionState(from result: String) {
|
private func updateConnectionState(from result: String) {
|
||||||
if result.starts(with: "Error:") {
|
DispatchQueue.main.async { [weak self] in
|
||||||
connectionState = .disconnected
|
if result.starts(with: "Error:") {
|
||||||
} else {
|
self?.connectionState = .disconnected
|
||||||
connectionState = .connected
|
self?.logger.warning("命令执行失败,连接状态设为断开: \(result)")
|
||||||
|
} else {
|
||||||
|
self?.connectionState = .connected
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -554,8 +636,20 @@ class ModernPrivilegedHelperManager: NSObject, ObservableObject {
|
|||||||
|
|
||||||
private func showApprovalGuidance() {
|
private func showApprovalGuidance() {
|
||||||
let alert = NSAlert()
|
let alert = NSAlert()
|
||||||
alert.messageText = String(localized: "需要在系统设置中允许 Helper")
|
alert.messageText = String(localized: "Helper服务需要批准")
|
||||||
alert.informativeText = String(localized: "Adobe Downloader 需要通过后台服务来安装与移动文件。请在\"系统设置 → 通用 → 登录项与扩展\"中允许此应用的后台项目。")
|
alert.informativeText = String(localized: """
|
||||||
|
Adobe Downloader需要后台Helper服务来执行安装和文件操作。
|
||||||
|
|
||||||
|
解决方法:
|
||||||
|
1. 点击下方「打开系统设置」按钮
|
||||||
|
2. 在「登录项与扩展」中找到 Adobe Downloader
|
||||||
|
3. 确保应用已被允许,并检查是否有任何需要启用的后台项目
|
||||||
|
4. 如果看不到相关选项,请尝试:
|
||||||
|
- 重启 Adobe Downloader
|
||||||
|
- 或重启系统后再试
|
||||||
|
|
||||||
|
注意:macOS可能需要重启才能完全激活Helper服务。
|
||||||
|
""")
|
||||||
alert.addButton(withTitle: String(localized: "打开系统设置"))
|
alert.addButton(withTitle: String(localized: "打开系统设置"))
|
||||||
alert.addButton(withTitle: String(localized: "稍后设置"))
|
alert.addButton(withTitle: String(localized: "稍后设置"))
|
||||||
|
|
||||||
|
|||||||
@@ -181,6 +181,8 @@ class PrivilegedHelperAdapter: NSObject, ObservableObject {
|
|||||||
return .disconnected
|
return .disconnected
|
||||||
case .connecting:
|
case .connecting:
|
||||||
return .connecting
|
return .connecting
|
||||||
|
case .needsApproval:
|
||||||
|
return .disconnected
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,10 +9,13 @@
|
|||||||
<key>NSAllowsArbitraryLoads</key>
|
<key>NSAllowsArbitraryLoads</key>
|
||||||
<true/>
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
<key>SMPrivilegedExecutables</key>
|
<key>SMAppService</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>com.x1a0he.macOS.Adobe-Downloader.helper</key>
|
<key>com.x1a0he.macOS.Adobe-Downloader.helper</key>
|
||||||
<string>identifier "com.x1a0he.macOS.Adobe-Downloader.helper" and anchor apple generic and certificate leaf[subject.CN] = "Apple Development: x1a0he@outlook.com (LFN2762T4F)" and certificate 1[field.1.2.840.113635.100.6.2.1] /* exists */</string>
|
<dict>
|
||||||
|
<key>PlistName</key>
|
||||||
|
<string>com.x1a0he.macOS.Adobe-Downloader.helper.plist</string>
|
||||||
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>SUFeedURL</key>
|
<key>SUFeedURL</key>
|
||||||
<string>https://raw.githubusercontent.com/X1a0He/Adobe-Downloader/refs/heads/main/appcast.xml</string>
|
<string>https://raw.githubusercontent.com/X1a0He/Adobe-Downloader/refs/heads/main/appcast.xml</string>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Sparkle
|
import Sparkle
|
||||||
import Combine
|
import Combine
|
||||||
|
import ServiceManagement
|
||||||
|
|
||||||
|
|
||||||
private enum AboutViewConstants {
|
private enum AboutViewConstants {
|
||||||
@@ -267,6 +268,7 @@ final class GeneralSettingsViewModel: ObservableObject {
|
|||||||
case connecting
|
case connecting
|
||||||
case disconnected
|
case disconnected
|
||||||
case checking
|
case checking
|
||||||
|
case needsApproval
|
||||||
}
|
}
|
||||||
|
|
||||||
init(updater: SPUUpdater) {
|
init(updater: SPUUpdater) {
|
||||||
@@ -287,6 +289,8 @@ final class GeneralSettingsViewModel: ObservableObject {
|
|||||||
self?.helperConnectionStatus = .disconnected
|
self?.helperConnectionStatus = .disconnected
|
||||||
case .connecting:
|
case .connecting:
|
||||||
self?.helperConnectionStatus = .connecting
|
self?.helperConnectionStatus = .connecting
|
||||||
|
case .needsApproval:
|
||||||
|
self?.helperConnectionStatus = .needsApproval
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
@@ -966,8 +970,11 @@ struct HelperStatusRow: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Button(action: {
|
Button(action: {
|
||||||
if helperStatus == .installed &&
|
if viewModel.helperConnectionStatus == .needsApproval {
|
||||||
viewModel.helperConnectionStatus != .connected {
|
// 打开系统设置让用户批准Helper
|
||||||
|
SMAppService.openSystemSettingsLoginItems()
|
||||||
|
} else if helperStatus == .installed &&
|
||||||
|
viewModel.helperConnectionStatus != .connected {
|
||||||
Task {
|
Task {
|
||||||
do {
|
do {
|
||||||
try await ModernPrivilegedHelperManager.shared.reconnectHelper()
|
try await ModernPrivilegedHelperManager.shared.reconnectHelper()
|
||||||
@@ -987,9 +994,9 @@ struct HelperStatusRow: View {
|
|||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
HStack(spacing: 4) {
|
HStack(spacing: 4) {
|
||||||
Image(systemName: "network")
|
Image(systemName: viewModel.helperConnectionStatus == .needsApproval ? "gear" : "network")
|
||||||
.font(.system(size: 12))
|
.font(.system(size: 12))
|
||||||
Text("重新连接")
|
Text(viewModel.helperConnectionStatus == .needsApproval ? "打开设置" : "重新连接")
|
||||||
.font(.system(size: 13))
|
.font(.system(size: 13))
|
||||||
}
|
}
|
||||||
.frame(minWidth: 90)
|
.frame(minWidth: 90)
|
||||||
@@ -997,7 +1004,7 @@ struct HelperStatusRow: View {
|
|||||||
.buttonStyle(BeautifulButtonStyle(baseColor: shouldDisableReconnectButton ? Color.gray.opacity(0.6) : Color.blue.opacity(0.8)))
|
.buttonStyle(BeautifulButtonStyle(baseColor: shouldDisableReconnectButton ? Color.gray.opacity(0.6) : Color.blue.opacity(0.8)))
|
||||||
.foregroundColor(shouldDisableReconnectButton ? Color.white.opacity(0.8) : .white)
|
.foregroundColor(shouldDisableReconnectButton ? Color.white.opacity(0.8) : .white)
|
||||||
.disabled(shouldDisableReconnectButton)
|
.disabled(shouldDisableReconnectButton)
|
||||||
.help("尝试重新连接到已安装的 Helper")
|
.help(viewModel.helperConnectionStatus == .needsApproval ? "打开系统设置批准Helper" : "尝试重新连接到已安装的 Helper")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.task {
|
.task {
|
||||||
@@ -1011,13 +1018,13 @@ struct HelperStatusRow: View {
|
|||||||
case .connecting: return .orange
|
case .connecting: return .orange
|
||||||
case .disconnected: return .red
|
case .disconnected: return .red
|
||||||
case .checking: return .orange
|
case .checking: return .orange
|
||||||
|
case .needsApproval: return .yellow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var shouldDisableReconnectButton: Bool {
|
private var shouldDisableReconnectButton: Bool {
|
||||||
return helperStatus != .installed ||
|
// 只在重新安装Helper时禁用按钮,其他情况都允许用户点击
|
||||||
viewModel.helperConnectionStatus == .connected ||
|
return isReinstallingHelper
|
||||||
isReinstallingHelper
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private var helperStatusBackgroundColor: Color {
|
private var helperStatusBackgroundColor: Color {
|
||||||
@@ -1026,6 +1033,7 @@ struct HelperStatusRow: View {
|
|||||||
case .connecting: return Color.orange.opacity(0.1)
|
case .connecting: return Color.orange.opacity(0.1)
|
||||||
case .disconnected: return Color.red.opacity(0.1)
|
case .disconnected: return Color.red.opacity(0.1)
|
||||||
case .checking: return Color.orange.opacity(0.1)
|
case .checking: return Color.orange.opacity(0.1)
|
||||||
|
case .needsApproval: return Color.yellow.opacity(0.1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1035,6 +1043,7 @@ struct HelperStatusRow: View {
|
|||||||
case .connecting: return String(localized: "正在连接")
|
case .connecting: return String(localized: "正在连接")
|
||||||
case .disconnected: return String(localized: "连接断开")
|
case .disconnected: return String(localized: "连接断开")
|
||||||
case .checking: return String(localized: "检查中")
|
case .checking: return String(localized: "检查中")
|
||||||
|
case .needsApproval: return String(localized: "需要批准")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>Label</key>
|
<key>Label</key>
|
||||||
<string>com.x1a0he.macOS.Adobe-Downloader.helper</string>
|
<string>com.x1a0he.macOS.Adobe-Downloader.helper</string>
|
||||||
<key>MachServices</key>
|
<key>MachServices</key>
|
||||||
@@ -19,5 +19,5 @@
|
|||||||
<string>/tmp/com.x1a0he.macOS.Adobe-Downloader.helper.err</string>
|
<string>/tmp/com.x1a0he.macOS.Adobe-Downloader.helper.err</string>
|
||||||
<key>StandardOutPath</key>
|
<key>StandardOutPath</key>
|
||||||
<string>/tmp/com.x1a0he.macOS.Adobe-Downloader.helper.out</string>
|
<string>/tmp/com.x1a0he.macOS.Adobe-Downloader.helper.out</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
Reference in New Issue
Block a user