diff --git a/Lite/MainWindow.xaml b/Lite/MainWindow.xaml index 14e32e0..d3008d3 100644 --- a/Lite/MainWindow.xaml +++ b/Lite/MainWindow.xaml @@ -175,6 +175,13 @@ + + + + + + diff --git a/Lite/MainWindow.xaml.cs b/Lite/MainWindow.xaml.cs index f229108..0409c76 100644 --- a/Lite/MainWindow.xaml.cs +++ b/Lite/MainWindow.xaml.cs @@ -840,6 +840,47 @@ private void AboutButton_Click(object sender, RoutedEventArgs e) window.ShowDialog(); } + private void ImportConnectionsButton_Click(object sender, RoutedEventArgs e) + { + var dialog = new Microsoft.Win32.OpenFolderDialog + { + Title = "Select Previous Lite Install Folder" + }; + + if (dialog.ShowDialog() != true) return; + + var serversJsonPath = System.IO.Path.Combine(dialog.FolderName, "config", "servers.json"); + if (!System.IO.File.Exists(serversJsonPath)) + { + MessageBox.Show( + "No config\\servers.json found in the selected folder.\n\nSelect the root folder of a previous Lite installation.", + "Import Connections", + MessageBoxButton.OK, + MessageBoxImage.Warning); + return; + } + + try + { + var (imported, skipped) = _serverManager.ImportServersFromFile(serversJsonPath); + + var message = $"Imported {imported} server connection(s)."; + if (skipped > 0) + message += $"\nSkipped {skipped} duplicate(s) (already configured)."; + if (imported > 0) + message += "\n\nCredentials from the previous install are preserved.\nIf any connections fail to authenticate, re-enter the password in Manage Servers."; + + MessageBox.Show(message, "Import Connections", MessageBoxButton.OK, MessageBoxImage.Information); + + if (imported > 0) + RefreshServerList(); + } + catch (Exception ex) + { + MessageBox.Show($"Failed to import connections: {ex.Message}", "Import Error", MessageBoxButton.OK, MessageBoxImage.Error); + } + } + private async void ImportDataButton_Click(object sender, RoutedEventArgs e) { /* Open folder browser to select the old Lite install directory */ diff --git a/Lite/Services/ServerManager.cs b/Lite/Services/ServerManager.cs index bc738a2..167d957 100644 --- a/Lite/Services/ServerManager.cs +++ b/Lite/Services/ServerManager.cs @@ -510,6 +510,60 @@ private void LoadServers() } } + /// + /// Imports server connections from an external servers.json file. + /// Upserts by ServerName — existing servers are skipped, new ones are added + /// with their original GUIDs so Credential Manager entries still resolve. + /// Returns (imported count, skipped count). + /// + public (int Imported, int Skipped) ImportServersFromFile(string serversJsonPath) + { + if (!File.Exists(serversJsonPath)) + throw new FileNotFoundException("servers.json not found", serversJsonPath); + + var json = File.ReadAllText(serversJsonPath); + var config = JsonSerializer.Deserialize(json); + var importedServers = config?.Servers ?? []; + + int imported = 0; + int skipped = 0; + + lock (_serversLock) + { + foreach (var server in importedServers) + { + // Skip if we already have a server with the same name + var existing = _servers.FirstOrDefault(s => + string.Equals(s.ServerName, server.ServerName, StringComparison.OrdinalIgnoreCase) && + s.ReadOnlyIntent == server.ReadOnlyIntent); + + if (existing != null) + { + skipped++; + continue; + } + + // Also skip if the same GUID already exists (shouldn't happen, but defensive) + if (_servers.Any(s => s.Id == server.Id)) + { + skipped++; + continue; + } + + // Add with original GUID so Credential Manager entries still work + _servers.Add(server); + _connectionStatuses[server.Id] = new ServerConnectionStatus { ServerId = server.Id }; + imported++; + } + + if (imported > 0) + SaveServers(); + } + + _logger?.LogInformation("Imported {Imported} servers, skipped {Skipped} duplicates", imported, skipped); + return (imported, skipped); + } + /// /// Saves servers to the JSON config file. ///