feat(02-03): add SSH profile management to ConfigManager
- Add SSH connection profile management methods to ConfigManager - addSshProfile(): Store connection profiles for quick reconnection - removeSshProfile(): Remove profiles by ID - updateSshProfile(): Update existing profiles - getSshProfiles(): Retrieve all profiles - setLastActiveContextId(): Persist last active context for app restart - profiles array and lastActiveContextId field in ssh config section - Context API already existed from plan 02-02 with all required IPC handlers and preload bridge
This commit is contained in:
parent
4aafc9c4fc
commit
4921c610c4
3 changed files with 126 additions and 0 deletions
|
|
@ -18,6 +18,7 @@ import * as path from 'path';
|
||||||
import { DEFAULT_TRIGGERS, TriggerManager } from './TriggerManager';
|
import { DEFAULT_TRIGGERS, TriggerManager } from './TriggerManager';
|
||||||
|
|
||||||
import type { TriggerColor } from '@shared/constants/triggerColors';
|
import type { TriggerColor } from '@shared/constants/triggerColors';
|
||||||
|
import type { SshConnectionProfile } from '@shared/types/api';
|
||||||
|
|
||||||
const logger = createLogger('Service:ConfigManager');
|
const logger = createLogger('Service:ConfigManager');
|
||||||
|
|
||||||
|
|
@ -199,6 +200,8 @@ export interface SshPersistConfig {
|
||||||
privateKeyPath?: string;
|
privateKeyPath?: string;
|
||||||
} | null;
|
} | null;
|
||||||
autoReconnect: boolean;
|
autoReconnect: boolean;
|
||||||
|
profiles: SshConnectionProfile[];
|
||||||
|
lastActiveContextId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AppConfig {
|
export interface AppConfig {
|
||||||
|
|
@ -247,6 +250,8 @@ const DEFAULT_CONFIG: AppConfig = {
|
||||||
ssh: {
|
ssh: {
|
||||||
lastConnection: null,
|
lastConnection: null,
|
||||||
autoReconnect: false,
|
autoReconnect: false,
|
||||||
|
profiles: [],
|
||||||
|
lastActiveContextId: 'local',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -664,6 +669,77 @@ export class ConfigManager {
|
||||||
this.saveConfig();
|
this.saveConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// SSH Profile Management
|
||||||
|
// ===========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an SSH connection profile.
|
||||||
|
* @param profile - The SSH connection profile to add
|
||||||
|
*/
|
||||||
|
addSshProfile(profile: SshConnectionProfile): void {
|
||||||
|
// Check for duplicates by ID
|
||||||
|
if (this.config.ssh.profiles.some((p) => p.id === profile.id)) {
|
||||||
|
logger.warn(`SSH profile with ID ${profile.id} already exists`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.config.ssh.profiles.push(profile);
|
||||||
|
this.saveConfig();
|
||||||
|
logger.info(`SSH profile added: ${profile.name} (${profile.id})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an SSH connection profile by ID.
|
||||||
|
* @param profileId - The profile ID to remove
|
||||||
|
*/
|
||||||
|
removeSshProfile(profileId: string): void {
|
||||||
|
const index = this.config.ssh.profiles.findIndex((p) => p.id === profileId);
|
||||||
|
if (index === -1) {
|
||||||
|
logger.warn(`SSH profile not found: ${profileId}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const removed = this.config.ssh.profiles.splice(index, 1)[0];
|
||||||
|
this.saveConfig();
|
||||||
|
logger.info(`SSH profile removed: ${removed.name} (${profileId})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates an existing SSH connection profile.
|
||||||
|
* @param profileId - The profile ID to update
|
||||||
|
* @param updates - Partial profile data to merge
|
||||||
|
*/
|
||||||
|
updateSshProfile(profileId: string, updates: Partial<SshConnectionProfile>): void {
|
||||||
|
const profile = this.config.ssh.profiles.find((p) => p.id === profileId);
|
||||||
|
if (!profile) {
|
||||||
|
logger.warn(`SSH profile not found: ${profileId}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(profile, updates);
|
||||||
|
this.saveConfig();
|
||||||
|
logger.info(`SSH profile updated: ${profile.name} (${profileId})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all SSH connection profiles.
|
||||||
|
* @returns Array of SSH connection profiles
|
||||||
|
*/
|
||||||
|
getSshProfiles(): SshConnectionProfile[] {
|
||||||
|
return this.deepClone(this.config.ssh.profiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the last active context ID (for restoration on app restart).
|
||||||
|
* @param contextId - The context ID that was active
|
||||||
|
*/
|
||||||
|
setLastActiveContextId(contextId: string): void {
|
||||||
|
this.config.ssh.lastActiveContextId = contextId;
|
||||||
|
this.saveConfig();
|
||||||
|
logger.info(`Last active context ID saved: ${contextId}`);
|
||||||
|
}
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
// Utility Methods
|
// Utility Methods
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,10 @@ import { WINDOW_ZOOM_FACTOR_CHANGED_CHANNEL } from '@shared/constants';
|
||||||
import { contextBridge, ipcRenderer } from 'electron';
|
import { contextBridge, ipcRenderer } from 'electron';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
CONTEXT_CHANGED,
|
||||||
|
CONTEXT_GET_ACTIVE,
|
||||||
|
CONTEXT_LIST,
|
||||||
|
CONTEXT_SWITCH,
|
||||||
SSH_CONNECT,
|
SSH_CONNECT,
|
||||||
SSH_DISCONNECT,
|
SSH_DISCONNECT,
|
||||||
SSH_GET_CONFIG_HOSTS,
|
SSH_GET_CONFIG_HOSTS,
|
||||||
|
|
@ -38,6 +42,7 @@ import {
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
AppConfig,
|
AppConfig,
|
||||||
|
ContextInfo,
|
||||||
ElectronAPI,
|
ElectronAPI,
|
||||||
NotificationTrigger,
|
NotificationTrigger,
|
||||||
SessionsPaginationOptions,
|
SessionsPaginationOptions,
|
||||||
|
|
@ -363,6 +368,31 @@ const electronAPI: ElectronAPI = {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Context API
|
||||||
|
context: {
|
||||||
|
list: async (): Promise<ContextInfo[]> => {
|
||||||
|
return invokeIpcWithResult<ContextInfo[]>(CONTEXT_LIST);
|
||||||
|
},
|
||||||
|
getActive: async (): Promise<string> => {
|
||||||
|
return invokeIpcWithResult<string>(CONTEXT_GET_ACTIVE);
|
||||||
|
},
|
||||||
|
switch: async (contextId: string): Promise<{ contextId: string }> => {
|
||||||
|
return invokeIpcWithResult<{ contextId: string }>(CONTEXT_SWITCH, contextId);
|
||||||
|
},
|
||||||
|
onChanged: (callback: (event: unknown, data: ContextInfo) => void): (() => void) => {
|
||||||
|
ipcRenderer.on(
|
||||||
|
CONTEXT_CHANGED,
|
||||||
|
callback as (event: Electron.IpcRendererEvent, ...args: unknown[]) => void
|
||||||
|
);
|
||||||
|
return (): void => {
|
||||||
|
ipcRenderer.removeListener(
|
||||||
|
CONTEXT_CHANGED,
|
||||||
|
callback as (event: Electron.IpcRendererEvent, ...args: unknown[]) => void
|
||||||
|
);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Use contextBridge to securely expose the API to the renderer process
|
// Use contextBridge to securely expose the API to the renderer process
|
||||||
|
|
|
||||||
|
|
@ -144,6 +144,18 @@ export interface UpdaterAPI {
|
||||||
onStatus: (callback: (event: unknown, status: unknown) => void) => () => void;
|
onStatus: (callback: (event: unknown, status: unknown) => void) => () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Context API
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context information for listing available contexts.
|
||||||
|
*/
|
||||||
|
export interface ContextInfo {
|
||||||
|
id: string;
|
||||||
|
type: 'local' | 'ssh';
|
||||||
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// SSH API
|
// SSH API
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
@ -314,6 +326,14 @@ export interface ElectronAPI {
|
||||||
|
|
||||||
// SSH API
|
// SSH API
|
||||||
ssh: SshAPI;
|
ssh: SshAPI;
|
||||||
|
|
||||||
|
// Context API
|
||||||
|
context: {
|
||||||
|
list: () => Promise<ContextInfo[]>;
|
||||||
|
getActive: () => Promise<string>;
|
||||||
|
switch: (contextId: string) => Promise<{ contextId: string }>;
|
||||||
|
onChanged: (callback: (event: unknown, data: ContextInfo) => void) => () => void;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue