Files
iKeyMon/Sources/Views/ServerFormView.swift

271 lines
8.5 KiB
Swift
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// EditServerView.swift
// iKeyMon
//
// Created by tracer on 30.03.25.
//
import SwiftUI
struct ServerFormView: View {
enum Mode {
case add
case edit(Server)
}
var mode: Mode
@Binding var servers: [Server]
@Binding var groups: [ServerGroup]
@State private var hostname: String
@State private var apiKey: String
@State private var selectedGroupID: UUID?
@State private var connectionOK: Bool = false
@State private var testingConnection: Bool = false
@State private var connectionError: String = ""
@Environment(\.dismiss) private var dismiss
init(
mode: Mode,
servers: Binding<[Server]>,
groups: Binding<[ServerGroup]>,
dismiss: @escaping () -> Void
) {
self.mode = mode
self._servers = servers
self._groups = groups
switch mode {
case .add:
self._hostname = State(initialValue: "")
self._apiKey = State(initialValue: "")
self._selectedGroupID = State(initialValue: nil)
case .edit(let server):
self._hostname = State(initialValue: server.hostname)
self._apiKey = State(initialValue: KeychainHelper.loadApiKey(for: server.hostname) ?? "")
self._selectedGroupID = State(initialValue: server.groupID)
self._connectionOK = State(initialValue: true)
}
}
var body: some View {
VStack {
Text(modeTitle)
.font(.headline)
TextField("Hostname", text: $hostname)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding(.top)
SecureField("API Key", text: $apiKey)
.textFieldStyle(RoundedBorderTextFieldStyle())
Picker("Group", selection: $selectedGroupID) {
Text("No Group").tag(nil as UUID?)
ForEach(groups) { group in
Text(group.name).tag(Optional(group.id))
}
}
.pickerStyle(.menu)
if !connectionError.isEmpty {
Text(connectionError)
.foregroundColor(.red)
.font(.caption)
}
HStack {
Button("Cancel") {
dismiss()
}
Spacer()
Button("Test connection") {
Task {
await testConnection()
}
}
.disabled(hostname.isEmpty || apiKey.isEmpty || testingConnection)
Button("Save") {
saveServer()
dismiss()
}
.disabled(hostname.isEmpty || apiKey.isEmpty || !connectionOK)
}
.padding(.top)
}
.padding()
.frame(width: 300)
.onAppear {
print("on appear")
if case let .edit(server) = mode {
print("serve \(server)")
hostname = server.hostname
apiKey = KeychainHelper.loadApiKey(for: server.hostname) ?? ""
selectedGroupID = server.groupID
connectionOK = true
print("💡 Loaded server: \(hostname)")
}
}
}
private var modeTitle: String {
switch mode {
case .add:
return "Add Server"
case .edit(let server):
return "Edit \(server.hostname)"
}
}
private func testConnection() async {
let host = hostname.trimmingCharacters(in: .whitespacesAndNewlines)
let key = apiKey.trimmingCharacters(in: .whitespacesAndNewlines)
await MainActor.run {
testingConnection = true
connectionError = ""
}
let reachable = await PingService.ping(hostname: host, apiKey: key)
await MainActor.run {
testingConnection = false
if reachable {
connectionOK = true
connectionError = ""
} else {
connectionOK = false
connectionError = "Connection failed. Check hostname and API key."
}
}
//
// guard let url = URL(string: "https://\(host)/api/v2/ping") else {
// print(" Invalid URL")
// return
// }
//
// var request = URLRequest(url: url)
// request.httpMethod = "GET"
// request.setValue(key, forHTTPHeaderField: "X-API-KEY")
// request.setValue("application/json", forHTTPHeaderField: "Content-Type")
//
// print("🔍 Headers: \(request.allHTTPHeaderFields ?? [:])")
//
// Task {
// do {
// let (data, response) = try await URLSession.shared.data(for: request)
// if let httpResponse = response as? HTTPURLResponse {
// print("🔄 Status: \(httpResponse.statusCode)")
// }
//
// if let result = try? JSONDecoder().decode([String: String].self, from: data),
// result["response"] == "pong" {
// print(" Pong received")
// connectionOK = true
// } else {
// print(" Unexpected response")
// connectionOK = false
// }
// } catch {
// print(" Error: \(error)")
// connectionOK = false
// }
// }
}
// func testKeyHelpConnection() {
// let url = URL(string: "https://keyhelp.lab.24unix.net/api/v2/ping")!
// var request = URLRequest(url: url)
// request.httpMethod = "GET"
//
// // @State is no reliable source
// let key = apiKey.trimmingCharacters(in: .whitespacesAndNewlines)
// request.setValue(key, forHTTPHeaderField: "X-API-KEY")
//
// request.setValue("application/json", forHTTPHeaderField: "Content-Type")
//
// print("🔍 Headers: \(request.allHTTPHeaderFields ?? [:])")
//
// let task = URLSession.shared.dataTask(with: request) { data, response, error in
// if let error = error {
// print(" Error: \(error.localizedDescription)")
// return
// }
//
// if let httpResponse = response as? HTTPURLResponse {
// print("🔄 Status Code: \(httpResponse.statusCode)")
// }
//
// if let data = data,
// let json = try? JSONSerialization.jsonObject(with: data) {
// print(" Response: \(json)")
// } else {
// print(" No JSON response")
// }
// }
//
// task.resume()
// }
private func saveServer() {
print("in save server")
let trimmedHost = hostname.trimmingCharacters(in: .whitespacesAndNewlines)
let trimmedKey = apiKey.trimmingCharacters(in: .whitespacesAndNewlines)
switch mode {
case .add:
print("adding server")
let newServer = Server(hostname: trimmedHost, groupID: selectedGroupID)
servers.append(newServer)
KeychainHelper.saveApiKey(trimmedKey, for: trimmedHost)
case .edit(let oldServer):
if let index = servers.firstIndex(where: { $0.id == oldServer.id }) {
let oldHostname = servers[index].hostname
servers[index].hostname = trimmedHost
servers[index].groupID = selectedGroupID
if oldHostname != trimmedHost {
KeychainHelper.deleteApiKey(for: oldHostname)
}
KeychainHelper.saveApiKey(trimmedKey, for: trimmedHost)
}
}
saveServers()
}
private func saveServers() {
if let data = try? JSONEncoder().encode(servers) {
UserDefaults.standard.set(data, forKey: "storedServers")
}
}
static func delete(server: Server, from servers: inout [Server]) {
if let index = servers.firstIndex(where: { $0.id == server.id }) {
servers.remove(at: index)
if let data = try? JSONEncoder().encode(servers) {
UserDefaults.standard.set(data, forKey: "storedServers")
}
}
}
}
#Preview {
ServerFormView(
mode: .edit(Server(hostname: "example.com")),
servers: .constant([
Server(hostname: "example.com")
]),
groups: .constant([
ServerGroup(name: "Production")
]),
dismiss: {}
)
}