update
This commit is contained in:
@@ -76,6 +76,7 @@ export const createConnectionsTableSQL = `
|
||||
CREATE TABLE IF NOT EXISTS connections (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NULL, -- 允许 name 为空
|
||||
type TEXT NOT NULL CHECK(type IN ('SSH', 'RDP')) DEFAULT 'SSH',
|
||||
host TEXT NOT NULL,
|
||||
port INTEGER NOT NULL,
|
||||
username TEXT NOT NULL,
|
||||
|
||||
@@ -7,6 +7,7 @@ import { getDbInstance, runDb, getDb as getDbRow, allDb } from '../database/conn
|
||||
interface ConnectionBase {
|
||||
id: number;
|
||||
name: string | null;
|
||||
type: 'SSH' | 'RDP'; // Add type field
|
||||
host: string;
|
||||
port: number;
|
||||
username: string;
|
||||
@@ -17,15 +18,18 @@ interface ConnectionBase {
|
||||
last_connected_at: number | null;
|
||||
}
|
||||
|
||||
// ConnectionWithTagsRow implicitly includes 'type' via ConnectionBase
|
||||
interface ConnectionWithTagsRow extends ConnectionBase {
|
||||
tag_ids_str: string | null;
|
||||
tag_ids_str: string | null;
|
||||
}
|
||||
|
||||
// ConnectionWithTags implicitly includes 'type' via ConnectionBase
|
||||
export interface ConnectionWithTags extends ConnectionBase {
|
||||
tag_ids: number[];
|
||||
}
|
||||
|
||||
// 包含加密字段的完整类型,用于插入/更新
|
||||
// FullConnectionData implicitly includes 'type' via ConnectionBase
|
||||
export interface FullConnectionData extends ConnectionBase {
|
||||
encrypted_password?: string | null;
|
||||
encrypted_private_key?: string | null;
|
||||
@@ -34,6 +38,7 @@ export interface FullConnectionData extends ConnectionBase {
|
||||
}
|
||||
|
||||
|
||||
// FullConnectionDbRow implicitly includes 'type' via FullConnectionData
|
||||
interface FullConnectionDbRow extends FullConnectionData {
|
||||
proxy_db_id: number | null;
|
||||
proxy_name: string | null;
|
||||
@@ -53,7 +58,7 @@ interface FullConnectionDbRow extends FullConnectionData {
|
||||
export const findAllConnectionsWithTags = async (): Promise<ConnectionWithTags[]> => {
|
||||
const sql = `
|
||||
SELECT
|
||||
c.id, c.name, c.host, c.port, c.username, c.auth_method, c.proxy_id,
|
||||
c.id, c.name, c.type, c.host, c.port, c.username, c.auth_method, c.proxy_id,
|
||||
c.created_at, c.updated_at, c.last_connected_at,
|
||||
GROUP_CONCAT(ct.tag_id) as tag_ids_str
|
||||
FROM connections c
|
||||
@@ -79,7 +84,7 @@ export const findAllConnectionsWithTags = async (): Promise<ConnectionWithTags[]
|
||||
export const findConnectionByIdWithTags = async (id: number): Promise<ConnectionWithTags | null> => {
|
||||
const sql = `
|
||||
SELECT
|
||||
c.id, c.name, c.host, c.port, c.username, c.auth_method, c.proxy_id,
|
||||
c.id, c.name, c.type, c.host, c.port, c.username, c.auth_method, c.proxy_id,
|
||||
c.created_at, c.updated_at, c.last_connected_at,
|
||||
GROUP_CONCAT(ct.tag_id) as tag_ids_str
|
||||
FROM connections c
|
||||
@@ -132,13 +137,15 @@ export const findFullConnectionById = async (id: number): Promise<FullConnection
|
||||
/**
|
||||
* 创建新连接 (不处理标签)
|
||||
*/
|
||||
// Update input type to reflect FullConnectionData now has 'type'
|
||||
export const createConnection = async (data: Omit<FullConnectionData, 'id' | 'created_at' | 'updated_at' | 'last_connected_at' | 'tag_ids'>): Promise<number> => {
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const sql = `
|
||||
INSERT INTO connections (name, host, port, username, auth_method, encrypted_password, encrypted_private_key, encrypted_passphrase, proxy_id, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
|
||||
INSERT INTO connections (name, type, host, port, username, auth_method, encrypted_password, encrypted_private_key, encrypted_passphrase, proxy_id, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`; // Add type column and placeholder
|
||||
const params = [
|
||||
data.name ?? null,
|
||||
data.type, // Add type parameter
|
||||
data.host, data.port, data.username, data.auth_method,
|
||||
data.encrypted_password ?? null, data.encrypted_private_key ?? null, data.encrypted_passphrase ?? null,
|
||||
data.proxy_id ?? null,
|
||||
@@ -160,6 +167,7 @@ export const createConnection = async (data: Omit<FullConnectionData, 'id' | 'cr
|
||||
/**
|
||||
* 更新连接信息 (不处理标签)
|
||||
*/
|
||||
// Update input type to reflect FullConnectionData now has 'type'
|
||||
export const updateConnection = async (id: number, data: Partial<Omit<FullConnectionData, 'id' | 'created_at' | 'last_connected_at' | 'tag_ids'>>): Promise<boolean> => {
|
||||
const fieldsToUpdate: { [key: string]: any } = { ...data };
|
||||
const params: any[] = [];
|
||||
@@ -270,17 +278,18 @@ export const updateConnectionTags = async (connectionId: number, tagIds: number[
|
||||
*/
|
||||
export const bulkInsertConnections = async (
|
||||
db: Database,
|
||||
// Update input type to reflect FullConnectionData now has 'type'
|
||||
connections: Array<Omit<FullConnectionData, 'id' | 'created_at' | 'updated_at' | 'last_connected_at'> & { tag_ids?: number[] }>
|
||||
): Promise<{ connectionId: number, originalData: any }[]> => {
|
||||
|
||||
const insertConnSql = `INSERT INTO connections (name, host, port, username, auth_method, encrypted_password, encrypted_private_key, encrypted_passphrase, proxy_id, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
|
||||
const insertConnSql = `INSERT INTO connections (name, type, host, port, username, auth_method, encrypted_password, encrypted_private_key, encrypted_passphrase, proxy_id, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`; // Add type column and placeholder
|
||||
const results: { connectionId: number, originalData: any }[] = [];
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
|
||||
|
||||
for (const connData of connections) {
|
||||
const params = [
|
||||
connData.name ?? null, connData.host, connData.port, connData.username, connData.auth_method,
|
||||
connData.name ?? null, connData.type, connData.host, connData.port, connData.username, connData.auth_method, // Add type parameter
|
||||
connData.encrypted_password || null,
|
||||
connData.encrypted_private_key || null,
|
||||
connData.encrypted_passphrase || null,
|
||||
|
||||
@@ -18,57 +18,93 @@ const auditLogService = new AuditLogService(); // 实例化 AuditLogService
|
||||
* 获取所有连接(包含标签)
|
||||
*/
|
||||
export const getAllConnections = async (): Promise<ConnectionWithTags[]> => {
|
||||
return ConnectionRepository.findAllConnectionsWithTags();
|
||||
// Repository now returns ConnectionWithTags including 'type'
|
||||
// Explicit type assertion to ensure compatibility
|
||||
return ConnectionRepository.findAllConnectionsWithTags() as Promise<ConnectionWithTags[]>;
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据 ID 获取单个连接(包含标签)
|
||||
*/
|
||||
export const getConnectionById = async (id: number): Promise<ConnectionWithTags | null> => {
|
||||
return ConnectionRepository.findConnectionByIdWithTags(id);
|
||||
// Repository now returns ConnectionWithTags including 'type'
|
||||
// Explicit type assertion to ensure compatibility
|
||||
return ConnectionRepository.findConnectionByIdWithTags(id) as Promise<ConnectionWithTags | null>;
|
||||
};
|
||||
|
||||
/**
|
||||
* 创建新连接
|
||||
*/
|
||||
export const createConnection = async (input: CreateConnectionInput): Promise<ConnectionWithTags> => {
|
||||
// 1. 验证输入
|
||||
if (!input.host || !input.username || !input.auth_method) {
|
||||
throw new Error('缺少必要的连接信息 (host, username, auth_method)。');
|
||||
console.log('[Service:createConnection] Received input:', JSON.stringify(input, null, 2)); // Log input
|
||||
// 1. 验证输入 (包含 type)
|
||||
// Convert type to uppercase for validation and consistency
|
||||
const connectionType = input.type?.toUpperCase() as 'SSH' | 'RDP' | undefined; // Ensure type safety
|
||||
if (!connectionType || !['SSH', 'RDP'].includes(connectionType)) {
|
||||
throw new Error('必须提供有效的连接类型 (SSH 或 RDP)。');
|
||||
}
|
||||
if (input.auth_method === 'password' && !input.password) {
|
||||
throw new Error('密码认证方式需要提供 password。');
|
||||
if (!input.host || !input.username) {
|
||||
throw new Error('缺少必要的连接信息 (host, username)。');
|
||||
}
|
||||
if (input.auth_method === 'key' && !input.private_key) {
|
||||
throw new Error('密钥认证方式需要提供 private_key。');
|
||||
// Type-specific validation using the uppercase version
|
||||
if (connectionType === 'SSH') {
|
||||
if (!input.auth_method || !['password', 'key'].includes(input.auth_method)) {
|
||||
throw new Error('SSH 连接必须提供有效的认证方式 (password 或 key)。');
|
||||
}
|
||||
if (input.auth_method === 'password' && !input.password) {
|
||||
throw new Error('SSH 密码认证方式需要提供 password。');
|
||||
}
|
||||
if (input.auth_method === 'key' && !input.private_key) {
|
||||
throw new Error('SSH 密钥认证方式需要提供 private_key。');
|
||||
}
|
||||
} else if (connectionType === 'RDP') {
|
||||
if (!input.password) {
|
||||
throw new Error('RDP 连接需要提供 password。');
|
||||
}
|
||||
// For RDP, we'll ignore auth_method, private_key, passphrase from input if provided
|
||||
}
|
||||
|
||||
// 2. 加密凭证
|
||||
// 2. 加密凭证 (根据 type)
|
||||
let encryptedPassword = null;
|
||||
let encryptedPrivateKey = null;
|
||||
let encryptedPassphrase = null;
|
||||
// Default to 'password' for DB compatibility, especially for RDP
|
||||
let authMethodForDb: 'password' | 'key' = 'password';
|
||||
|
||||
if (input.auth_method === 'password') {
|
||||
encryptedPassword = encrypt(input.password!);
|
||||
} else if (input.auth_method === 'key') {
|
||||
encryptedPrivateKey = encrypt(input.private_key!);
|
||||
if (input.passphrase) {
|
||||
encryptedPassphrase = encrypt(input.passphrase);
|
||||
if (connectionType === 'SSH') {
|
||||
authMethodForDb = input.auth_method!; // Already validated above
|
||||
if (input.auth_method === 'password') {
|
||||
encryptedPassword = encrypt(input.password!);
|
||||
} else { // key
|
||||
encryptedPrivateKey = encrypt(input.private_key!);
|
||||
if (input.passphrase) {
|
||||
encryptedPassphrase = encrypt(input.passphrase);
|
||||
}
|
||||
}
|
||||
} else { // RDP (connectionType is 'RDP')
|
||||
encryptedPassword = encrypt(input.password!);
|
||||
// authMethodForDb remains 'password' for RDP to satisfy DB constraint
|
||||
// Ensure SSH specific fields are null for RDP
|
||||
encryptedPrivateKey = null;
|
||||
encryptedPassphrase = null;
|
||||
}
|
||||
|
||||
// 3. 准备仓库数据
|
||||
const connectionData = {
|
||||
name: input.name || '', // 如果 name 为空或 undefined,则使用空字符串 ''
|
||||
const defaultPort = input.type === 'RDP' ? 3389 : 22;
|
||||
// Explicitly type the object being passed to the repository
|
||||
const connectionData: Omit<ConnectionRepository.FullConnectionData, 'id' | 'created_at' | 'updated_at' | 'last_connected_at' | 'tag_ids'> = {
|
||||
name: input.name || '',
|
||||
type: connectionType, // Use the validated uppercase type
|
||||
host: input.host,
|
||||
port: input.port ?? 22, // 默认端口
|
||||
port: input.port ?? defaultPort, // Use type-specific default port
|
||||
username: input.username,
|
||||
auth_method: input.auth_method,
|
||||
auth_method: authMethodForDb, // Use determined auth method
|
||||
encrypted_password: encryptedPassword,
|
||||
encrypted_private_key: encryptedPrivateKey,
|
||||
encrypted_passphrase: encryptedPassphrase,
|
||||
encrypted_private_key: encryptedPrivateKey, // Will be null for RDP
|
||||
encrypted_passphrase: encryptedPassphrase, // Will be null for RDP
|
||||
proxy_id: input.proxy_id ?? null,
|
||||
};
|
||||
console.log('[Service:createConnection] Data to be saved:', JSON.stringify(connectionData, null, 2)); // Log data before saving
|
||||
|
||||
// 4. 在仓库中创建连接记录
|
||||
const newConnectionId = await ConnectionRepository.createConnection(connectionData);
|
||||
@@ -86,7 +122,7 @@ export const createConnection = async (input: CreateConnectionInput): Promise<Co
|
||||
console.error(`[Audit Log Error] Failed to retrieve connection ${newConnectionId} after creation.`);
|
||||
throw new Error('创建连接后无法检索到该连接。');
|
||||
}
|
||||
auditLogService.logAction('CONNECTION_CREATED', { connectionId: newConnection.id, name: newConnection.name, host: newConnection.host });
|
||||
auditLogService.logAction('CONNECTION_CREATED', { connectionId: newConnection.id, type: newConnection.type, name: newConnection.name, host: newConnection.host }); // Add type to audit log
|
||||
|
||||
// 7. 返回新创建的带标签的连接
|
||||
return newConnection;
|
||||
@@ -103,57 +139,85 @@ export const updateConnection = async (id: number, input: UpdateConnectionInput)
|
||||
}
|
||||
|
||||
// 2. 准备更新数据
|
||||
const dataToUpdate: Partial<ConnectionRepository.FullConnectionData> = {};
|
||||
// Explicitly type dataToUpdate to match the repository's expected input
|
||||
const dataToUpdate: Partial<Omit<ConnectionRepository.FullConnectionData, 'id' | 'created_at' | 'last_connected_at' | 'tag_ids'>> = {};
|
||||
let needsCredentialUpdate = false;
|
||||
let newAuthMethod = input.auth_method || currentFullConnection.auth_method;
|
||||
// Determine the final type, converting input type to uppercase if provided
|
||||
const targetType = input.type?.toUpperCase() as 'SSH' | 'RDP' | undefined || currentFullConnection.type;
|
||||
|
||||
// 更新非凭证字段
|
||||
if (input.name !== undefined) dataToUpdate.name = input.name || ''; // 如果 name 是空字符串或 null/undefined,则使用空字符串 ''
|
||||
if (input.name !== undefined) dataToUpdate.name = input.name || '';
|
||||
// Update type if changed, using the uppercase version
|
||||
if (input.type !== undefined && targetType !== currentFullConnection.type) dataToUpdate.type = targetType;
|
||||
if (input.host !== undefined) dataToUpdate.host = input.host;
|
||||
if (input.port !== undefined) dataToUpdate.port = input.port;
|
||||
if (input.username !== undefined) dataToUpdate.username = input.username;
|
||||
if (input.proxy_id !== undefined) dataToUpdate.proxy_id = input.proxy_id; // 允许设置为 null
|
||||
if (input.proxy_id !== undefined) dataToUpdate.proxy_id = input.proxy_id;
|
||||
|
||||
// 处理认证方法更改或凭证更新
|
||||
if (input.auth_method && input.auth_method !== currentFullConnection.auth_method) {
|
||||
// 认证方法已更改
|
||||
dataToUpdate.auth_method = input.auth_method;
|
||||
needsCredentialUpdate = true;
|
||||
if (input.auth_method === 'password') {
|
||||
if (!input.password) throw new Error('切换到密码认证时需要提供 password。');
|
||||
dataToUpdate.encrypted_password = encrypt(input.password);
|
||||
// 处理认证方法更改或凭证更新 (根据 targetType)
|
||||
// Use the validated targetType for logic
|
||||
if (targetType === 'SSH') {
|
||||
const currentAuthMethod = currentFullConnection.auth_method;
|
||||
const inputAuthMethod = input.auth_method;
|
||||
|
||||
// Determine the final auth method for SSH
|
||||
const finalAuthMethod = inputAuthMethod || currentAuthMethod;
|
||||
if (finalAuthMethod !== currentAuthMethod) {
|
||||
dataToUpdate.auth_method = finalAuthMethod; // Update auth_method if it changed
|
||||
}
|
||||
|
||||
if (finalAuthMethod === 'password') {
|
||||
// If switching to password or updating password
|
||||
if (input.password !== undefined) { // Check if password was provided in input
|
||||
if (!input.password && finalAuthMethod !== currentAuthMethod) {
|
||||
// Switching to password requires a password
|
||||
throw new Error('切换到密码认证时需要提供 password。');
|
||||
}
|
||||
// Encrypt if password is not empty, otherwise set to null (to clear)
|
||||
dataToUpdate.encrypted_password = input.password ? encrypt(input.password) : null;
|
||||
needsCredentialUpdate = true;
|
||||
}
|
||||
// When switching to password, clear key fields
|
||||
if (finalAuthMethod !== currentAuthMethod) {
|
||||
dataToUpdate.encrypted_private_key = null;
|
||||
dataToUpdate.encrypted_passphrase = null;
|
||||
}
|
||||
} else { // finalAuthMethod is 'key'
|
||||
let keyUpdated = false;
|
||||
// If switching to key or updating key
|
||||
if (input.private_key !== undefined) {
|
||||
if (!input.private_key && finalAuthMethod !== currentAuthMethod) {
|
||||
// Switching to key requires a private key
|
||||
throw new Error('切换到密钥认证时需要提供 private_key。');
|
||||
}
|
||||
// Encrypt if key is not empty, otherwise set to null (to clear)
|
||||
dataToUpdate.encrypted_private_key = input.private_key ? encrypt(input.private_key) : null;
|
||||
needsCredentialUpdate = true;
|
||||
keyUpdated = true;
|
||||
}
|
||||
// Update passphrase only if key was updated OR passphrase itself was provided
|
||||
if (keyUpdated || input.passphrase !== undefined) {
|
||||
// Encrypt if passphrase is not empty, otherwise set to null (to clear)
|
||||
dataToUpdate.encrypted_passphrase = input.passphrase ? encrypt(input.passphrase) : null;
|
||||
needsCredentialUpdate = true; // Consider passphrase change a credential update
|
||||
}
|
||||
// When switching to key, clear password field
|
||||
if (finalAuthMethod !== currentAuthMethod) {
|
||||
dataToUpdate.encrypted_password = null;
|
||||
}
|
||||
}
|
||||
} else { // targetType is 'RDP'
|
||||
// RDP only uses password
|
||||
if (input.password !== undefined) { // Check if password was provided
|
||||
// Encrypt if password is not empty, otherwise set to null (to clear)
|
||||
dataToUpdate.encrypted_password = input.password ? encrypt(input.password) : null;
|
||||
needsCredentialUpdate = true;
|
||||
}
|
||||
// Ensure SSH specific fields are nullified if switching to RDP or updating RDP
|
||||
if (targetType !== currentFullConnection.type || needsCredentialUpdate) {
|
||||
dataToUpdate.auth_method = 'password'; // RDP uses password auth method in DB
|
||||
dataToUpdate.encrypted_private_key = null;
|
||||
dataToUpdate.encrypted_passphrase = null;
|
||||
} else { // 密钥
|
||||
if (!input.private_key) throw new Error('切换到密钥认证时需要提供 private_key。');
|
||||
dataToUpdate.encrypted_private_key = encrypt(input.private_key);
|
||||
// 仅当密码短语为非空字符串时才加密
|
||||
dataToUpdate.encrypted_passphrase = (input.passphrase && input.passphrase.trim() !== '') ? encrypt(input.passphrase) : null;
|
||||
dataToUpdate.encrypted_password = null;
|
||||
}
|
||||
} else {
|
||||
// 认证方法未更改,检查是否提供了当前方法的凭证
|
||||
// 仅当提供了非空字符串时才加密和更新
|
||||
if (newAuthMethod === 'password' && input.password && input.password.trim() !== '') {
|
||||
dataToUpdate.encrypted_password = encrypt(input.password);
|
||||
needsCredentialUpdate = true;
|
||||
} else if (newAuthMethod === 'key') {
|
||||
let passphraseChanged = false;
|
||||
if (input.private_key && input.private_key.trim() !== '') {
|
||||
dataToUpdate.encrypted_private_key = encrypt(input.private_key);
|
||||
// 如果私钥更新,则必须更新(或清除)密码短语
|
||||
// 仅当非空时加密,否则设置为 null
|
||||
dataToUpdate.encrypted_passphrase = (input.passphrase && input.passphrase.trim() !== '') ? encrypt(input.passphrase) : null;
|
||||
needsCredentialUpdate = true;
|
||||
passphraseChanged = true; // 如果密钥更改,则将密码短语标记为已处理
|
||||
}
|
||||
// 处理仅更改密码短语(且密钥未更改)的情况
|
||||
// 检查 input.passphrase 是否已定义(可能是空字符串以清除)
|
||||
if (!passphraseChanged && input.passphrase !== undefined) {
|
||||
// 仅当非空时加密,否则设置为 null
|
||||
dataToUpdate.encrypted_passphrase = (input.passphrase && input.passphrase.trim() !== '') ? encrypt(input.passphrase) : null;
|
||||
needsCredentialUpdate = true; // 将此视为凭证更新
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +246,12 @@ export const updateConnection = async (id: number, input: UpdateConnectionInput)
|
||||
|
||||
// 5. 如果进行了任何更改,则记录审计操作
|
||||
if (hasNonTagChanges || input.tag_ids !== undefined) {
|
||||
auditLogService.logAction('CONNECTION_UPDATED', { connectionId: id, updatedFields: updatedFieldsForAudit });
|
||||
// Add type to audit log if it was updated
|
||||
const auditDetails: any = { connectionId: id, updatedFields: updatedFieldsForAudit };
|
||||
if (dataToUpdate.type) {
|
||||
auditDetails.newType = dataToUpdate.type;
|
||||
}
|
||||
auditLogService.logAction('CONNECTION_UPDATED', auditDetails);
|
||||
}
|
||||
|
||||
// 6. 获取并返回更新后的连接
|
||||
|
||||
@@ -7,6 +7,7 @@ import { getDbInstance, runDb, getDb as getDbRow, allDb } from '../database/conn
|
||||
|
||||
interface ImportedConnectionData {
|
||||
name: string;
|
||||
type: 'SSH' | 'RDP'; // Add type field
|
||||
host: string;
|
||||
port: number;
|
||||
username: string;
|
||||
@@ -42,10 +43,11 @@ export const exportConnections = async (): Promise<ExportedConnectionData[]> =>
|
||||
try {
|
||||
const db = await getDbInstance();
|
||||
|
||||
// Ensure ExportRow reflects the updated FullConnectionData (which now includes 'type')
|
||||
type ExportRow = ConnectionRepository.FullConnectionData & {
|
||||
proxy_db_id: number | null;
|
||||
proxy_name: string | null;
|
||||
proxy_type: 'SOCKS5' | 'HTTP' | null;
|
||||
proxy_type: 'SOCKS5' | 'HTTP' | null; // Proxy type remains the same
|
||||
proxy_host: string | null;
|
||||
proxy_port: number | null;
|
||||
proxy_username: string | null;
|
||||
@@ -85,7 +87,8 @@ export const exportConnections = async (): Promise<ExportedConnectionData[]> =>
|
||||
|
||||
const formattedData: ExportedConnectionData[] = connectionsWithProxies.map(row => {
|
||||
const connection: ExportedConnectionData = {
|
||||
name: row.name ?? 'Unnamed',
|
||||
name: row.name ?? 'Unnamed',
|
||||
type: row.type, // Add type field
|
||||
host: row.host,
|
||||
port: row.port,
|
||||
username: row.username,
|
||||
@@ -154,9 +157,18 @@ export const importConnections = async (fileBuffer: Buffer): Promise<ImportResul
|
||||
for (const connData of importedData) {
|
||||
try {
|
||||
|
||||
if (!connData.name || !connData.host || !connData.port || !connData.username || !connData.auth_method) {
|
||||
throw new Error('缺少必要的连接字段 (name, host, port, username, auth_method)。');
|
||||
// Validate imported data, including type
|
||||
if (!connData.type || !['SSH', 'RDP'].includes(connData.type)) {
|
||||
throw new Error('缺少或无效的连接类型 (type)。');
|
||||
}
|
||||
if (!connData.name || !connData.host || !connData.port || !connData.username) {
|
||||
throw new Error('缺少必要的连接字段 (name, host, port, username)。');
|
||||
}
|
||||
// Validate SSH specific fields only if type is SSH
|
||||
if (connData.type === 'SSH' && (!connData.auth_method || !['password', 'key'].includes(connData.auth_method))) {
|
||||
throw new Error('SSH 连接缺少有效的认证方式 (auth_method)。');
|
||||
}
|
||||
// RDP specific validation (e.g., password required) could be added here if needed
|
||||
|
||||
|
||||
let proxyIdToUse: number | null = null;
|
||||
@@ -192,12 +204,15 @@ export const importConnections = async (fileBuffer: Buffer): Promise<ImportResul
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare data for repository, ensuring correct auth_method for RDP
|
||||
const authMethodForDb = connData.type === 'RDP' ? 'password' : connData.auth_method!;
|
||||
connectionsToInsert.push({
|
||||
name: connData.name,
|
||||
type: connData.type, // Add type
|
||||
host: connData.host,
|
||||
port: connData.port,
|
||||
username: connData.username,
|
||||
auth_method: connData.auth_method,
|
||||
auth_method: authMethodForDb, // Use determined auth method
|
||||
encrypted_password: connData.encrypted_password || null,
|
||||
encrypted_private_key: connData.encrypted_private_key || null,
|
||||
encrypted_passphrase: connData.encrypted_passphrase || null,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
export interface ConnectionBase {
|
||||
id: number;
|
||||
name: string | null;
|
||||
type: 'SSH' | 'RDP';
|
||||
host: string;
|
||||
port: number;
|
||||
username: string;
|
||||
@@ -19,7 +20,8 @@ export interface ConnectionWithTags extends ConnectionBase {
|
||||
|
||||
|
||||
export interface CreateConnectionInput {
|
||||
name?: string;
|
||||
name?: string;
|
||||
type: 'SSH' | 'RDP';
|
||||
host: string;
|
||||
port?: number;
|
||||
username: string;
|
||||
@@ -34,6 +36,7 @@ export interface CreateConnectionInput {
|
||||
|
||||
export interface UpdateConnectionInput {
|
||||
name?: string;
|
||||
type?: 'SSH' | 'RDP';
|
||||
host?: string;
|
||||
port?: number;
|
||||
username?: string;
|
||||
@@ -49,6 +52,7 @@ export interface UpdateConnectionInput {
|
||||
export interface FullConnectionData {
|
||||
id: number;
|
||||
name: string | null;
|
||||
type: 'SSH' | 'RDP';
|
||||
host: string;
|
||||
port: number;
|
||||
username: string;
|
||||
|
||||
Reference in New Issue
Block a user