解决登陆问题
This commit is contained in:
parent
616ba21662
commit
27d0d11508
@ -1,120 +0,0 @@
|
|||||||
//
|
|
||||||
// SDLApi.swift
|
|
||||||
// sdlan
|
|
||||||
//
|
|
||||||
// Created by 安礼成 on 2024/6/5.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
struct JSONRPCResponse<T: Decodable>: Decodable {
|
|
||||||
let result: T?
|
|
||||||
let error: JSONRPCError?
|
|
||||||
}
|
|
||||||
|
|
||||||
struct JSONRPCError: Error, Decodable {
|
|
||||||
let code: Int
|
|
||||||
let message: String
|
|
||||||
let data: String?
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SDLAPI {
|
|
||||||
|
|
||||||
enum Mode {
|
|
||||||
case debug
|
|
||||||
case prod
|
|
||||||
}
|
|
||||||
|
|
||||||
static let mode: Mode = .debug
|
|
||||||
|
|
||||||
static var baseUrl: String {
|
|
||||||
switch mode {
|
|
||||||
case .debug:
|
|
||||||
return "http://127.0.0.1:18082/test"
|
|
||||||
case .prod:
|
|
||||||
return "https://punchnet.s5s8.com/api"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Upgrade: Decodable {
|
|
||||||
let upgrade_type: Int
|
|
||||||
let upgrade_prompt: String
|
|
||||||
let upgrade_address: String
|
|
||||||
}
|
|
||||||
|
|
||||||
struct NetworkProfile: Decodable {
|
|
||||||
struct NetworkItem: Decodable {
|
|
||||||
let name: String
|
|
||||||
let code: String
|
|
||||||
}
|
|
||||||
let network: [NetworkItem]
|
|
||||||
}
|
|
||||||
|
|
||||||
static func checkVersion(clientId: String, version: Int, channel: String) async throws -> JSONRPCResponse<Upgrade> {
|
|
||||||
let params: [String:Any] = [
|
|
||||||
"client_id": clientId,
|
|
||||||
"version": version,
|
|
||||||
"channel": channel
|
|
||||||
]
|
|
||||||
|
|
||||||
let postData = try! JSONSerialization.data(withJSONObject: params)
|
|
||||||
var request = URLRequest(url: URL(string: baseUrl + "/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<Upgrade>.self, from: data)
|
|
||||||
}
|
|
||||||
|
|
||||||
static func getUserNetworks(clientId: String) async throws -> JSONRPCResponse<NetworkProfile> {
|
|
||||||
let params: [String:Any] = [
|
|
||||||
"client_id": clientId
|
|
||||||
]
|
|
||||||
|
|
||||||
let postData = try! JSONSerialization.data(withJSONObject: params)
|
|
||||||
var request = URLRequest(url: URL(string: baseUrl + "/get_user_network")!)
|
|
||||||
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<NetworkProfile>.self, from: data)
|
|
||||||
}
|
|
||||||
|
|
||||||
static func loginWithAccountAndPassword<T: Decodable>(clientId: String, username: String, password: String, as: T.Type) async throws -> T {
|
|
||||||
let params: [String:Any] = [
|
|
||||||
"client_id": clientId,
|
|
||||||
"username": username,
|
|
||||||
"password": password
|
|
||||||
]
|
|
||||||
|
|
||||||
return try await doPost(path: "/login_with_account", params: params, as: T.self)
|
|
||||||
}
|
|
||||||
|
|
||||||
private static func doPost<T: Decodable>(path: String, params: [String: Any], as: T.Type) async throws -> T {
|
|
||||||
let postData = try! JSONSerialization.data(withJSONObject: params)
|
|
||||||
var request = URLRequest(url: URL(string: baseUrl + path)!)
|
|
||||||
request.httpMethod = "POST"
|
|
||||||
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
||||||
request.httpBody = postData
|
|
||||||
|
|
||||||
let (data, _) = try await URLSession.shared.data(for: request)
|
|
||||||
let rpcResponse = try JSONDecoder().decode(JSONRPCResponse<T>.self, from: data)
|
|
||||||
if let result = rpcResponse.result {
|
|
||||||
return result
|
|
||||||
} else if let error = rpcResponse.error {
|
|
||||||
throw error
|
|
||||||
} else {
|
|
||||||
throw DecodingError.dataCorrupted(
|
|
||||||
.init(
|
|
||||||
codingPath: [],
|
|
||||||
debugDescription: "Invalid JSON-RPC response: \(String(data: data, encoding: .utf8) ?? "")"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
49
punchnet/Core/SDLAPIClient.swift
Normal file
49
punchnet/Core/SDLAPIClient.swift
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
//
|
||||||
|
// SDLApi.swift
|
||||||
|
// sdlan
|
||||||
|
//
|
||||||
|
// Created by 安礼成 on 2024/6/5.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct SDLAPIResponse<T: Decodable>: Decodable {
|
||||||
|
let code: Int
|
||||||
|
let message: String?
|
||||||
|
let data: T?
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SDLAPIError: Error, Decodable {
|
||||||
|
let code: Int
|
||||||
|
let message: String
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SDLAPIClient {
|
||||||
|
|
||||||
|
static var baseUrl: String = "https://punchnet.s5s8.com/api"
|
||||||
|
|
||||||
|
static func doPost<T: Decodable>(path: String, params: [String: Any], as: T.Type) async throws -> T {
|
||||||
|
let postData = try! JSONSerialization.data(withJSONObject: params)
|
||||||
|
var request = URLRequest(url: URL(string: baseUrl + path)!)
|
||||||
|
request.httpMethod = "POST"
|
||||||
|
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||||
|
request.httpBody = postData
|
||||||
|
|
||||||
|
let (data, _) = try await URLSession.shared.data(for: request)
|
||||||
|
let apiResponse = try JSONDecoder().decode(SDLAPIResponse<T>.self, from: data)
|
||||||
|
|
||||||
|
if apiResponse.code == 0, let data = apiResponse.data {
|
||||||
|
return data
|
||||||
|
} else if let message = apiResponse.message {
|
||||||
|
throw SDLAPIError(code: apiResponse.code, message: message)
|
||||||
|
} else {
|
||||||
|
throw DecodingError.dataCorrupted(
|
||||||
|
.init(
|
||||||
|
codingPath: [],
|
||||||
|
debugDescription: "Invalid JSON-RPC response: \(String(data: data, encoding: .utf8) ?? "")"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -75,6 +75,11 @@ struct SystemConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static func macAddressString(mac: Data, separator: String = ":") -> String {
|
||||||
|
return mac.map { String(format: "%02X", $0) }
|
||||||
|
.joined(separator: separator)
|
||||||
|
}
|
||||||
|
|
||||||
// 随机生成mac地址
|
// 随机生成mac地址
|
||||||
private static func generateMacAddress() -> Data {
|
private static func generateMacAddress() -> Data {
|
||||||
var macAddress = [UInt8](repeating: 0, count: 6)
|
var macAddress = [UInt8](repeating: 0, count: 6)
|
||||||
|
|||||||
@ -30,34 +30,34 @@ struct AbortView: View {
|
|||||||
Text("Version1.1")
|
Text("Version1.1")
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
Task {
|
// Task {
|
||||||
guard let response = try? await SDLAPI.checkVersion(clientId: "test", version: 1, channel: "macos") else {
|
// guard let response = try? await SDLAPI.checkVersion(clientId: "test", version: 1, channel: "macos") else {
|
||||||
DispatchQueue.main.async {
|
// DispatchQueue.main.async {
|
||||||
self.alertShow = AlertShow(id: "network_error", content: .error("Network Error"))
|
// self.alertShow = AlertShow(id: "network_error", content: .error("Network Error"))
|
||||||
}
|
// }
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
if let result = response.result {
|
// if let result = response.result {
|
||||||
if result.upgrade_type == 0 {
|
// if result.upgrade_type == 0 {
|
||||||
DispatchQueue.main.async {
|
// DispatchQueue.main.async {
|
||||||
self.alertShow = AlertShow(id: "upgrade_0", content: .upgrade(result.upgrade_prompt, ""))
|
// self.alertShow = AlertShow(id: "upgrade_0", content: .upgrade(result.upgrade_prompt, ""))
|
||||||
}
|
// }
|
||||||
} else if result.upgrade_type == 1 {
|
// } else if result.upgrade_type == 1 {
|
||||||
DispatchQueue.main.async {
|
// DispatchQueue.main.async {
|
||||||
self.alertShow = AlertShow(id: "upgrade_1", content: .upgrade(result.upgrade_prompt, result.upgrade_address))
|
// self.alertShow = AlertShow(id: "upgrade_1", content: .upgrade(result.upgrade_prompt, result.upgrade_address))
|
||||||
}
|
// }
|
||||||
} else if result.upgrade_type == 2 {
|
// } else if result.upgrade_type == 2 {
|
||||||
DispatchQueue.main.async {
|
// DispatchQueue.main.async {
|
||||||
self.alertShow = AlertShow(id: "upgrade_1", content: .upgrade(result.upgrade_prompt, result.upgrade_address))
|
// self.alertShow = AlertShow(id: "upgrade_1", content: .upgrade(result.upgrade_prompt, result.upgrade_address))
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
} else if let error = response.error {
|
// } else if let error = response.error {
|
||||||
DispatchQueue.main.async {
|
// DispatchQueue.main.async {
|
||||||
self.alertShow = AlertShow(id: "response_error", content: .error(error.message))
|
// self.alertShow = AlertShow(id: "response_error", content: .error(error.message))
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
} label: {
|
} label: {
|
||||||
Text("版本检测")
|
Text("版本检测")
|
||||||
|
|||||||
@ -1,303 +0,0 @@
|
|||||||
//
|
|
||||||
// ContentView.swift
|
|
||||||
// sdlan
|
|
||||||
//
|
|
||||||
// Created by 安礼成 on 2024/1/17.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
import SwiftData
|
|
||||||
import Combine
|
|
||||||
|
|
||||||
struct IndexView: View {
|
|
||||||
@AppStorage("token") private var token: String = ""
|
|
||||||
@AppStorage("hostname") private var hostname: String = ""
|
|
||||||
@AppStorage("network_code") private var networkCode: String = ""
|
|
||||||
|
|
||||||
@State private var showToken: Bool = false
|
|
||||||
|
|
||||||
@ObservedObject private var vpnManager = VPNManager.shared
|
|
||||||
@State private var showAlert = false
|
|
||||||
@State private var showStunAlert = false
|
|
||||||
@State private var message: NoticeMessage.InboundMessage = .none
|
|
||||||
@State private var cancel: AnyCancellable?
|
|
||||||
|
|
||||||
@State private var showMenu: Bool = false
|
|
||||||
|
|
||||||
@State private var networkProfile: SDLAPI.NetworkProfile = .init(network: [])
|
|
||||||
@State private var selectedIdx: Int = 0
|
|
||||||
|
|
||||||
// 显示ip信息
|
|
||||||
@State private var showIpAdress: Bool = false
|
|
||||||
@State private var ipAddress: String = ""
|
|
||||||
|
|
||||||
public var noticeServer: UDPNoticeCenterServer
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
|
|
||||||
VStack(alignment: .center, spacing: 10) {
|
|
||||||
VStack(alignment: .center, spacing: 10) {
|
|
||||||
Spacer()
|
|
||||||
.frame(height: 100)
|
|
||||||
|
|
||||||
Image("logo")
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 150, height: 150)
|
|
||||||
|
|
||||||
Text("Connecting the Infinite")
|
|
||||||
.font(.system(size: 24, weight: .bold))
|
|
||||||
.foregroundColor(.white)
|
|
||||||
.cornerRadius(5.0)
|
|
||||||
|
|
||||||
Text("Welcome to PunchNet")
|
|
||||||
.font(.system(size: 14, weight: .regular))
|
|
||||||
.foregroundColor(.white)
|
|
||||||
.cornerRadius(5.0)
|
|
||||||
}
|
|
||||||
.contentShape(Rectangle())
|
|
||||||
.onTapGesture {
|
|
||||||
self.showMenu = false
|
|
||||||
}
|
|
||||||
TextField("主机名", text: $hostname)
|
|
||||||
.multilineTextAlignment(.leading)
|
|
||||||
.textFieldStyle(PlainTextFieldStyle())
|
|
||||||
.frame(width: 200, height: 25)
|
|
||||||
.background(Color.white)
|
|
||||||
.foregroundColor(Color.black)
|
|
||||||
.cornerRadius(5.0)
|
|
||||||
|
|
||||||
if showIpAdress {
|
|
||||||
HStack {
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Text("ip: ")
|
|
||||||
.font(.system(size: 16, weight: .medium))
|
|
||||||
.foregroundColor(.white)
|
|
||||||
.cornerRadius(5.0)
|
|
||||||
|
|
||||||
Text(ipAddress)
|
|
||||||
.font(.system(size: 16, weight: .medium))
|
|
||||||
.foregroundColor(.white)
|
|
||||||
.cornerRadius(5.0)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
.frame(width: 1, height: 10)
|
|
||||||
|
|
||||||
VStack(spacing: 0) {
|
|
||||||
ForEach(Array(networkProfile.network.enumerated()), id: \.offset) { idx, network in
|
|
||||||
NetworkItemView(idx: idx, item: network)
|
|
||||||
.padding(.horizontal, 8)
|
|
||||||
.padding(.vertical, 6)
|
|
||||||
.background(
|
|
||||||
RoundedRectangle(cornerRadius: 8)
|
|
||||||
.fill(selectedIdx == idx ? Color.blue.opacity(0.3) : Color.clear)
|
|
||||||
)
|
|
||||||
.contentShape(Rectangle())
|
|
||||||
.onTapGesture {
|
|
||||||
withAnimation(.easeInOut(duration: 0.2)) {
|
|
||||||
selectedIdx = idx
|
|
||||||
self.networkCode = network.code
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TextField("邀请码", text: $token)
|
|
||||||
.multilineTextAlignment(.leading)
|
|
||||||
.textFieldStyle(PlainTextFieldStyle())
|
|
||||||
.frame(width: 200, height: 25)
|
|
||||||
.background(Color.white)
|
|
||||||
.foregroundColor(Color.black)
|
|
||||||
.cornerRadius(5.0)
|
|
||||||
.opacity(showToken ? 1 : 0)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
.frame(width: 1, height: 10)
|
|
||||||
|
|
||||||
Rectangle()
|
|
||||||
.overlay {
|
|
||||||
Text(vpnManager.title)
|
|
||||||
.font(.system(size: 14, weight: .regular))
|
|
||||||
.foregroundColor(vpnManager.color)
|
|
||||||
}
|
|
||||||
.frame(width: 120, height: 35)
|
|
||||||
.foregroundColor(Color(red: 74 / 255, green: 207 / 255, blue: 154 / 255))
|
|
||||||
.cornerRadius(5.0)
|
|
||||||
.onTapGesture {
|
|
||||||
Task {
|
|
||||||
do {
|
|
||||||
try await self.clickSwitchButton()
|
|
||||||
} catch let err {
|
|
||||||
NSLog("start vpn get error: \(err)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.overlay(alignment: .top) {
|
|
||||||
|
|
||||||
HStack(spacing: 200) {
|
|
||||||
HStack {
|
|
||||||
Button(action: {
|
|
||||||
NSApplication.shared.terminate(nil)
|
|
||||||
}) {
|
|
||||||
|
|
||||||
Image("close")
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 15, height: 15)
|
|
||||||
}
|
|
||||||
.buttonStyle(PlainButtonStyle())
|
|
||||||
|
|
||||||
Button(action: {
|
|
||||||
NSApplication.shared.keyWindow?.miniaturize(nil)
|
|
||||||
}) {
|
|
||||||
Image("line")
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 15, height: 15)
|
|
||||||
}
|
|
||||||
.buttonStyle(PlainButtonStyle())
|
|
||||||
}
|
|
||||||
|
|
||||||
Button(action: {
|
|
||||||
showMenu.toggle()
|
|
||||||
}) {
|
|
||||||
Image("IosSettings")
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 20, height: 20)
|
|
||||||
}
|
|
||||||
.buttonStyle(PlainButtonStyle())
|
|
||||||
.overlay(alignment: .leading) {
|
|
||||||
showMenu ?
|
|
||||||
|
|
||||||
GeometryReader { geometry in
|
|
||||||
|
|
||||||
VStack(alignment: .leading, spacing: 8) {
|
|
||||||
Button(action: {
|
|
||||||
self.showMenu = false
|
|
||||||
}) {
|
|
||||||
Text("主页")
|
|
||||||
.font(.system(size: 14))
|
|
||||||
.foregroundColor(.white)
|
|
||||||
}
|
|
||||||
.buttonStyle(PlainButtonStyle())
|
|
||||||
|
|
||||||
Button(action: {
|
|
||||||
self.showToken.toggle()
|
|
||||||
}) {
|
|
||||||
Text("邀请码")
|
|
||||||
.font(.system(size: 14))
|
|
||||||
.foregroundColor(.white)
|
|
||||||
}
|
|
||||||
.buttonStyle(PlainButtonStyle())
|
|
||||||
|
|
||||||
Button(action: {
|
|
||||||
NSApplication.shared.terminate(nil)
|
|
||||||
}) {
|
|
||||||
Text("退出")
|
|
||||||
.font(.system(size: 14))
|
|
||||||
.foregroundColor(.white)
|
|
||||||
}
|
|
||||||
.buttonStyle(PlainButtonStyle())
|
|
||||||
}
|
|
||||||
.frame(width: 90, height: 80)
|
|
||||||
.background(Color(red: 50 / 255, green: 55 / 255, blue: 52 / 255))
|
|
||||||
.offset(x: -55, y: 20)
|
|
||||||
}
|
|
||||||
|
|
||||||
: nil
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
.offset(x: 0, y: 10)
|
|
||||||
}
|
|
||||||
.padding([.leading, .trailing, .top], 10)
|
|
||||||
.padding([.bottom], 20)
|
|
||||||
.background(Color(red: 36 / 255, green: 38 / 255, blue: 51 / 255))
|
|
||||||
.frame(width: 320)
|
|
||||||
.alert(isPresented: $showAlert) {
|
|
||||||
Alert(title: Text("请输入正确的邀请码"))
|
|
||||||
}
|
|
||||||
.alert(isPresented: $showStunAlert) {
|
|
||||||
switch self.message {
|
|
||||||
case .alertMessage(let alert):
|
|
||||||
Alert(title: Text(alert))
|
|
||||||
default:
|
|
||||||
Alert(title: Text(""))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.task {
|
|
||||||
do {
|
|
||||||
let response = try await SDLAPI.getUserNetworks(clientId: SystemConfig.getClientId())
|
|
||||||
print("get user networks: \(response)")
|
|
||||||
if let result = response.result {
|
|
||||||
self.networkProfile = result
|
|
||||||
if self.networkProfile.network.count > 0 {
|
|
||||||
self.networkCode = self.networkProfile.network[0].code
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch let err {
|
|
||||||
NSLog("get user networks get error: \(err)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onAppear {
|
|
||||||
self.cancel = self.noticeServer.messageFlow.sink{ message in
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
switch message {
|
|
||||||
case .none:
|
|
||||||
()
|
|
||||||
default:
|
|
||||||
self.message = message
|
|
||||||
self.showStunAlert = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func clickSwitchButton() async throws {
|
|
||||||
switch self.vpnManager.vpnStatus {
|
|
||||||
case .connected:
|
|
||||||
self.showIpAdress = false
|
|
||||||
self.ipAddress = ""
|
|
||||||
try await vpnManager.disableVpn()
|
|
||||||
case .disconnected:
|
|
||||||
// let clientId = SystemConfig.getClientId()
|
|
||||||
// NSLog("[IndexView] use token: \(self.token), network_code: \(networkCode)")
|
|
||||||
// // token存在则优先使用token
|
|
||||||
// try await vpnManager.enableVpn(options: SystemConfig.getOptions(networkCode: self.networkCode, token: self.token, clientId: clientId, hostname: self.hostname, noticePort: self.noticeServer.port)!)
|
|
||||||
()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension IndexView {
|
|
||||||
struct NetworkItemView: View {
|
|
||||||
let idx: Int
|
|
||||||
let item: SDLAPI.NetworkProfile.NetworkItem
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
HStack {
|
|
||||||
Text(item.name)
|
|
||||||
.font(.system(size: 14))
|
|
||||||
.foregroundColor(.white)
|
|
||||||
.frame(width: 80, alignment: .leading)
|
|
||||||
|
|
||||||
Text(item.code)
|
|
||||||
.font(.system(size: 14))
|
|
||||||
.foregroundColor(.white)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#Preview {
|
|
||||||
let server = UDPNoticeCenterServer()
|
|
||||||
IndexView(noticeServer: server)
|
|
||||||
//.modelContainer(for: Item.self, inMemory: true)
|
|
||||||
}
|
|
||||||
@ -12,10 +12,74 @@ import Observation
|
|||||||
class UserContext {
|
class UserContext {
|
||||||
var isLogined: Bool = false
|
var isLogined: Bool = false
|
||||||
var loginCredit: LoginCredit?
|
var loginCredit: LoginCredit?
|
||||||
|
var networkSession: NetworkSession?
|
||||||
|
|
||||||
enum LoginCredit {
|
enum LoginCredit {
|
||||||
case token(token: String, networkdId: Int)
|
case token(token: String)
|
||||||
case accountAndPasword(account: String, password: String, networkId: Int)
|
case accountAndPasword(account: String, password: String)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 登陆后的网络会话信息
|
||||||
|
struct NetworkSession: Codable {
|
||||||
|
struct ExitNode: Codable {
|
||||||
|
let nnid: Int
|
||||||
|
let nodeName: String
|
||||||
|
|
||||||
|
enum CodingKeys: String, CodingKey {
|
||||||
|
case nnid
|
||||||
|
case nodeName = "node_name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let accessToken: String
|
||||||
|
let username: String
|
||||||
|
let userType: String
|
||||||
|
let audit: Int
|
||||||
|
let networkId: Int
|
||||||
|
let networkName: String
|
||||||
|
let networkDomain: String
|
||||||
|
let exitNodes: [ExitNode]
|
||||||
|
|
||||||
|
enum CodingKeys: String, CodingKey {
|
||||||
|
case accessToken = "access_token"
|
||||||
|
case username
|
||||||
|
case userType = "user_type"
|
||||||
|
case audit
|
||||||
|
case networkId = "network_id"
|
||||||
|
case networkName = "network_name"
|
||||||
|
case networkDomain = "network_domain"
|
||||||
|
case exitNodes = "exit_node"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private let baseParams: [String: Any] = [
|
||||||
|
"client_id": SystemConfig.getClientId(),
|
||||||
|
"mac": SystemConfig.macAddressString(mac: SystemConfig.getMacAddress())
|
||||||
|
]
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
func loginWithAccountAndPassword(username: String, password: String) async throws {
|
||||||
|
var params: [String: Any] = [
|
||||||
|
"username": username,
|
||||||
|
"password": password,
|
||||||
|
]
|
||||||
|
params.merge(baseParams) {$1}
|
||||||
|
|
||||||
|
self.networkSession = try await SDLAPIClient.doPost(path: "/auth/login", params: params, as: NetworkSession.self)
|
||||||
|
self.loginCredit = .accountAndPasword(account: username, password: password)
|
||||||
|
self.isLogined = true
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
func loginWithToken(token: String) async throws {
|
||||||
|
var params: [String: Any] = [
|
||||||
|
"token": token,
|
||||||
|
]
|
||||||
|
params.merge(baseParams) {$1}
|
||||||
|
|
||||||
|
self.networkSession = try await SDLAPIClient.doPost(path: "/auth/token", params: params, as: NetworkSession.self)
|
||||||
|
self.loginCredit = .token(token: token)
|
||||||
|
self.isLogined = true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,9 +10,10 @@ import Observation
|
|||||||
|
|
||||||
// 登陆页面
|
// 登陆页面
|
||||||
struct LoginView: View {
|
struct LoginView: View {
|
||||||
@State private var loginMode: LoginMode = .account
|
@Environment(UserContext.self) var userContext: UserContext
|
||||||
|
@State private var authMethod: AuthMethod = .account
|
||||||
|
|
||||||
enum LoginMode {
|
enum AuthMethod {
|
||||||
case token
|
case token
|
||||||
case account
|
case account
|
||||||
}
|
}
|
||||||
@ -24,7 +25,7 @@ struct LoginView: View {
|
|||||||
|
|
||||||
HStack(alignment: .center, spacing: 30) {
|
HStack(alignment: .center, spacing: 30) {
|
||||||
Button {
|
Button {
|
||||||
self.loginMode = .token
|
self.authMethod = .token
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
Image("logo")
|
Image("logo")
|
||||||
@ -33,14 +34,14 @@ struct LoginView: View {
|
|||||||
.frame(width: 25, height: 25)
|
.frame(width: 25, height: 25)
|
||||||
|
|
||||||
Text("密钥登陆")
|
Text("密钥登陆")
|
||||||
.foregroundColor(self.loginMode == .token ? .blue : .black)
|
.foregroundColor(self.authMethod == .token ? .blue : .black)
|
||||||
}
|
}
|
||||||
.contentShape(Rectangle())
|
.contentShape(Rectangle())
|
||||||
}
|
}
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
self.loginMode = .account
|
self.authMethod = .account
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
Image("logo")
|
Image("logo")
|
||||||
@ -48,7 +49,7 @@ struct LoginView: View {
|
|||||||
.clipped()
|
.clipped()
|
||||||
.frame(width: 25, height: 25)
|
.frame(width: 25, height: 25)
|
||||||
Text("账户登陆")
|
Text("账户登陆")
|
||||||
.foregroundColor(self.loginMode == .account ? .blue : .black)
|
.foregroundColor(self.authMethod == .account ? .blue : .black)
|
||||||
}
|
}
|
||||||
.contentShape(Rectangle())
|
.contentShape(Rectangle())
|
||||||
}
|
}
|
||||||
@ -56,7 +57,7 @@ struct LoginView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Group {
|
Group {
|
||||||
switch loginMode {
|
switch self.authMethod {
|
||||||
case .token:
|
case .token:
|
||||||
LoginTokenView()
|
LoginTokenView()
|
||||||
case .account:
|
case .account:
|
||||||
@ -75,6 +76,9 @@ struct LoginTokenView: View {
|
|||||||
@Environment(UserContext.self) var userContext: UserContext
|
@Environment(UserContext.self) var userContext: UserContext
|
||||||
@State private var token: String = ""
|
@State private var token: String = ""
|
||||||
|
|
||||||
|
@State private var showAlert = false
|
||||||
|
@State private var errorMessage = ""
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
TextField("认证密钥", text: $token)
|
TextField("认证密钥", text: $token)
|
||||||
.multilineTextAlignment(.leading)
|
.multilineTextAlignment(.leading)
|
||||||
@ -99,17 +103,44 @@ struct LoginTokenView: View {
|
|||||||
.foregroundColor(Color(red: 74 / 255, green: 207 / 255, blue: 154 / 255))
|
.foregroundColor(Color(red: 74 / 255, green: 207 / 255, blue: 154 / 255))
|
||||||
.cornerRadius(5.0)
|
.cornerRadius(5.0)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
print("call me here")
|
Task {
|
||||||
|
await self.doLogin()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 执行登陆操作
|
||||||
|
private func doLogin() async {
|
||||||
|
do {
|
||||||
|
try await self.userContext.loginWithToken(token: self.token)
|
||||||
|
print(self.userContext.networkSession?.accessToken)
|
||||||
|
|
||||||
|
// 保存信息到KeychainStore
|
||||||
|
// let store = KeychainStore.shared
|
||||||
|
// if let tokenData = loginResult.accessToken.data(using: .utf8) {
|
||||||
|
// try store.save(tokenData, account: self.username)
|
||||||
|
// }
|
||||||
|
|
||||||
|
} catch let err as SDLAPIError {
|
||||||
|
await MainActor.run {
|
||||||
|
self.showAlert = true
|
||||||
|
self.errorMessage = err.message
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
await MainActor.run {
|
||||||
|
self.showAlert = true
|
||||||
|
self.errorMessage = "内部错误,请稍后重试"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LoginAccountView: View {
|
struct LoginAccountView: View {
|
||||||
@Environment(UserContext.self) var userContext: UserContext
|
@Environment(UserContext.self) var userContext: UserContext
|
||||||
|
|
||||||
@State private var username: String = ""
|
@State private var username: String = "test3"
|
||||||
@State private var password: String = ""
|
@State private var password: String = "111111"
|
||||||
|
|
||||||
@State private var showAlert = false
|
@State private var showAlert = false
|
||||||
@State private var errorMessage = ""
|
@State private var errorMessage = ""
|
||||||
@ -181,34 +212,26 @@ struct LoginAccountView: View {
|
|||||||
|
|
||||||
// 执行登陆操作
|
// 执行登陆操作
|
||||||
private func doLogin() async {
|
private func doLogin() async {
|
||||||
// let clientId = SystemConfig.getClientId()
|
do {
|
||||||
// do {
|
try await self.userContext.loginWithAccountAndPassword(username: self.username, password: self.password)
|
||||||
// let loginResult = try await SDLAPI.loginWithAccountAndPassword(clientId: clientId, username: self.username, password: self.password, as: LoginResult.self)
|
|
||||||
//
|
// 保存信息到KeychainStore
|
||||||
// print(loginResult.accessToken)
|
|
||||||
//
|
|
||||||
// // 保存信息到KeychainStore
|
|
||||||
// let store = KeychainStore.shared
|
// let store = KeychainStore.shared
|
||||||
// if let tokenData = loginResult.accessToken.data(using: .utf8) {
|
// if let tokenData = loginResult.accessToken.data(using: .utf8) {
|
||||||
// try store.save(tokenData, account: self.username)
|
// try store.save(tokenData, account: self.username)
|
||||||
// }
|
// }
|
||||||
//
|
|
||||||
// await MainActor.run {
|
} catch let err as SDLAPIError {
|
||||||
// self.userContext.isLogined = true
|
await MainActor.run {
|
||||||
// self.userContext.loginCredit = .accountAndPasword(account: username, password: password, networkId: loginResult.networkId)
|
self.showAlert = true
|
||||||
// }
|
self.errorMessage = err.message
|
||||||
//
|
}
|
||||||
// } catch let err as JSONRPCError {
|
} catch {
|
||||||
// await MainActor.run {
|
await MainActor.run {
|
||||||
// self.showAlert = true
|
self.showAlert = true
|
||||||
// self.errorMessage = err.message
|
self.errorMessage = "内部错误,请稍后重试"
|
||||||
// }
|
}
|
||||||
// } catch {
|
}
|
||||||
// await MainActor.run {
|
|
||||||
// self.showAlert = true
|
|
||||||
// self.errorMessage = "内部错误,请稍后重试"
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,9 +44,8 @@ struct punchnetApp: App {
|
|||||||
|
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
WindowGroup(id: "mainWindow") {
|
WindowGroup(id: "mainWindow") {
|
||||||
//IndexView(noticeServer: self.noticeServer)
|
RootView()
|
||||||
//RootView()
|
//NetworkDisconnctedView(state: NetworkState())
|
||||||
NetworkDisconnctedView(state: NetworkState())
|
|
||||||
.onAppear {
|
.onAppear {
|
||||||
// 获取主屏幕的尺寸
|
// 获取主屏幕的尺寸
|
||||||
guard let screenFrame = NSScreen.main?.frame else { return }
|
guard let screenFrame = NSScreen.main?.frame else { return }
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user