From 269ee4f524c302a964a3e924e3d00c055a3c4ac5 Mon Sep 17 00:00:00 2001 From: anlicheng <244108715@qq.com> Date: Mon, 12 May 2025 16:27:55 +0800 Subject: [PATCH] init view --- punchnet/AbortView.swift | 90 ++++++++++++++ .../logo.imageset/Contents.json | 21 ++++ .../Assets.xcassets/logo.imageset/logo.jpg | Bin 0 -> 22144 bytes punchnet/ContentView.swift | 104 ++++++++++++++-- punchnet/Core/NoticeMessage.swift | 50 ++++++++ punchnet/Core/SDLAPI.swift | 47 +++++++ punchnet/Core/SystemConfig.swift | 18 +++ punchnet/Core/UDPNoticeCenterServer.swift | 86 +++++++++++++ punchnet/Extension/DataExtension.swift | 35 ++++++ punchnet/VPNManager.swift | 98 +++++++++++++++ punchnet/sdlan.entitlements | 28 +++++ punchnet/sdlanApp.swift | 117 ++++++++++++++++++ 12 files changed, 684 insertions(+), 10 deletions(-) create mode 100644 punchnet/AbortView.swift create mode 100644 punchnet/Assets.xcassets/logo.imageset/Contents.json create mode 100644 punchnet/Assets.xcassets/logo.imageset/logo.jpg create mode 100644 punchnet/Core/NoticeMessage.swift create mode 100644 punchnet/Core/SDLAPI.swift create mode 100644 punchnet/Core/SystemConfig.swift create mode 100644 punchnet/Core/UDPNoticeCenterServer.swift create mode 100644 punchnet/Extension/DataExtension.swift create mode 100644 punchnet/VPNManager.swift create mode 100644 punchnet/sdlan.entitlements create mode 100644 punchnet/sdlanApp.swift diff --git a/punchnet/AbortView.swift b/punchnet/AbortView.swift new file mode 100644 index 0000000..7c05c29 --- /dev/null +++ b/punchnet/AbortView.swift @@ -0,0 +1,90 @@ +// +// AbortView.swift +// sdlan +// +// Created by 安礼成 on 2024/6/5. +// + +import Foundation +import SwiftUI + +struct AbortView: View { + + struct AlertShow: Identifiable { + enum ShowContent { + case error(String) + case upgrade(String, String) + } + + var id: String + var content: ShowContent + } + + @State private var alertShow: AlertShow? + + var body: some View { + VStack { + Image("logo") + + Text("sdlan") + + Text("Version1.1") + + Button { + Task { + guard let response = try? await SDLAPI.checkVersion(clientId: "test", version: 1, channel: "macos") else { + DispatchQueue.main.async { + self.alertShow = AlertShow(id: "network_error", content: .error("Network Error")) + } + return + } + + if let result = response.result { + if result.upgrade_type == 0 { + DispatchQueue.main.async { + self.alertShow = AlertShow(id: "upgrade_0", content: .upgrade(result.upgrade_prompt, "")) + } + } else if result.upgrade_type == 1 { + DispatchQueue.main.async { + self.alertShow = AlertShow(id: "upgrade_1", content: .upgrade(result.upgrade_prompt, result.upgrade_address)) + } + } else if result.upgrade_type == 2 { + DispatchQueue.main.async { + self.alertShow = AlertShow(id: "upgrade_1", content: .upgrade(result.upgrade_prompt, result.upgrade_address)) + } + } + } else if let error = response.error { + DispatchQueue.main.async { + self.alertShow = AlertShow(id: "response_error", content: .error(error.message)) + } + } + } + + } label: { + Text("版本检测") + .font(.system(size: 16, weight: .regular)) + .foregroundColor(.white) + .cornerRadius(5.0) + } + .frame(width: 138, height: 33) + .buttonStyle(PlainButtonStyle()) + .background(Color(red: 74 / 255, green: 207 / 255, blue: 154 / 255)) + .cornerRadius(5.0) + + } + .alert(item: $alertShow) { show in + switch show.content { + case .error(let errorMessage): + Alert(title: Text("错误提示"), message: Text(errorMessage)) + case .upgrade(let prompt, let address): + Alert(title: Text("版本升级"), message: Text(prompt), primaryButton: .default(Text("升级版本"), action: { + if let url = URL(string: address) { + // schema: "macappstore://apps.apple.com/app/idYOUR_APP_ID" + NSWorkspace.shared.open(url) + } + }), secondaryButton: .cancel()) + } + } + } + +} diff --git a/punchnet/Assets.xcassets/logo.imageset/Contents.json b/punchnet/Assets.xcassets/logo.imageset/Contents.json new file mode 100644 index 0000000..769bacf --- /dev/null +++ b/punchnet/Assets.xcassets/logo.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "logo.jpg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/punchnet/Assets.xcassets/logo.imageset/logo.jpg b/punchnet/Assets.xcassets/logo.imageset/logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ce597f576666282e1d0f24a8ea1df7ed75c3b039 GIT binary patch literal 22144 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: Decodable { + let result: T? + let error: JSONRPCError? +} + +struct JSONRPCError: Decodable { + let code: Int + let message: String + let data: String? +} + +struct SDLAPI { + + struct Upgrade: Decodable { + let upgrade_type: Int + let upgrade_prompt: String + let upgrade_address: String + } + + static func checkVersion(clientId: String, version: Int, channel: String) async throws -> JSONRPCResponse { + let params: [String:Any] = [ + "client_id": clientId, + "version": version, + "channel": channel + ] + + let postData = try! JSONSerialization.data(withJSONObject: params) + var request = URLRequest(url: URL(string: "http://127.0.0.1:18082/test/upgrade")!) + request.httpMethod = "POST" + request.addValue("application/json", forHTTPHeaderField: "Content-Type") + request.httpBody = postData + + let (data, _) = try await URLSession.shared.data(for: request) + + return try JSONDecoder().decode(JSONRPCResponse.self, from: data) + } + +} diff --git a/punchnet/Core/SystemConfig.swift b/punchnet/Core/SystemConfig.swift new file mode 100644 index 0000000..9f00db5 --- /dev/null +++ b/punchnet/Core/SystemConfig.swift @@ -0,0 +1,18 @@ +// +// SystemConfig.swift +// sdlan +// +// Created by 安礼成 on 2024/6/3. +// + +import Foundation + +struct SystemConfig { + // 版本设置 + static let version = 1 + + static let version_name = "1.1" + + // 安装渠道 + static let installedChannel = "MacAppStore" +} diff --git a/punchnet/Core/UDPNoticeCenterServer.swift b/punchnet/Core/UDPNoticeCenterServer.swift new file mode 100644 index 0000000..e0f3bf0 --- /dev/null +++ b/punchnet/Core/UDPNoticeCenterServer.swift @@ -0,0 +1,86 @@ +// +// UDPMessageCenterServer.swift +// sdlan +// +// Created by 安礼成 on 2024/5/20. +// + +import Foundation +import NIOCore +import NIOPosix +import Combine + +final class UDPNoticeCenterServer: ChannelInboundHandler { + public typealias InboundIn = AddressedEnvelope + public typealias OutboundOut = AddressedEnvelope + + private var group: MultiThreadedEventLoopGroup? + private var thread: Thread? + + var messageFlow = PassthroughSubject() + static let shared = UDPNoticeCenterServer() + + private init() { + + } + + func start() { + self.thread = Thread { + self.group = MultiThreadedEventLoopGroup(numberOfThreads: 1) + let bootstrap = DatagramBootstrap(group: self.group!) + .channelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1) + .channelInitializer { channel in + channel.pipeline.addHandler(self) + } + + let channel = try! bootstrap.bind(host: "127.0.0.1", port: 50195).wait() + try! channel.closeFuture.wait() + } + self.thread?.start() + } + + func stop() { + self.thread?.cancel() + try? self.group?.syncShutdownGracefully() + } + + // --MARK: ChannelInboundHandler + + public func channelRead(context: ChannelHandlerContext, data: NIOAny) { + let envelope = self.unwrapInboundIn(data) + var buffer = envelope.data + guard let type = buffer.readInteger(as: UInt8.self), + let noticeType = NoticeMessage.NoticeType(rawValue: type), + let bytes = buffer.readBytes(length: buffer.readableBytes) else { + return + } + + switch noticeType { + case .upgrade: + if let upgradeMessage = try? JSONDecoder().decode(NoticeMessage.UpgradeMessage.self, from: Data(bytes)) { + DispatchQueue.main.async { + self.messageFlow.send(.upgradeMessage(upgradeMessage)) + } + } + case .alert: + if let alertMessage = try? JSONDecoder().decode(NoticeMessage.AlertMessage.self, from: Data(bytes)) { + DispatchQueue.main.async { + self.messageFlow.send(.alertMessage(alertMessage)) + } + } + } + } + + public func channelReadComplete(context: ChannelHandlerContext) { + // As we are not really interested getting notified on success or failure we just pass nil as promise to + // reduce allocations. + context.flush() + } + + public func errorCaught(context: ChannelHandlerContext, error: Error) { + // As we are not really interested getting notified on success or failure we just pass nil as promise to + // reduce allocations. + context.close(promise: nil) + } + +} diff --git a/punchnet/Extension/DataExtension.swift b/punchnet/Extension/DataExtension.swift new file mode 100644 index 0000000..f5fafcc --- /dev/null +++ b/punchnet/Extension/DataExtension.swift @@ -0,0 +1,35 @@ +// +// DataExtension.swift +// sdlan +// +// Created by 安礼成 on 2024/2/1. +// + +import Foundation + +extension Data { + + mutating public func append(int32val: Int32) { + self.append(contentsOf: [ + (UInt8) (int32val >> 24 & 0xFF), + (UInt8) (int32val >> 16 & 0xFF), + (UInt8) (int32val >> 8 & 0xFF), + (UInt8) (int32val & 0xFF) + ]) + } + + mutating public func append(int16val: Int16) { + self.append(contentsOf: [ + (UInt8) (int16val >> 8 & 0xFF), + (UInt8) (int16val & 0xFF) + ]) + } + + mutating public func append(str: String) { + if let data = str.data(using: .utf8) { + self.append(contentsOf: data) + } + } + +} + diff --git a/punchnet/VPNManager.swift b/punchnet/VPNManager.swift new file mode 100644 index 0000000..0108cf3 --- /dev/null +++ b/punchnet/VPNManager.swift @@ -0,0 +1,98 @@ +// +// VPNManager.swift +// sdlan +// +// Created by 安礼成 on 2024/1/17. +// + +import Foundation +import NetworkExtension +import SwiftUI + +// vpn管理类 +class VPNManager: ObservableObject { + static let shared = VPNManager() + + @Published var vpnStatus: VPNStatus = .disconnected + @Published var title: String = "启动" + @Published var color: Color = .white + + enum VPNStatus { + case connected + case disconnected + } + + private init() { + + } + + // 开启vpn + func enableVpn(options: [String : NSObject]? = nil) async throws { + let manager = try await loadAndCreateProviderManager() + try await manager.loadFromPreferences() + self.addVPNStatusObserver(manager) + + try manager.connection.startVPNTunnel(options: options) + } + + // 关闭vpn + func disableVpn() async throws { + let managers = try await NETunnelProviderManager.loadAllFromPreferences() + managers.first?.connection.stopVPNTunnel() + } + + // MARK: - Private Methods + + // 监控系统VPN的状态的变化 + private func addVPNStatusObserver(_ manager: NETunnelProviderManager) { + NotificationCenter.default.removeObserver(self) + + NotificationCenter.default.addObserver(forName: .NEVPNStatusDidChange, object: manager.connection, queue: .main) { [unowned self] (notification) -> Void in + // 更新vpn的状态 + switch manager.connection.status { + case .invalid, .disconnected, .disconnecting: + self.vpnStatus = .disconnected + self.title = "启动" + self.color = .white + case .connecting, .connected, .reasserting: + self.vpnStatus = .connected + self.title = "停止" + self.color = .red + @unknown default: + self.vpnStatus = .disconnected + self.title = "启动" + self.color = .red + } + } + } + + // MARK: - Private Methods + + // 加载或者创建相关的配置 + private func loadAndCreateProviderManager() async throws -> NETunnelProviderManager { + let managers = try await NETunnelProviderManager.loadAllFromPreferences() + + let manager = managers.first ?? NETunnelProviderManager() + manager.localizedDescription = "sdlan" + manager.isEnabled = true + + // 设置相关参数,在PacketTunnel中可以用 + let protocolConfiguration = NETunnelProviderProtocol() + protocolConfiguration.serverAddress = "sdlan" + protocolConfiguration.providerConfiguration = [String:AnyObject]() + protocolConfiguration.providerBundleIdentifier = "com.jihe.sdlan.Tun" + manager.protocolConfiguration = protocolConfiguration + manager.isOnDemandEnabled = false + + manager.isEnabled = true + + try await manager.saveToPreferences() + + return manager + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + +} diff --git a/punchnet/sdlan.entitlements b/punchnet/sdlan.entitlements new file mode 100644 index 0000000..3e702e4 --- /dev/null +++ b/punchnet/sdlan.entitlements @@ -0,0 +1,28 @@ + + + + + com.apple.developer.networking.networkextension + + packet-tunnel-provider + + com.apple.developer.networking.vpn.api + + allow-vpn + + com.apple.developer.system-extension.install + + com.apple.security.app-sandbox + + com.apple.security.application-groups + + $(TeamIdentifierPrefix) + + com.apple.security.files.user-selected.read-only + + com.apple.security.network.client + + com.apple.security.network.server + + + diff --git a/punchnet/sdlanApp.swift b/punchnet/sdlanApp.swift new file mode 100644 index 0000000..66902da --- /dev/null +++ b/punchnet/sdlanApp.swift @@ -0,0 +1,117 @@ +// +// sdlanApp.swift +// sdlan +// +// Created by 安礼成 on 2024/1/17. +// + +import SwiftUI +import AppKit +import SwiftData +import Combine + +@main +struct sdlanApp: App { + /* + var sharedModelContainer: ModelContainer = { + let schema = Schema([ + Item.self, + ]) + let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false) + + do { + return try ModelContainer(for: schema, configurations: [modelConfiguration]) + } catch { + fatalError("Could not create ModelContainer: \(error)") + } + }() + */ + + @Environment(\.openWindow) private var openWindow + @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate + + @AppStorage("token") var token: String = "" + @ObservedObject var vpnManager = VPNManager.shared + + var body: some Scene { + WindowGroup(id: "mainWindow") { + ContentView() + } + .commands { + CommandGroup(replacing: .appInfo) { + Button { + openWindow(id: "abortSDLAN") + } label: { + Text("About sdlan") + } + } + } + + Window("", id: "abortSDLAN") { + AbortView() + } + + //.modelContainer(sharedModelContainer) + MenuBarExtra("sdlanApp", systemImage: "hammer") { + VStack { + Button(action: { + self.menuClick() + }, label: { + Text(vpnManager.title) + }) + + Divider() + + Button(action: { + NSApplication.shared.terminate(nil) + }, label: { Text("退出") }) + + } + } + .menuBarExtraStyle(.menu) + } + + private func menuClick() { + switch self.vpnManager.vpnStatus { + case .disconnected: + if token.isEmpty { + let windows = NSApplication.shared.windows + if let window = windows.first(where: {$0.title == "sdlan"}) { + window.level = .floating + } else { + self.openWindow(id: "mainWindow") + } + } else { + Task { + try await vpnManager.enableVpn(options: ["token": token as NSObject]) + } + } + case .connected: + Task { + try await vpnManager.disableVpn() + } + } + } + +} + +// 处理APP的生命周期 +class AppDelegate: NSObject, NSApplicationDelegate { + + func applicationWillFinishLaunching(_ notification: Notification) { + UDPNoticeCenterServer.shared.start() + } + + func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply { + Task { + try await VPNManager.shared.disableVpn() + DispatchQueue.main.async { + sender.reply(toApplicationShouldTerminate: true) + } + UDPNoticeCenterServer.shared.stop() + } + + return .terminateLater + } + +}