Initial Commit
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
import SwiftUI
|
||||
|
||||
struct HostEditor: View {
|
||||
let store: ProxyStore
|
||||
let isNew: Bool
|
||||
@State private var host: ProxyHost
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@State private var domains: String
|
||||
@State private var port: String
|
||||
@State private var saving = false
|
||||
@State private var error: String?
|
||||
|
||||
init(store: ProxyStore, host: ProxyHost, isNew: Bool) {
|
||||
self.store = store
|
||||
self.isNew = isNew
|
||||
_host = State(initialValue: host)
|
||||
_domains = State(initialValue: host.domainNames.joined(separator: ", "))
|
||||
_port = State(initialValue: String(host.forwardPort))
|
||||
}
|
||||
|
||||
private var valid: Bool {
|
||||
let names = domains.split(separator: ",").map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
|
||||
return !names.isEmpty && names.allSatisfy { !$0.isEmpty && !$0.contains(" ") && !$0.contains("/") }
|
||||
&& !host.forwardHost.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
|
||||
&& (1...65535).contains(Int(port) ?? 0)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
HStack {
|
||||
Text(isNew ? "New Proxy Host" : "Edit Proxy Host").font(.headline)
|
||||
Spacer()
|
||||
}
|
||||
.padding(20)
|
||||
Form {
|
||||
Section {
|
||||
TextField("Domain Names", text: $domains, prompt: Text("app.example.com"))
|
||||
Picker("Forward Scheme", selection: $host.forwardScheme) {
|
||||
Text("HTTP").tag("http")
|
||||
Text("HTTPS").tag("https")
|
||||
}
|
||||
TextField("Forward Host", text: $host.forwardHost, prompt: Text("192.168.1.20"))
|
||||
TextField("Forward Port", text: $port, prompt: Text("8080"))
|
||||
} header: {
|
||||
Text("Forwarding")
|
||||
} footer: {
|
||||
Text("Separate multiple domain names with commas.")
|
||||
}
|
||||
Section("Security") {
|
||||
Picker("SSL Certificate", selection: $host.certificateId) {
|
||||
Text("None").tag(0)
|
||||
ForEach(store.certificates) { certificate in
|
||||
Text(certificate.niceName).tag(certificate.id)
|
||||
}
|
||||
}
|
||||
Toggle("Force HTTPS", isOn: $host.sslForced).disabled(host.certificateId == 0)
|
||||
Toggle("Block Common Exploits", isOn: $host.blockExploits)
|
||||
}
|
||||
Section("Options") {
|
||||
Toggle("HTTP/2 Support", isOn: $host.http2Support)
|
||||
Toggle("WebSocket Support", isOn: $host.allowWebsocketUpgrade)
|
||||
}
|
||||
}
|
||||
.formStyle(.grouped)
|
||||
.disabled(saving)
|
||||
if let error {
|
||||
Label(error, systemImage: "exclamationmark.triangle")
|
||||
.foregroundStyle(.red).padding(.horizontal, 20).padding(.bottom, 12)
|
||||
}
|
||||
Divider()
|
||||
HStack {
|
||||
if saving { ProgressView().controlSize(.small) }
|
||||
if store.isDemo { Text("Demo workspace").font(.callout).foregroundStyle(.secondary) }
|
||||
Spacer()
|
||||
Button("Cancel") { dismiss() }.keyboardShortcut(.cancelAction).disabled(saving)
|
||||
Button(isNew ? "Add" : "Save") { Task { await save() } }
|
||||
.keyboardShortcut(.defaultAction)
|
||||
.disabled(!valid || saving)
|
||||
}
|
||||
.padding(20)
|
||||
}
|
||||
.frame(width: 510, height: 610)
|
||||
.interactiveDismissDisabled(saving)
|
||||
}
|
||||
|
||||
private func save() async {
|
||||
saving = true
|
||||
error = nil
|
||||
host.domainNames = domains.split(separator: ",").map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
|
||||
host.forwardHost = host.forwardHost.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
host.forwardPort = Int(port) ?? 80
|
||||
if host.certificateId == 0 { host.sslForced = false }
|
||||
do {
|
||||
try await store.save(host, isNew: isNew)
|
||||
dismiss()
|
||||
} catch { self.error = error.localizedDescription }
|
||||
saving = false
|
||||
}
|
||||
}
|
||||
|
||||
struct ConnectionSheet: View {
|
||||
let store: ProxyStore
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@State private var url = ""
|
||||
@State private var email = ""
|
||||
@State private var password = ""
|
||||
@State private var connecting = false
|
||||
@State private var error: String?
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 20) {
|
||||
Text("Connect to Server").font(.headline)
|
||||
Text("Enter the address and credentials for your Nginx Proxy Manager server.")
|
||||
.foregroundStyle(.secondary)
|
||||
Form {
|
||||
TextField("Server URL:", text: $url, prompt: Text("https://proxy.example.com"))
|
||||
TextField("Email:", text: $email, prompt: Text("admin@example.com"))
|
||||
SecureField("Password:", text: $password)
|
||||
}
|
||||
.textFieldStyle(.roundedBorder)
|
||||
.disabled(connecting || store.isBusy)
|
||||
Text("Your login is saved in Keychain after connecting and used to reconnect when you open the app.")
|
||||
.font(.callout).foregroundStyle(.secondary)
|
||||
Button("Forget Saved Login") {
|
||||
do {
|
||||
try store.forgetCredentials()
|
||||
password = ""
|
||||
error = nil
|
||||
} catch { self.error = error.localizedDescription }
|
||||
}
|
||||
.disabled(connecting || store.isBusy)
|
||||
Text("Forgetting the saved login leaves the current session connected.")
|
||||
.font(.caption).foregroundStyle(.secondary)
|
||||
if let error {
|
||||
Label(error, systemImage: "exclamationmark.triangle").foregroundStyle(.red)
|
||||
}
|
||||
HStack {
|
||||
if connecting { ProgressView().controlSize(.small) }
|
||||
Spacer()
|
||||
Button("Cancel") { dismiss() }.keyboardShortcut(.cancelAction).disabled(connecting)
|
||||
Button("Connect") { Task { await connect() } }
|
||||
.keyboardShortcut(.defaultAction)
|
||||
.disabled(connecting || store.isBusy || url.isEmpty || email.isEmpty || password.isEmpty)
|
||||
}
|
||||
}
|
||||
.padding(24)
|
||||
.frame(width: 460)
|
||||
.onAppear { url = store.baseURL }
|
||||
.interactiveDismissDisabled(connecting)
|
||||
}
|
||||
|
||||
private func connect() async {
|
||||
connecting = true
|
||||
error = nil
|
||||
do {
|
||||
try await store.connect(url: url, email: email, password: password)
|
||||
password = ""
|
||||
dismiss()
|
||||
} catch { self.error = error.localizedDescription }
|
||||
connecting = false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user