@@ -11,204 +11,6 @@ import SwiftUI
1111
1212// MARK: - SSH Configuration
1313
14- /// SSH authentication method
15- enum SSHAuthMethod : String , CaseIterable , Identifiable , Codable {
16- case password = " Password "
17- case privateKey = " Private Key "
18- case sshAgent = " SSH Agent "
19- case keyboardInteractive = " Keyboard Interactive "
20-
21- var id : String { rawValue }
22-
23- var displayName : String {
24- switch self {
25- case . password: return String ( localized: " Password " )
26- case . privateKey: return String ( localized: " Private Key " )
27- case . sshAgent: return String ( localized: " SSH Agent " )
28- case . keyboardInteractive: return String ( localized: " Keyboard Interactive " )
29- }
30- }
31-
32- var iconName : String {
33- switch self {
34- case . password: return " key.fill "
35- case . privateKey: return " doc.text.fill "
36- case . sshAgent: return " person.badge.key.fill "
37- case . keyboardInteractive: return " lock.rotation "
38- }
39- }
40- }
41-
42- enum SSHAgentSocketOption : String , CaseIterable , Identifiable {
43- case systemDefault
44- case onePassword
45- case custom
46-
47- static let onePasswordSocketPath = " ~/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock "
48- private static let onePasswordAliasPath = " ~/.1password/agent.sock "
49-
50- var id : String { rawValue }
51-
52- var displayName : String {
53- switch self {
54- case . systemDefault:
55- return " SSH_AUTH_SOCK "
56- case . onePassword:
57- return " 1Password "
58- case . custom:
59- return String ( localized: " Custom Path " )
60- }
61- }
62-
63- init ( socketPath: String ) {
64- let trimmedPath = socketPath. trimmingCharacters ( in: . whitespacesAndNewlines)
65-
66- switch trimmedPath {
67- case " " :
68- self = . systemDefault
69- case Self . onePasswordSocketPath, Self . onePasswordAliasPath:
70- self = . onePassword
71- default :
72- self = . custom
73- }
74- }
75-
76- func resolvedPath( customPath: String ) -> String {
77- switch self {
78- case . systemDefault:
79- return " "
80- case . onePassword:
81- return Self . onePasswordSocketPath
82- case . custom:
83- return customPath. trimmingCharacters ( in: . whitespacesAndNewlines)
84- }
85- }
86- }
87-
88- enum SSHJumpAuthMethod : String , CaseIterable , Identifiable , Codable {
89- case privateKey = " Private Key "
90- case sshAgent = " SSH Agent "
91-
92- var id : String { rawValue }
93- }
94-
95- struct SSHJumpHost : Codable , Hashable , Identifiable {
96- var id = UUID ( )
97- var host : String = " "
98- var port : Int = 22
99- var username : String = " "
100- var authMethod : SSHJumpAuthMethod = . sshAgent
101- var privateKeyPath : String = " "
102-
103- var isValid : Bool {
104- !host. isEmpty && !username. isEmpty &&
105- ( authMethod == . sshAgent || !privateKeyPath. isEmpty)
106- }
107-
108- var proxyJumpString : String {
109- " \( username) @ \( host) : \( port) "
110- }
111- }
112-
113- /// SSH tunnel configuration for database connections
114- struct SSHConfiguration : Codable , Hashable {
115- var enabled : Bool = false
116- var host : String = " "
117- var port : Int = 22
118- var username : String = " "
119- var authMethod : SSHAuthMethod = . password
120- var privateKeyPath : String = " " // Path to identity file (e.g., ~/.ssh/id_rsa)
121- var useSSHConfig : Bool = true // Auto-fill from ~/.ssh/config when selecting host
122- var agentSocketPath : String = " " // Custom SSH_AUTH_SOCK path (empty = use system default)
123- var jumpHosts : [ SSHJumpHost ] = [ ]
124- var totpMode : TOTPMode = . none
125- var totpAlgorithm : TOTPAlgorithm = . sha1
126- var totpDigits : Int = 6
127- var totpPeriod : Int = 30
128-
129- /// Check if SSH configuration is complete enough for connection
130- var isValid : Bool {
131- guard enabled else { return true } // Not enabled = valid (skip SSH)
132- guard !host. isEmpty, !username. isEmpty else { return false }
133-
134- let authValid : Bool
135- switch authMethod {
136- case . password:
137- authValid = true
138- case . privateKey:
139- authValid = !privateKeyPath. isEmpty
140- case . sshAgent:
141- authValid = true
142- case . keyboardInteractive:
143- authValid = true
144- }
145-
146- return authValid && jumpHosts. allSatisfy ( \. isValid)
147- }
148- }
149-
150- extension SSHConfiguration {
151- enum CodingKeys : String , CodingKey {
152- case enabled, host, port, username, authMethod, privateKeyPath, useSSHConfig, agentSocketPath, jumpHosts
153- case totpMode, totpAlgorithm, totpDigits, totpPeriod
154- }
155-
156- init ( from decoder: Decoder ) throws {
157- let container = try decoder. container ( keyedBy: CodingKeys . self)
158- enabled = try container. decode ( Bool . self, forKey: . enabled)
159- host = try container. decode ( String . self, forKey: . host)
160- port = try container. decode ( Int . self, forKey: . port)
161- username = try container. decode ( String . self, forKey: . username)
162- authMethod = try container. decode ( SSHAuthMethod . self, forKey: . authMethod)
163- privateKeyPath = try container. decode ( String . self, forKey: . privateKeyPath)
164- useSSHConfig = try container. decode ( Bool . self, forKey: . useSSHConfig)
165- agentSocketPath = try container. decode ( String . self, forKey: . agentSocketPath)
166- jumpHosts = try container. decodeIfPresent ( [ SSHJumpHost ] . self, forKey: . jumpHosts) ?? [ ]
167- totpMode = try container. decodeIfPresent ( TOTPMode . self, forKey: . totpMode) ?? . none
168- totpAlgorithm = try container. decodeIfPresent ( TOTPAlgorithm . self, forKey: . totpAlgorithm) ?? . sha1
169- totpDigits = try container. decodeIfPresent ( Int . self, forKey: . totpDigits) ?? 6
170- totpPeriod = try container. decodeIfPresent ( Int . self, forKey: . totpPeriod) ?? 30
171- }
172- }
173-
174- // MARK: - SSL Configuration
175-
176- /// SSL/TLS connection mode
177- enum SSLMode : String , CaseIterable , Identifiable , Codable {
178- case disabled = " Disabled "
179- case preferred = " Preferred "
180- case required = " Required "
181- case verifyCa = " Verify CA "
182- case verifyIdentity = " Verify Identity "
183-
184- var id : String { rawValue }
185-
186- var description : String {
187- switch self {
188- case . disabled: return String ( localized: " No SSL encryption " )
189- case . preferred: return String ( localized: " Use SSL if available " )
190- case . required: return String ( localized: " Require SSL, skip verification " )
191- case . verifyCa: return String ( localized: " Verify server certificate " )
192- case . verifyIdentity: return String ( localized: " Verify certificate and hostname " )
193- }
194- }
195- }
196-
197- /// SSL/TLS configuration for database connections
198- struct SSLConfiguration : Codable , Hashable {
199- var mode : SSLMode = . disabled
200- var caCertificatePath : String = " "
201- var clientCertificatePath : String = " "
202- var clientKeyPath : String = " "
203-
204- /// Whether SSL is effectively enabled
205- var isEnabled : Bool { mode != . disabled }
206-
207- /// Whether certificate verification is enabled
208- var verifiesCertificate : Bool { mode == . verifyCa || mode == . verifyIdentity }
209- }
210-
211- // MARK: - Database Type
21214
21315/// Represents the type of database
21416struct DatabaseType : Hashable , Identifiable , Sendable {
0 commit comments