You've already forked ios-client
mirror of
https://github.com/netbirdio/ios-client.git
synced 2026-05-22 17:10:12 -07:00
0327624b30
* fix layout on ipad * Create multi profiles * multi-profile server config, connection cache, and auth fixes Split ProfilesView into ProfilesListView, AddProfileSheet, ProfileBadge, AddProfileViewModel - Add server URL + setup key fields to Add Profile screen - Store per-profile connection data (ip/fqdn/managementURL) as typed model in ProfileConnectionCache - Show cached connection info immediately on profile switch; empty if no prior data - Fix profile deletion persistence via tombstone in profiles.json - Fix logout to remove both netbird.cfg and state.json (cfg holds auth tokens) - Preserve managementURL in UI after logout via cache fallback - Guard polling from overwriting new profile's data during disconnect/reconnect cycle * fix(multi-profile): reinitialize VPN adapter on profile switch and show current server URL * Update project.pbxproj * fix(multi-profile): prevent default server overwrite on logout/re-login * Update PacketTunnelProvider.swift * fix(multi-profile): address code review findings - Normalize only scheme and host to lowercase when saving management server URL; previously the full URL was lowercased which could corrupt case-sensitive paths on self-hosted servers - Move switchConnectionInfo(to:) inside the do-block so the connection UI is only updated after a successful profile switch - Add ProfileConnectionCache.remove(for:) and clearConnectionData(for:) to prevent stale ip/fqdn/managementURL from persisting after a profile is deleted or logged out; call them from removeProfile and logoutProfile - Use ASWebAuthenticationSession.Callback.customScheme on iOS 17.4+, falling back to the deprecated callbackURLScheme initializer on older versions; add a comment explaining why "http" is used and that a proper fix requires custom-scheme support in the SDK - Guard presentationAnchor against a missing key window with assertionFailure in debug builds instead of silently returning an empty UIWindow
172 lines
6.5 KiB
Swift
172 lines
6.5 KiB
Swift
//
|
|
// AddProfileSheet.swift
|
|
// NetBird
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
#if os(iOS)
|
|
|
|
struct AddProfileSheet: View {
|
|
@Environment(\.dismiss) private var dismiss
|
|
@StateObject private var addVM = AddProfileViewModel()
|
|
|
|
@State private var profileName = ""
|
|
@State private var managementServerUrl = ""
|
|
@State private var setupKey = ""
|
|
@State private var showSetupKeyField = false
|
|
@State private var showNameValidationAlert = false
|
|
|
|
var onCreated: (() -> Void)?
|
|
|
|
private let defaultManagementServerUrl = "https://api.netbird.io"
|
|
|
|
private var isNameValid: Bool {
|
|
!profileName.isEmpty && profileName.range(of: "^[a-zA-Z0-9_-]+$", options: .regularExpression) != nil
|
|
}
|
|
|
|
var body: some View {
|
|
NavigationView {
|
|
List {
|
|
// Profile name
|
|
Section {
|
|
TextField("Profile name", text: $profileName)
|
|
.autocorrectionDisabled()
|
|
.textInputAutocapitalization(.never)
|
|
.disabled(addVM.isLoading)
|
|
} header: {
|
|
Text("Profile")
|
|
} footer: {
|
|
if let error = addVM.profileError {
|
|
Text(error)
|
|
.foregroundColor(.red)
|
|
} else {
|
|
Text("Only letters, numbers, underscores and hyphens allowed")
|
|
.foregroundColor(Color("TextSecondary"))
|
|
}
|
|
}
|
|
|
|
// Server URL
|
|
Section(header: Text("Server")) {
|
|
TextField("https://api.netbird.io", text: $managementServerUrl)
|
|
.autocorrectionDisabled()
|
|
.textInputAutocapitalization(.never)
|
|
.keyboardType(.URL)
|
|
.disabled(addVM.isLoading)
|
|
.onChange(of: managementServerUrl) { _ in
|
|
addVM.urlError = nil
|
|
addVM.generalError = nil
|
|
addVM.ssoNotSupportedError = nil
|
|
}
|
|
if let error = addVM.urlError {
|
|
Text(error).foregroundColor(.red).font(.footnote)
|
|
}
|
|
if let error = addVM.generalError {
|
|
Text(error).foregroundColor(.red).font(.footnote)
|
|
}
|
|
}
|
|
|
|
// Setup key (optional)
|
|
Section {
|
|
DisclosureGroup("Add this device with a setup key", isExpanded: $showSetupKeyField) {
|
|
TextField("0EF79C2F-DEE1-419B-BFC8-1BF529332998", text: $setupKey)
|
|
.disableAutocorrection(true)
|
|
.autocapitalization(.allCharacters)
|
|
.disabled(addVM.isLoading)
|
|
.onChange(of: setupKey) { _ in
|
|
addVM.setupKeyError = nil
|
|
}
|
|
if let error = addVM.setupKeyError {
|
|
Text(error).foregroundColor(.red).font(.footnote)
|
|
}
|
|
if let error = addVM.ssoNotSupportedError {
|
|
Text(error).foregroundColor(.red).font(.footnote)
|
|
}
|
|
Text("Using setup keys for user devices is not recommended. SSO with MFA provides stronger security, proper user-device association, and periodic re-authentication.")
|
|
.font(.footnote)
|
|
.foregroundColor(.accentColor)
|
|
.padding(.vertical, 4)
|
|
}
|
|
}
|
|
.onChange(of: showSetupKeyField) { expanded in
|
|
if !expanded {
|
|
setupKey = ""
|
|
addVM.setupKeyError = nil
|
|
}
|
|
}
|
|
|
|
// Use NetBird server shortcut
|
|
Section {
|
|
Button {
|
|
managementServerUrl = defaultManagementServerUrl
|
|
} label: {
|
|
HStack {
|
|
Spacer()
|
|
Image("icon-netbird-button")
|
|
.resizable()
|
|
.scaledToFit()
|
|
.frame(width: 22, height: 22)
|
|
Text("Use NetBird server")
|
|
Spacer()
|
|
}
|
|
}
|
|
.disabled(addVM.isLoading)
|
|
}
|
|
}
|
|
.listStyle(.insetGrouped)
|
|
.navigationTitle("New Profile")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.toolbar {
|
|
ToolbarItem(placement: .navigationBarLeading) {
|
|
Button("Cancel") {
|
|
dismiss()
|
|
}
|
|
.foregroundColor(.accentColor)
|
|
.disabled(addVM.isLoading)
|
|
}
|
|
ToolbarItem(placement: .navigationBarTrailing) {
|
|
if addVM.isLoading {
|
|
ProgressView()
|
|
} else {
|
|
Button("Create") {
|
|
guard isNameValid else {
|
|
showNameValidationAlert = true
|
|
return
|
|
}
|
|
addVM.create(
|
|
name: profileName,
|
|
serverUrl: managementServerUrl,
|
|
setupKey: setupKey
|
|
)
|
|
}
|
|
.foregroundColor(.accentColor)
|
|
}
|
|
}
|
|
}
|
|
.alert("Invalid Profile Name", isPresented: $showNameValidationAlert) {
|
|
Button("OK", role: .cancel) {}
|
|
} message: {
|
|
Text("Only letters, numbers, underscores and hyphens are allowed.")
|
|
}
|
|
.onChange(of: addVM.ssoNotSupportedError) { error in
|
|
if error != nil {
|
|
showSetupKeyField = true
|
|
}
|
|
}
|
|
.onChange(of: addVM.isSuccess) { success in
|
|
if success {
|
|
onCreated?()
|
|
dismiss()
|
|
}
|
|
}
|
|
}
|
|
.navigationViewStyle(.stack)
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
AddProfileSheet()
|
|
}
|
|
|
|
#endif
|