Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 10 additions & 124 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { SettingsModal } from './components/settings-modal';
import { IntegratedFileBrowser } from './components/integrated-file-browser';
import { WelcomeScreen } from './components/welcome-screen';
import { UpdateChecker } from './components/update-checker';
import { toConnectionConfig } from './lib/connection-config';
import { ActiveConnectionsManager, ConnectionStorageManager } from './lib/connection-storage';
import { isDesktopProtocol } from './lib/protocol-config';
import { registerRestoration, clearAllRestorations } from './lib/restoration-manager';
Expand Down Expand Up @@ -506,22 +507,7 @@ function AppContent() {
: !!connectionData.privateKeyPath);

if (!hasCredentials) {
setEditingConnection({
id: connection.id,
name: connectionData.name,
protocol: connectionData.protocol as ConnectionConfig['protocol'],
host: connectionData.host,
port: connectionData.port,
username: connectionData.username,
authMethod: connectionData.authMethod || 'password',
password: connectionData.password,
privateKeyPath: connectionData.privateKeyPath,
passphrase: connectionData.passphrase,
ftpsEnabled: connectionData.ftpsEnabled,
domain: connectionData.domain,
rdpResolution: connectionData.rdpResolution as ConnectionConfig['rdpResolution'],
vncColorDepth: connectionData.vncColorDepth as ConnectionConfig['vncColorDepth'],
});
setEditingConnection(toConnectionConfig(connectionData));
setPendingConnectionId(connection.id);
setConnectionDialogOpen(true);
return;
Expand Down Expand Up @@ -631,18 +617,7 @@ function AppContent() {
toast.error(t('app.connectionFailed'), {
description: result.error || 'Unable to connect to the server. Please check your credentials and try again.',
});
setEditingConnection({
id: connection.id,
name: connectionData.name,
protocol: connectionData.protocol as ConnectionConfig['protocol'],
host: connectionData.host,
port: connectionData.port,
username: connectionData.username,
authMethod: connectionData.authMethod || 'password',
password: connectionData.password,
privateKeyPath: connectionData.privateKeyPath,
passphrase: connectionData.passphrase,
});
setEditingConnection(toConnectionConfig(connectionData));
setPendingConnectionId(connection.id);
setConnectionDialogOpen(true);
}
Expand All @@ -652,18 +627,7 @@ function AppContent() {
toast.error(t('app.connectionError'), {
description: error instanceof Error ? error.message : t('app.connectionErrorDesc'),
});
setEditingConnection({
id: connection.id,
name: connectionData.name,
protocol: connectionData.protocol as ConnectionConfig['protocol'],
host: connectionData.host,
port: connectionData.port,
username: connectionData.username,
authMethod: connectionData.authMethod || 'password',
password: connectionData.password,
privateKeyPath: connectionData.privateKeyPath,
passphrase: connectionData.passphrase,
});
setEditingConnection(toConnectionConfig(connectionData));
setPendingConnectionId(connection.id);
setConnectionDialogOpen(true);
}
Expand Down Expand Up @@ -873,22 +837,7 @@ function AppContent() {
toast.error(t('app.cannotReconnect'), {
description: t('app.noCredentialsDesc'),
});
setEditingConnection({
id: originalConnectionId,
name: connectionData.name,
protocol: connectionData.protocol as ConnectionConfig['protocol'],
host: connectionData.host,
port: connectionData.port,
username: connectionData.username,
authMethod: connectionData.authMethod || 'password',
password: connectionData.password,
privateKeyPath: connectionData.privateKeyPath,
passphrase: connectionData.passphrase,
ftpsEnabled: connectionData.ftpsEnabled,
domain: connectionData.domain,
rdpResolution: connectionData.rdpResolution as ConnectionConfig['rdpResolution'],
vncColorDepth: connectionData.vncColorDepth as ConnectionConfig['vncColorDepth'],
});
setEditingConnection(toConnectionConfig(connectionData));
setPendingConnectionId(originalConnectionId);
setConnectionDialogOpen(true);
return;
Expand Down Expand Up @@ -1296,21 +1245,7 @@ function AppContent() {
if (connection.type === 'connection') {
const connectionData = ConnectionStorageManager.getConnection(connection.id);
if (connectionData) {
setEditingConnection({
id: connectionData.id,
name: connectionData.name,
protocol: connectionData.protocol as ConnectionConfig['protocol'],
host: connectionData.host,
port: connectionData.port,
username: connectionData.username,
authMethod: connectionData.authMethod || 'password',
password: connectionData.password,
privateKeyPath: connectionData.privateKeyPath,
passphrase: connectionData.passphrase,
domain: connectionData.domain,
rdpResolution: connectionData.rdpResolution as ConnectionConfig['rdpResolution'],
vncColorDepth: connectionData.vncColorDepth as ConnectionConfig['vncColorDepth'],
});
setEditingConnection(toConnectionConfig(connectionData));
setConnectionDialogOpen(true);
setPendingConnectionId(null);
} else {
Expand Down Expand Up @@ -1541,42 +1476,15 @@ function AppContent() {
: !!connectionData.privateKeyPath);

if (!hasCredentials) {
setEditingConnection({
id: connectionData.id,
name: connectionData.name,
protocol: connectionData.protocol as ConnectionConfig['protocol'],
host: connectionData.host,
port: connectionData.port,
username: connectionData.username,
authMethod: connectionData.authMethod || 'password',
password: connectionData.password,
privateKeyPath: connectionData.privateKeyPath,
passphrase: connectionData.passphrase,
ftpsEnabled: connectionData.ftpsEnabled,
domain: connectionData.domain,
rdpResolution: connectionData.rdpResolution as ConnectionConfig['rdpResolution'],
vncColorDepth: connectionData.vncColorDepth as ConnectionConfig['vncColorDepth'],
});
setEditingConnection(toConnectionConfig(connectionData));
setPendingConnectionId(connectionData.id);
setConnectionDialogOpen(true);
return;
}

if (isFileBrowser) {
// Route through handleConnectionDialogConnect which handles SFTP/FTP
const config: ConnectionConfig = {
id: connectionData.id,
name: connectionData.name,
protocol: connectionData.protocol as ConnectionConfig['protocol'],
host: connectionData.host,
port: connectionData.port,
username: connectionData.username,
authMethod: connectionData.authMethod || 'password',
password: connectionData.password,
privateKeyPath: connectionData.privateKeyPath,
passphrase: connectionData.passphrase,
ftpsEnabled: connectionData.ftpsEnabled,
};
const config: ConnectionConfig = toConnectionConfig(connectionData);
await handleConnectionDialogConnect(config);
toast.success(t('app.quickConnected'), {
description: t('app.quickConnectedDesc', { name: connectionData.name }),
Expand All @@ -1603,18 +1511,7 @@ function AppContent() {
if (result.success) {
ConnectionStorageManager.updateLastConnected(connectionData.id);

const config: ConnectionConfig = {
id: connectionData.id,
name: connectionData.name,
protocol: connectionData.protocol as ConnectionConfig['protocol'],
host: connectionData.host,
port: connectionData.port,
username: connectionData.username,
authMethod: connectionData.authMethod || 'password',
password: connectionData.password,
privateKeyPath: connectionData.privateKeyPath,
passphrase: connectionData.passphrase,
};
const config: ConnectionConfig = toConnectionConfig(connectionData);

handleConnectionDialogConnect(config);

Expand All @@ -1626,18 +1523,7 @@ function AppContent() {
toast.error(t('app.connectionFailed'), {
description: result.error || 'Unable to connect. Please try again.',
});
setEditingConnection({
id: connectionData.id,
name: connectionData.name,
protocol: connectionData.protocol as ConnectionConfig['protocol'],
host: connectionData.host,
port: connectionData.port,
username: connectionData.username,
authMethod: connectionData.authMethod || 'password',
password: connectionData.password,
privateKeyPath: connectionData.privateKeyPath,
passphrase: connectionData.passphrase,
});
setEditingConnection(toConnectionConfig(connectionData));
setPendingConnectionId(connectionData.id);
setConnectionDialogOpen(true);
}
Expand Down
79 changes: 79 additions & 0 deletions src/__tests__/connection-config.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* Unit tests for toConnectionConfig — the mapping from persisted
* ConnectionData to the dialog form's ConnectionConfig.
*
* Covers the proxy round-trip: saved proxy settings must be carried into
* the edit dialog, and legacy connections without proxy must not show a
* phantom proxy.
*/
import { describe, expect, it } from 'vitest';
import { toConnectionConfig } from '../lib/connection-config';

describe('toConnectionConfig', () => {
const base = {
id: 'conn-1',
name: 'My Server',
host: '192.168.1.1',
port: 22,
username: 'admin',
protocol: 'SSH',
authMethod: 'password' as const,
password: 'secret',
};

it('maps all proxy fields from storage', () => {
const config = toConnectionConfig({
...base,
proxyType: 'http' as const,
proxyHost: 'proxy.example.com',
proxyPort: 3128,
proxyUsername: 'proxyuser',
proxyPassword: 'proxypass',
});

expect(config.proxyType).toBe('http');
expect(config.proxyHost).toBe('proxy.example.com');
expect(config.proxyPort).toBe(3128);
expect(config.proxyUsername).toBe('proxyuser');
expect(config.proxyPassword).toBe('proxypass');
});

it('defaults proxyType to none when storage has no proxy', () => {
const config = toConnectionConfig(base);

expect(config.proxyType).toBe('none');
expect(config.proxyHost).toBeUndefined();
expect(config.proxyUsername).toBeUndefined();
});

it('defaults proxyPort to 8080 when storage omits it', () => {
const config = toConnectionConfig({
...base,
proxyType: 'socks5' as const,
proxyHost: 'socks.example.com',
});

expect(config.proxyType).toBe('socks5');
expect(config.proxyPort).toBe(8080);
});

it('carries basic fields and auth method default', () => {
const config = toConnectionConfig({
...base,
authMethod: 'publickey' as const,
privateKeyPath: '/home/user/.ssh/id_ed25519',
});

expect(config.id).toBe('conn-1');
expect(config.name).toBe('My Server');
expect(config.host).toBe('192.168.1.1');
expect(config.authMethod).toBe('publickey');
expect(config.privateKeyPath).toBe('/home/user/.ssh/id_ed25519');
});

it('falls back to password auth when storage omits authMethod', () => {
const config = toConnectionConfig({ ...base, authMethod: undefined });

expect(config.authMethod).toBe('password');
});
});
Loading
Loading