feat: 调整 Helper

This commit is contained in:
X1a0He
2025-07-30 20:15:04 +08:00
parent 30bfe76fbf
commit 4273d05526
5 changed files with 140 additions and 32 deletions

View File

@@ -39,6 +39,7 @@ class ModernPrivilegedHelperManager: NSObject, ObservableObject {
case connected
case disconnected
case connecting
case needsApproval
var description: String {
switch self {
@@ -48,6 +49,8 @@ class ModernPrivilegedHelperManager: NSObject, ObservableObject {
return String(localized: "未连接")
case .connecting:
return String(localized: "正在连接")
case .needsApproval:
return String(localized: "需要批准")
}
}
}
@@ -111,9 +114,10 @@ class ModernPrivilegedHelperManager: NSObject, ObservableObject {
}
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 }
if self.connectionState == .disconnected && self.shouldAutoReconnect {
if self.connectionState == .disconnected && self.shouldAutoReconnect && !self.isInitializing {
self.logger.info("尝试自动重连Helper...")
Task {
await self.attemptConnection()
}
@@ -122,6 +126,7 @@ class ModernPrivilegedHelperManager: NSObject, ObservableObject {
}
func checkAndInstallHelper() async {
isInitializing = true
logger.info("开始检查 Helper 状态")
let status = await getHelperStatus()
@@ -135,6 +140,7 @@ class ModernPrivilegedHelperManager: NSObject, ObservableObject {
registerHelper()
break
case .needsApproval:
self.connectionState = .needsApproval
showApprovalGuidance()
break
case .requiresUpdate:
@@ -142,8 +148,17 @@ class ModernPrivilegedHelperManager: NSObject, ObservableObject {
break
case .installed:
Task {
await attemptConnection()
let connectionResult = await attemptConnection()
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
case .requiresApproval:
logger.info("Helper需要用户批准")
return .needsApproval
case .notFound:
@@ -179,7 +195,7 @@ class ModernPrivilegedHelperManager: NSObject, ObservableObject {
@unknown default:
logger.warning("未知的 SMAppService 状态: \(status.rawValue)")
return .notInstalled
return .needsApproval
}
}
@@ -199,16 +215,58 @@ class ModernPrivilegedHelperManager: NSObject, ObservableObject {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
Task {
await self.attemptConnection()
await self.tryEnableHelper()
}
}
} catch {
logger.error("Helper 注册失败: \(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() {
registerHelper()
}
@@ -286,17 +344,33 @@ class ModernPrivilegedHelperManager: NSObject, ObservableObject {
var isConnected = false
if let helper = newConnection.remoteObjectProxy as? HelperToolProtocol {
helper.executeCommand(type: .shellCommand, path1: "id -u", path2: "", permissions: 0) { [weak self] result in
if result.contains("0") || result == "0" {
helper.executeCommand(type: .shellCommand, path1: "whoami", path2: "", permissions: 0) { [weak self] result in
let trimmedResult = result.trimmingCharacters(in: .whitespacesAndNewlines)
self?.logger.info("Helper 响应: \(trimmedResult)")
if trimmedResult == "root" {
isConnected = true
DispatchQueue.main.async {
self?.connection = newConnection
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.wait(timeout: .now() + 1.0)
let waitResult = semaphore.wait(timeout: .now() + 8.0)
if waitResult == .timedOut {
logger.warning("Helper 连接超时")
}
}
if !isConnected {
@@ -335,7 +409,7 @@ class ModernPrivilegedHelperManager: NSObject, ObservableObject {
}
func getHelperProxy() throws -> HelperToolProtocol {
if connectionState != .connected {
if connectionState != .connected || connection == nil {
guard let newConnection = connectionQueue.sync(execute: { createConnection() }) else {
throw HelperError.connectionFailed
}
@@ -344,7 +418,9 @@ class ModernPrivilegedHelperManager: NSObject, ObservableObject {
guard let helper = connection?.remoteObjectProxyWithErrorHandler({ [weak self] error in
self?.logger.error("XPC 代理错误: \(error)")
DispatchQueue.main.async {
self?.connectionState = .disconnected
}
}) as? HelperToolProtocol else {
throw HelperError.proxyError
}
@@ -375,10 +451,13 @@ class ModernPrivilegedHelperManager: NSObject, ObservableObject {
}
}
} catch {
connectionState = .disconnected
DispatchQueue.main.async { [weak self] in
self?.connectionState = .disconnected
self?.logger.error("执行命令失败: \(error.localizedDescription)")
completion("Error: \(error.localizedDescription)")
}
}
}
func executeInstallation(_ command: String, progress: @escaping (String) -> Void) async throws {
let helper: HelperToolProtocol = try connectionQueue.sync {
@@ -435,10 +514,13 @@ class ModernPrivilegedHelperManager: NSObject, ObservableObject {
}
private func updateConnectionState(from result: String) {
DispatchQueue.main.async { [weak self] in
if result.starts(with: "Error:") {
connectionState = .disconnected
self?.connectionState = .disconnected
self?.logger.warning("命令执行失败,连接状态设为断开: \(result)")
} else {
connectionState = .connected
self?.connectionState = .connected
}
}
}
@@ -554,8 +636,20 @@ class ModernPrivilegedHelperManager: NSObject, ObservableObject {
private func showApprovalGuidance() {
let alert = NSAlert()
alert.messageText = String(localized: "需要在系统设置中允许 Helper")
alert.informativeText = String(localized: "Adobe Downloader 需要通过后台服务来安装与移动文件。请在\"系统设置 → 通用 → 登录项与扩展\"中允许此应用的后台项目。")
alert.messageText = String(localized: "Helper服务需要批准")
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: "稍后设置"))

View File

@@ -181,6 +181,8 @@ class PrivilegedHelperAdapter: NSObject, ObservableObject {
return .disconnected
case .connecting:
return .connecting
case .needsApproval:
return .disconnected
}
}

View File

@@ -9,10 +9,13 @@
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>SMPrivilegedExecutables</key>
<key>SMAppService</key>
<dict>
<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>
<key>SUFeedURL</key>
<string>https://raw.githubusercontent.com/X1a0He/Adobe-Downloader/refs/heads/main/appcast.xml</string>

View File

@@ -7,6 +7,7 @@
import SwiftUI
import Sparkle
import Combine
import ServiceManagement
private enum AboutViewConstants {
@@ -267,6 +268,7 @@ final class GeneralSettingsViewModel: ObservableObject {
case connecting
case disconnected
case checking
case needsApproval
}
init(updater: SPUUpdater) {
@@ -287,6 +289,8 @@ final class GeneralSettingsViewModel: ObservableObject {
self?.helperConnectionStatus = .disconnected
case .connecting:
self?.helperConnectionStatus = .connecting
case .needsApproval:
self?.helperConnectionStatus = .needsApproval
}
}
.store(in: &cancellables)
@@ -966,7 +970,10 @@ struct HelperStatusRow: View {
Spacer()
Button(action: {
if helperStatus == .installed &&
if viewModel.helperConnectionStatus == .needsApproval {
// Helper
SMAppService.openSystemSettingsLoginItems()
} else if helperStatus == .installed &&
viewModel.helperConnectionStatus != .connected {
Task {
do {
@@ -987,9 +994,9 @@ struct HelperStatusRow: View {
}
}) {
HStack(spacing: 4) {
Image(systemName: "network")
Image(systemName: viewModel.helperConnectionStatus == .needsApproval ? "gear" : "network")
.font(.system(size: 12))
Text("重新连接")
Text(viewModel.helperConnectionStatus == .needsApproval ? "打开设置" : "重新连接")
.font(.system(size: 13))
}
.frame(minWidth: 90)
@@ -997,7 +1004,7 @@ struct HelperStatusRow: View {
.buttonStyle(BeautifulButtonStyle(baseColor: shouldDisableReconnectButton ? Color.gray.opacity(0.6) : Color.blue.opacity(0.8)))
.foregroundColor(shouldDisableReconnectButton ? Color.white.opacity(0.8) : .white)
.disabled(shouldDisableReconnectButton)
.help("尝试重新连接到已安装的 Helper")
.help(viewModel.helperConnectionStatus == .needsApproval ? "打开系统设置批准Helper" : "尝试重新连接到已安装的 Helper")
}
}
.task {
@@ -1011,13 +1018,13 @@ struct HelperStatusRow: View {
case .connecting: return .orange
case .disconnected: return .red
case .checking: return .orange
case .needsApproval: return .yellow
}
}
private var shouldDisableReconnectButton: Bool {
return helperStatus != .installed ||
viewModel.helperConnectionStatus == .connected ||
isReinstallingHelper
// Helper
return isReinstallingHelper
}
private var helperStatusBackgroundColor: Color {
@@ -1026,6 +1033,7 @@ struct HelperStatusRow: View {
case .connecting: return Color.orange.opacity(0.1)
case .disconnected: return Color.red.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 .disconnected: return String(localized: "连接断开")
case .checking: return String(localized: "检查中")
case .needsApproval: return String(localized: "需要批准")
}
}
}

View File

@@ -1,7 +1,7 @@
<?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">
<plist version="1.0">
<dict>
<dict>
<key>Label</key>
<string>com.x1a0he.macOS.Adobe-Downloader.helper</string>
<key>MachServices</key>
@@ -19,5 +19,5 @@
<string>/tmp/com.x1a0he.macOS.Adobe-Downloader.helper.err</string>
<key>StandardOutPath</key>
<string>/tmp/com.x1a0he.macOS.Adobe-Downloader.helper.out</string>
</dict>
</dict>
</plist>