fix
This commit is contained in:
parent
b01e1ba039
commit
c8b2218841
141
Tun/Punchnet/TunMessage.pb.swift
Normal file
141
Tun/Punchnet/TunMessage.pb.swift
Normal file
@ -0,0 +1,141 @@
|
||||
// DO NOT EDIT.
|
||||
// swift-format-ignore-file
|
||||
// swiftlint:disable all
|
||||
//
|
||||
// Generated by the Swift generator plugin for the protocol buffer compiler.
|
||||
// Source: tun_pb.proto
|
||||
//
|
||||
// For information on using the generated types, please see the documentation:
|
||||
// https://github.com/apple/swift-protobuf/
|
||||
|
||||
import SwiftProtobuf
|
||||
|
||||
// If the compiler emits an error on this type, it is because this file
|
||||
// was generated by a version of the `protoc` Swift plug-in that is
|
||||
// incompatible with the version of SwiftProtobuf to which you are linking.
|
||||
// Please ensure that you are building against the same version of the API
|
||||
// that was used to generate this file.
|
||||
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
|
||||
typealias Version = _2
|
||||
}
|
||||
|
||||
struct NEMessage: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
||||
var message: NEMessage.OneOf_Message? = nil
|
||||
|
||||
var exitNodeIpChanged: NEMessage.ExitNodeIpChanged {
|
||||
get {
|
||||
if case .exitNodeIpChanged(let v)? = message {return v}
|
||||
return NEMessage.ExitNodeIpChanged()
|
||||
}
|
||||
set {message = .exitNodeIpChanged(newValue)}
|
||||
}
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
enum OneOf_Message: Equatable, Sendable {
|
||||
case exitNodeIpChanged(NEMessage.ExitNodeIpChanged)
|
||||
|
||||
}
|
||||
|
||||
/// 网络出口ip改变映射变化, 空字符串表示关闭
|
||||
struct ExitNodeIpChanged: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
||||
var ip: String = String()
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
extension NEMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = "NEMessage"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .standard(proto: "exit_node_ip_changed"),
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
// The use of inline closures is to circumvent an issue where the compiler
|
||||
// allocates stack space for every case branch when no optimizations are
|
||||
// enabled. https://github.com/apple/swift-protobuf/issues/1034
|
||||
switch fieldNumber {
|
||||
case 1: try {
|
||||
var v: NEMessage.ExitNodeIpChanged?
|
||||
var hadOneofValue = false
|
||||
if let current = self.message {
|
||||
hadOneofValue = true
|
||||
if case .exitNodeIpChanged(let m) = current {v = m}
|
||||
}
|
||||
try decoder.decodeSingularMessageField(value: &v)
|
||||
if let v = v {
|
||||
if hadOneofValue {try decoder.handleConflictingOneOf()}
|
||||
self.message = .exitNodeIpChanged(v)
|
||||
}
|
||||
}()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
// The use of inline closures is to circumvent an issue where the compiler
|
||||
// allocates stack space for every if/case branch local when no optimizations
|
||||
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
|
||||
// https://github.com/apple/swift-protobuf/issues/1182
|
||||
try { if case .exitNodeIpChanged(let v)? = self.message {
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 1)
|
||||
} }()
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
static func ==(lhs: NEMessage, rhs: NEMessage) -> Bool {
|
||||
if lhs.message != rhs.message {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension NEMessage.ExitNodeIpChanged: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = NEMessage.protoMessageName + ".ExitNodeIpChanged"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "ip"),
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
// The use of inline closures is to circumvent an issue where the compiler
|
||||
// allocates stack space for every case branch when no optimizations are
|
||||
// enabled. https://github.com/apple/swift-protobuf/issues/1034
|
||||
switch fieldNumber {
|
||||
case 1: try { try decoder.decodeSingularStringField(value: &self.ip) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
if !self.ip.isEmpty {
|
||||
try visitor.visitSingularStringField(value: self.ip, fieldNumber: 1)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
static func ==(lhs: NEMessage.ExitNodeIpChanged, rhs: NEMessage.ExitNodeIpChanged) -> Bool {
|
||||
if lhs.ip != rhs.ip {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -29,7 +29,15 @@ struct SystemConfig {
|
||||
return "macOS \(version.majorVersion).\(version.minorVersion)"
|
||||
}()
|
||||
|
||||
static func getOptions(networkId: UInt32, networkDomain: String, ip: String, maskLen: UInt8, accessToken: String, identityId: UInt32, hostname: String, noticePort: Int) -> [String: NSObject] {
|
||||
static func getOptions(networkId: UInt32,
|
||||
networkDomain: String,
|
||||
ip: String,
|
||||
maskLen: UInt8,
|
||||
accessToken: String,
|
||||
identityId: UInt32,
|
||||
hostname: String,
|
||||
noticePort: Int,
|
||||
exitNodeIp: String?) -> [String: NSObject] {
|
||||
// guard let serverIp = DNSResolver.resolveAddrInfos(serverHost).first,
|
||||
// let stunAssistIp = DNSResolver.resolveAddrInfos(stunAssistHost).first else {
|
||||
// return nil
|
||||
@ -38,7 +46,7 @@ struct SystemConfig {
|
||||
let clientId = getClientId()
|
||||
let mac = getMacAddress()
|
||||
|
||||
return [
|
||||
var options = [
|
||||
"version": version as NSObject,
|
||||
"client_id": clientId as NSObject,
|
||||
"access_token": accessToken as NSObject,
|
||||
@ -55,6 +63,12 @@ struct SystemConfig {
|
||||
"network_domain": networkDomain as NSObject
|
||||
] as NSObject
|
||||
]
|
||||
|
||||
if let exitNodeIp {
|
||||
options["exit_node_ip"] = exitNodeIp as NSObject
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
public static func getClientId() -> String {
|
||||
|
||||
@ -87,10 +87,6 @@ extension SDLAPIClient {
|
||||
case nodeList = "node_list"
|
||||
}
|
||||
|
||||
static func `default`() -> Self {
|
||||
return .init(ip: "0.0.0.0", maskLen: 24, hostname: "", identityId: 0, resourceList: [], nodeList: [])
|
||||
}
|
||||
|
||||
func getNode(id: Int?) -> Node? {
|
||||
return nodeList.first(where: { $0.id == id })
|
||||
}
|
||||
|
||||
@ -19,9 +19,9 @@ class AppContext {
|
||||
var noticePort: Int
|
||||
|
||||
// 调用 "/connect" 之后的网络信息
|
||||
var networkContext: SDLAPIClient.NetworkContext = .default()
|
||||
var networkContext: SDLAPIClient.NetworkContext? = nil
|
||||
|
||||
// 保存当前登陆vpn使用的配置项
|
||||
// 在menu里面需要使用
|
||||
var vpnOptions: [String: NSObject]? = nil
|
||||
|
||||
// 当前app所处的场景
|
||||
@ -95,7 +95,32 @@ class AppContext {
|
||||
throw AppContextError(message: "网络已经连接")
|
||||
}
|
||||
|
||||
let context = try await SDLAPIClient.connectNetwork(accesToken: session.accessToken)
|
||||
self.networkContext = try await SDLAPIClient.connectNetwork(accesToken: session.accessToken)
|
||||
}
|
||||
|
||||
func changeExitNodeIp(exitNodeIp: String) async throws -> Data {
|
||||
// 避免重复连接
|
||||
guard vpnManager.isConnected else {
|
||||
throw AppContextError(message: "网络未连接")
|
||||
}
|
||||
|
||||
var exitNodeIpChanged = NEMessage.ExitNodeIpChanged()
|
||||
exitNodeIpChanged.ip = exitNodeIp
|
||||
|
||||
var neMessage = NEMessage()
|
||||
neMessage.message = .exitNodeIpChanged(exitNodeIpChanged)
|
||||
|
||||
let message = try neMessage.serializedData()
|
||||
|
||||
return try await self.vpnManager.sendMessage(message)
|
||||
}
|
||||
|
||||
// 启动tun
|
||||
func startTun() async throws {
|
||||
guard let session = self.networkSession, let context = self.networkContext else {
|
||||
return
|
||||
}
|
||||
|
||||
let options = SystemConfig.getOptions(
|
||||
networkId: UInt32(session.networkId),
|
||||
networkDomain: session.networkDomain,
|
||||
@ -104,23 +129,21 @@ class AppContext {
|
||||
accessToken: session.accessToken,
|
||||
identityId: context.identityId,
|
||||
hostname: context.hostname,
|
||||
noticePort: noticePort
|
||||
noticePort: noticePort,
|
||||
exitNodeIp: self.loadExitNodeIp()
|
||||
)
|
||||
|
||||
try await self.vpnManager.enableVpn(options: options)
|
||||
self.networkContext = context
|
||||
self.vpnOptions = options
|
||||
}
|
||||
|
||||
// 断开网络连接
|
||||
func disconnectNetwork() async throws {
|
||||
func stopTun() async throws {
|
||||
try await self.vpnManager.disableVpn()
|
||||
}
|
||||
|
||||
// 退出登陆
|
||||
func logout() async throws {
|
||||
try await self.vpnManager.disableVpn()
|
||||
self.networkContext = .default()
|
||||
self.networkContext = nil
|
||||
self.loginCredit = nil
|
||||
}
|
||||
|
||||
@ -141,4 +164,24 @@ class AppContext {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 处理网络出口数据
|
||||
extension AppContext {
|
||||
|
||||
func loadExitNodeIp() -> String? {
|
||||
if let data = try? KeychainStore.shared.load(account: "exitNodeIp") {
|
||||
return String(data: data, encoding: .utf8)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func saveExitNodeIp(exitNodeIp: String) async throws {
|
||||
// 将数据缓存到keychain
|
||||
if let data = exitNodeIp.data(using: .utf8) {
|
||||
try KeychainStore.shared.save(data, account: "exitNodeIp")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -110,11 +110,14 @@ struct NetworkStatusBar: View {
|
||||
set: { newValue in
|
||||
if newValue {
|
||||
Task {
|
||||
try? await self.appContext.connectNetwork()
|
||||
if self.appContext.networkContext == nil {
|
||||
try? await self.appContext.connectNetwork()
|
||||
}
|
||||
try? await self.appContext.startTun()
|
||||
}
|
||||
} else {
|
||||
Task {
|
||||
try? await self.appContext.disconnectNetwork()
|
||||
try? await self.appContext.stopTun()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -139,7 +142,7 @@ struct NetworkStatusBar: View {
|
||||
Text(networkSession.networkName)
|
||||
.font(.system(size: 12, weight: .semibold))
|
||||
|
||||
Text("局域网IP: \(appContext.networkContext.ip)")
|
||||
Text("局域网IP: \(appContext.networkContext?.ip ?? "0.0.0.0")")
|
||||
.font(.system(size: 10, design: .monospaced))
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
@ -170,7 +173,7 @@ struct NetworkConnectedView: View {
|
||||
GridItem(.flexible(), spacing: 8),
|
||||
GridItem(.flexible(), spacing: 8)
|
||||
], spacing: 10) {
|
||||
ForEach(appContext.networkContext.resourceList, id: \.uuid) { res in
|
||||
ForEach(appContext.networkContext?.resourceList ?? [], id: \.uuid) { res in
|
||||
ResourceItemCard(resource: res)
|
||||
}
|
||||
}
|
||||
@ -266,7 +269,7 @@ struct NetworkDeviceGroupView: View {
|
||||
// 如果你的 WindowStyle 是 .hiddenTitleBar,这个 Padding 非常重要
|
||||
Color.clear.frame(height: 28)
|
||||
|
||||
List(appContext.networkContext.nodeList, id: \.id, selection: $selectedId) { node in
|
||||
List(appContext.networkContext?.nodeList ?? [], id: \.id, selection: $selectedId) { node in
|
||||
NetworkNodeHeadView(node: node)
|
||||
// 技巧:在 HStack 方案中,tag 配合 List 的 selection 依然有效
|
||||
.tag(node.id)
|
||||
@ -281,7 +284,7 @@ struct NetworkDeviceGroupView: View {
|
||||
|
||||
// --- 2. 详情区域 (Detail) ---
|
||||
ZStack {
|
||||
if let selectedNode = appContext.networkContext.getNode(id: selectedId) {
|
||||
if let selectedNode = appContext.networkContext?.getNode(id: selectedId) {
|
||||
NetworkNodeDetailView(node: selectedNode)
|
||||
.transition(.opacity.animation(.easeInOut(duration: 0.2)))
|
||||
} else {
|
||||
@ -298,7 +301,7 @@ struct NetworkDeviceGroupView: View {
|
||||
.ignoresSafeArea() // 真正顶到最上方
|
||||
.onAppear {
|
||||
if selectedId == nil {
|
||||
selectedId = appContext.networkContext.firstNodeId()
|
||||
selectedId = appContext.networkContext?.firstNodeId()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,10 +23,14 @@ struct SettingsDeviceView: View {
|
||||
.background(Color.blue.opacity(0.1))
|
||||
.cornerRadius(12)
|
||||
|
||||
// TODO
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(self.appContext.networkContext.hostname)
|
||||
.font(.title3.bold())
|
||||
if let networkContext = self.appContext.networkContext {
|
||||
Text(networkContext.hostname)
|
||||
.font(.title3.bold())
|
||||
} else {
|
||||
Text("未知")
|
||||
.font(.title3.bold())
|
||||
}
|
||||
|
||||
Text(SystemConfig.systemInfo)
|
||||
.font(.subheadline)
|
||||
@ -38,7 +42,7 @@ struct SettingsDeviceView: View {
|
||||
// MARK: - 详细参数卡片
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
// 设备名称行
|
||||
DevicePropertyRow(title: "设备名称", value: self.appContext.networkContext.hostname) {
|
||||
DevicePropertyRow(title: "设备名称", value: self.appContext.networkContext?.hostname ?? "未知") {
|
||||
Button {
|
||||
// 修改逻辑
|
||||
} label: {
|
||||
@ -55,7 +59,7 @@ struct SettingsDeviceView: View {
|
||||
Divider().padding(.leading, 16)
|
||||
|
||||
// IPv4 行
|
||||
DevicePropertyRow(title: "虚拟 IPv4", value: self.appContext.networkContext.ip) {
|
||||
DevicePropertyRow(title: "虚拟 IPv4", value: self.appContext.networkContext?.ip ?? "0.0.0.0") {
|
||||
Image(systemName: "info.circle")
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user