Skip to content

Commit ec5f7b8

Browse files
committed
fix: preserve SSH profile on sync round-trip and save fallback config (#490)
1 parent 2f81a5c commit ec5f7b8

File tree

5 files changed

+36
-32
lines changed

5 files changed

+36
-32
lines changed

CHANGELOG.md

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12-
- Nested hierarchical groups for connection list (up to 3 levels deep) with subgroup creation, group reparenting, and recursive delete
12+
- Nested hierarchical groups for connection list (up to 3 levels deep)
1313
- Confirmation dialogs for deep link queries, connection imports, and pre-connect scripts
1414
- JSON fields in Row Details sidebar now display in a scrollable monospaced text area
1515

1616
### Fixed
1717

18-
- MariaDB JSON columns misdetected as BLOB, showing hex dumps instead of JSON text
19-
20-
### Fixed
21-
22-
- MongoDB Atlas connections failing with "TLS certificate verify failed" due to missing CA bundle
23-
24-
### Fixed
25-
26-
- ENUM/SET dropdown chevron buttons not showing on first table open, requiring a refresh to appear
18+
- SSH profile lost after app restart when iCloud Sync enabled
19+
- MariaDB JSON columns showing as hex dumps instead of JSON text
20+
- MongoDB Atlas TLS certificate verification failure
21+
- ENUM/SET dropdown chevron buttons not showing on first table open
2722

2823
## [0.25.0] - 2026-03-27
2924

TablePro/Core/Storage/SSHProfileStorage.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ final class SSHProfileStorage {
1414
private let defaults = UserDefaults.standard
1515
private let encoder = JSONEncoder()
1616
private let decoder = JSONDecoder()
17-
private var lastLoadFailed = false
17+
private(set) var lastLoadFailed = false
1818

1919
private init() {}
2020

TablePro/Core/Sync/SyncRecordMapper.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ struct SyncRecordMapper {
8383
if let startupCommands = connection.startupCommands {
8484
record["startupCommands"] = startupCommands as CKRecordValue
8585
}
86+
if let sshProfileId = connection.sshProfileId {
87+
record["sshProfileId"] = sshProfileId.uuidString as CKRecordValue
88+
}
8689

8790
// Encode complex structs as JSON Data
8891
do {
@@ -130,6 +133,7 @@ struct SyncRecordMapper {
130133
let aiPolicyRaw = record["aiPolicy"] as? String
131134
let redisDatabase = (record["redisDatabase"] as? Int64).map { Int($0) }
132135
let startupCommands = record["startupCommands"] as? String
136+
let sshProfileId = (record["sshProfileId"] as? String).flatMap { UUID(uuidString: $0) }
133137

134138
var sshConfig = SSHConfiguration()
135139
if let sshData = record["sshConfigJson"] as? Data {
@@ -159,6 +163,7 @@ struct SyncRecordMapper {
159163
color: ConnectionColor(rawValue: colorRaw) ?? .none,
160164
tagId: tagId,
161165
groupId: groupId,
166+
sshProfileId: sshProfileId,
162167
safeModeLevel: SafeModeLevel(rawValue: safeModeLevelRaw) ?? .silent,
163168
aiPolicy: aiPolicyRaw.flatMap { AIConnectionPolicy(rawValue: $0) },
164169
redisDatabase: redisDatabase,

TablePro/Models/Connection/ConnectionGroupTree.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import Foundation
77

8-
enum ConnectionGroupTreeNode: Identifiable, Hashable {
8+
enum ConnectionGroupTreeNode: Identifiable {
99
case group(ConnectionGroup, children: [ConnectionGroupTreeNode])
1010
case connection(DatabaseConnection)
1111

@@ -15,9 +15,6 @@ enum ConnectionGroupTreeNode: Identifiable, Hashable {
1515
case .connection(let c): "conn-\(c.id)"
1616
}
1717
}
18-
19-
static func == (lhs: Self, rhs: Self) -> Bool { lhs.id == rhs.id }
20-
func hash(into hasher: inout Hasher) { hasher.combine(id) }
2118
}
2219

2320
// MARK: - Tree Building

TablePro/Views/Connection/ConnectionFormView.swift

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -613,8 +613,9 @@ struct ConnectionFormView: View { // swiftlint:disable:this type_body_length
613613

614614
private func reloadProfiles() {
615615
sshProfiles = SSHProfileStorage.shared.loadProfiles()
616-
// If the edited/deleted profile no longer exists, clear the selection
617-
if let id = sshProfileId, !sshProfiles.contains(where: { $0.id == id }) {
616+
if let id = sshProfileId,
617+
!SSHProfileStorage.shared.lastLoadFailed,
618+
!sshProfiles.contains(where: { $0.id == id }) {
618619
sshProfileId = nil
619620
}
620621
}
@@ -1176,21 +1177,27 @@ struct ConnectionFormView: View { // swiftlint:disable:this type_body_length
11761177
}
11771178

11781179
private func saveConnection() {
1179-
let sshConfig = SSHConfiguration(
1180-
enabled: sshEnabled,
1181-
host: sshHost,
1182-
port: Int(sshPort) ?? 22,
1183-
username: sshUsername,
1184-
authMethod: sshAuthMethod,
1185-
privateKeyPath: sshPrivateKeyPath,
1186-
useSSHConfig: !selectedSSHConfigHost.isEmpty,
1187-
agentSocketPath: resolvedSSHAgentSocketPath,
1188-
jumpHosts: jumpHosts,
1189-
totpMode: totpMode,
1190-
totpAlgorithm: totpAlgorithm,
1191-
totpDigits: totpDigits,
1192-
totpPeriod: totpPeriod
1193-
)
1180+
let sshConfig: SSHConfiguration
1181+
if let profileId = sshProfileId,
1182+
let profile = sshProfiles.first(where: { $0.id == profileId }) {
1183+
sshConfig = profile.toSSHConfiguration()
1184+
} else {
1185+
sshConfig = SSHConfiguration(
1186+
enabled: sshEnabled,
1187+
host: sshHost,
1188+
port: Int(sshPort) ?? 22,
1189+
username: sshUsername,
1190+
authMethod: sshAuthMethod,
1191+
privateKeyPath: sshPrivateKeyPath,
1192+
useSSHConfig: !selectedSSHConfigHost.isEmpty,
1193+
agentSocketPath: resolvedSSHAgentSocketPath,
1194+
jumpHosts: jumpHosts,
1195+
totpMode: totpMode,
1196+
totpAlgorithm: totpAlgorithm,
1197+
totpDigits: totpDigits,
1198+
totpPeriod: totpPeriod
1199+
)
1200+
}
11941201

11951202
let sslConfig = SSLConfiguration(
11961203
mode: sslMode,

0 commit comments

Comments
 (0)