Skip to content

Commit ab6f01b

Browse files
committed
refactor: split 3 large files into focused modules
DatabaseConnection.swift (526 → 327 lines): - SSHTypes.swift (168) — SSH enums, SSHJumpHost, SSHConfiguration - SSLConfiguration.swift (40) — SSLMode, SSLConfiguration ThemeDefinition.swift (765 → 110 lines): - ThemeColors.swift (414) — 8 color structs (Syntax, Editor, DataGrid, etc.) - ThemeLayout.swift (252) — 8 layout structs (Fonts, Spacing, Typography, etc.) AppSettings.swift (476 → 285 lines): - GeneralSettings.swift (94) — StartupBehavior, AppLanguage, GeneralSettings - EditorSettings.swift (107) — EditorFont, EditorSettings No type renames, no API changes, no import changes in consuming files.
1 parent 330a37a commit ab6f01b

9 files changed

Lines changed: 1075 additions & 1042 deletions

File tree

TablePro/Models/Connection/DatabaseConnection.swift

Lines changed: 0 additions & 198 deletions
Original file line numberDiff line numberDiff line change
@@ -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
21416
struct DatabaseType: Hashable, Identifiable, Sendable {
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
//
2+
// SSHTypes.swift
3+
// TablePro
4+
//
5+
6+
import Foundation
7+
/// SSH authentication method
8+
enum SSHAuthMethod: String, CaseIterable, Identifiable, Codable {
9+
case password = "Password"
10+
case privateKey = "Private Key"
11+
case sshAgent = "SSH Agent"
12+
case keyboardInteractive = "Keyboard Interactive"
13+
14+
var id: String { rawValue }
15+
16+
var displayName: String {
17+
switch self {
18+
case .password: return String(localized: "Password")
19+
case .privateKey: return String(localized: "Private Key")
20+
case .sshAgent: return String(localized: "SSH Agent")
21+
case .keyboardInteractive: return String(localized: "Keyboard Interactive")
22+
}
23+
}
24+
25+
var iconName: String {
26+
switch self {
27+
case .password: return "key.fill"
28+
case .privateKey: return "doc.text.fill"
29+
case .sshAgent: return "person.badge.key.fill"
30+
case .keyboardInteractive: return "lock.rotation"
31+
}
32+
}
33+
}
34+
35+
enum SSHAgentSocketOption: String, CaseIterable, Identifiable {
36+
case systemDefault
37+
case onePassword
38+
case custom
39+
40+
static let onePasswordSocketPath = "~/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock"
41+
private static let onePasswordAliasPath = "~/.1password/agent.sock"
42+
43+
var id: String { rawValue }
44+
45+
var displayName: String {
46+
switch self {
47+
case .systemDefault:
48+
return "SSH_AUTH_SOCK"
49+
case .onePassword:
50+
return "1Password"
51+
case .custom:
52+
return String(localized: "Custom Path")
53+
}
54+
}
55+
56+
init(socketPath: String) {
57+
let trimmedPath = socketPath.trimmingCharacters(in: .whitespacesAndNewlines)
58+
59+
switch trimmedPath {
60+
case "":
61+
self = .systemDefault
62+
case Self.onePasswordSocketPath, Self.onePasswordAliasPath:
63+
self = .onePassword
64+
default:
65+
self = .custom
66+
}
67+
}
68+
69+
func resolvedPath(customPath: String) -> String {
70+
switch self {
71+
case .systemDefault:
72+
return ""
73+
case .onePassword:
74+
return Self.onePasswordSocketPath
75+
case .custom:
76+
return customPath.trimmingCharacters(in: .whitespacesAndNewlines)
77+
}
78+
}
79+
}
80+
81+
enum SSHJumpAuthMethod: String, CaseIterable, Identifiable, Codable {
82+
case privateKey = "Private Key"
83+
case sshAgent = "SSH Agent"
84+
85+
var id: String { rawValue }
86+
}
87+
88+
struct SSHJumpHost: Codable, Hashable, Identifiable {
89+
var id = UUID()
90+
var host: String = ""
91+
var port: Int = 22
92+
var username: String = ""
93+
var authMethod: SSHJumpAuthMethod = .sshAgent
94+
var privateKeyPath: String = ""
95+
96+
var isValid: Bool {
97+
!host.isEmpty && !username.isEmpty &&
98+
(authMethod == .sshAgent || !privateKeyPath.isEmpty)
99+
}
100+
101+
var proxyJumpString: String {
102+
"\(username)@\(host):\(port)"
103+
}
104+
}
105+
106+
/// SSH tunnel configuration for database connections
107+
struct SSHConfiguration: Codable, Hashable {
108+
var enabled: Bool = false
109+
var host: String = ""
110+
var port: Int = 22
111+
var username: String = ""
112+
var authMethod: SSHAuthMethod = .password
113+
var privateKeyPath: String = "" // Path to identity file (e.g., ~/.ssh/id_rsa)
114+
var useSSHConfig: Bool = true // Auto-fill from ~/.ssh/config when selecting host
115+
var agentSocketPath: String = "" // Custom SSH_AUTH_SOCK path (empty = use system default)
116+
var jumpHosts: [SSHJumpHost] = []
117+
var totpMode: TOTPMode = .none
118+
var totpAlgorithm: TOTPAlgorithm = .sha1
119+
var totpDigits: Int = 6
120+
var totpPeriod: Int = 30
121+
122+
/// Check if SSH configuration is complete enough for connection
123+
var isValid: Bool {
124+
guard enabled else { return true } // Not enabled = valid (skip SSH)
125+
guard !host.isEmpty, !username.isEmpty else { return false }
126+
127+
let authValid: Bool
128+
switch authMethod {
129+
case .password:
130+
authValid = true
131+
case .privateKey:
132+
authValid = !privateKeyPath.isEmpty
133+
case .sshAgent:
134+
authValid = true
135+
case .keyboardInteractive:
136+
authValid = true
137+
}
138+
139+
return authValid && jumpHosts.allSatisfy(\.isValid)
140+
}
141+
}
142+
143+
extension SSHConfiguration {
144+
enum CodingKeys: String, CodingKey {
145+
case enabled, host, port, username, authMethod, privateKeyPath, useSSHConfig, agentSocketPath, jumpHosts
146+
case totpMode, totpAlgorithm, totpDigits, totpPeriod
147+
}
148+
149+
init(from decoder: Decoder) throws {
150+
let container = try decoder.container(keyedBy: CodingKeys.self)
151+
enabled = try container.decode(Bool.self, forKey: .enabled)
152+
host = try container.decode(String.self, forKey: .host)
153+
port = try container.decode(Int.self, forKey: .port)
154+
username = try container.decode(String.self, forKey: .username)
155+
authMethod = try container.decode(SSHAuthMethod.self, forKey: .authMethod)
156+
privateKeyPath = try container.decode(String.self, forKey: .privateKeyPath)
157+
useSSHConfig = try container.decode(Bool.self, forKey: .useSSHConfig)
158+
agentSocketPath = try container.decode(String.self, forKey: .agentSocketPath)
159+
jumpHosts = try container.decodeIfPresent([SSHJumpHost].self, forKey: .jumpHosts) ?? []
160+
totpMode = try container.decodeIfPresent(TOTPMode.self, forKey: .totpMode) ?? .none
161+
totpAlgorithm = try container.decodeIfPresent(TOTPAlgorithm.self, forKey: .totpAlgorithm) ?? .sha1
162+
totpDigits = try container.decodeIfPresent(Int.self, forKey: .totpDigits) ?? 6
163+
totpPeriod = try container.decodeIfPresent(Int.self, forKey: .totpPeriod) ?? 30
164+
}
165+
}
166+
167+
// MARK: - SSL Configuration
168+

0 commit comments

Comments
 (0)