update
This commit is contained in:
@@ -20,9 +20,10 @@ const auditLogService = new AuditLogService(); // 实例化 AuditLogService
|
||||
export const createConnection = async (req: Request, res: Response): Promise<void> => {
|
||||
try {
|
||||
// 基本输入验证(更复杂的验证可以在服务层或使用中间件)
|
||||
const { name, host, username, auth_method, password, private_key } = req.body;
|
||||
if (!name || !host || !username || !auth_method) {
|
||||
res.status(400).json({ message: '缺少必要的连接信息 (name, host, username, auth_method)。' });
|
||||
// 移除控制器层对 name 的验证,服务层会处理
|
||||
const { host, username, auth_method, password, private_key } = req.body;
|
||||
if (!host || !username || !auth_method) { // 移除 !name 检查
|
||||
res.status(400).json({ message: '缺少必要的连接信息 (host, username, auth_method)。' }); // 更新错误消息
|
||||
return;
|
||||
}
|
||||
if (auth_method === 'password' && !password) {
|
||||
|
||||
@@ -88,7 +88,7 @@ CREATE TABLE IF NOT EXISTS proxies (
|
||||
const createConnectionsTableSQL = `
|
||||
CREATE TABLE IF NOT EXISTS connections (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
name TEXT NULL, -- 允许 name 为空
|
||||
host TEXT NOT NULL,
|
||||
port INTEGER NOT NULL,
|
||||
username TEXT NOT NULL,
|
||||
|
||||
@@ -7,7 +7,7 @@ const db = getDb();
|
||||
// 注意:这里不包含加密字段,因为 Repository 不应处理解密
|
||||
interface ConnectionBase {
|
||||
id: number;
|
||||
name: string;
|
||||
name: string | null; // 允许 name 为 null
|
||||
host: string;
|
||||
port: number;
|
||||
username: string;
|
||||
@@ -126,15 +126,17 @@ export const findFullConnectionById = async (id: number): Promise<any | null> =>
|
||||
/**
|
||||
* 创建新连接
|
||||
*/
|
||||
export const createConnection = async (data: Omit<FullConnectionData, 'id' | 'created_at' | 'updated_at' | 'last_connected_at'>): Promise<number> => {
|
||||
// Update function signature to accept name as string | null
|
||||
export const createConnection = async (data: Omit<FullConnectionData, 'id' | 'created_at' | 'updated_at' | 'last_connected_at'> & { name: string | null }): Promise<number> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const stmt = db.prepare(
|
||||
`INSERT INTO connections (name, host, port, username, auth_method, encrypted_password, encrypted_private_key, encrypted_passphrase, proxy_id, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
||||
);
|
||||
stmt.run(
|
||||
data.name, data.host, data.port, data.username, data.auth_method,
|
||||
data.name ?? null, // Ensure null is passed if name is null/undefined
|
||||
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,
|
||||
now, now,
|
||||
@@ -153,7 +155,8 @@ export const createConnection = async (data: Omit<FullConnectionData, 'id' | 'cr
|
||||
/**
|
||||
* 更新连接信息
|
||||
*/
|
||||
export const updateConnection = async (id: number, data: Partial<Omit<FullConnectionData, 'id' | 'created_at' | 'last_connected_at'>>): Promise<boolean> => {
|
||||
// Update function signature to accept name as string | null | undefined
|
||||
export const updateConnection = async (id: number, data: Partial<Omit<FullConnectionData, 'id' | 'created_at' | 'last_connected_at'> & { name?: string | null }>): Promise<boolean> => {
|
||||
const fieldsToUpdate: { [key: string]: any } = { ...data };
|
||||
const params: any[] = [];
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { encrypt, decrypt } from '../utils/crypto';
|
||||
// For now, let's reuse the interfaces from the repository (adjust as needed)
|
||||
export interface ConnectionBase {
|
||||
id: number;
|
||||
name: string;
|
||||
name: string | null; // Allow name to be null
|
||||
host: string;
|
||||
port: number;
|
||||
username: string;
|
||||
@@ -23,7 +23,7 @@ export interface ConnectionWithTags extends ConnectionBase {
|
||||
|
||||
// Input type for creating a connection (from controller)
|
||||
export interface CreateConnectionInput {
|
||||
name: string;
|
||||
name?: string; // Name is now optional
|
||||
host: string;
|
||||
port?: number; // Optional, defaults in service/repo
|
||||
username: string;
|
||||
@@ -70,8 +70,9 @@ export const getConnectionById = async (id: number): Promise<ConnectionWithTags
|
||||
*/
|
||||
export const createConnection = async (input: CreateConnectionInput): Promise<ConnectionWithTags> => {
|
||||
// 1. Validate input (basic validation, more complex validation can be added)
|
||||
if (!input.name || !input.host || !input.username || !input.auth_method) {
|
||||
throw new Error('缺少必要的连接信息 (name, host, username, auth_method)。');
|
||||
// Removed name validation: if (!input.name || !input.host || !input.username || !input.auth_method) {
|
||||
if (!input.host || !input.username || !input.auth_method) { // Validate required fields except name
|
||||
throw new Error('缺少必要的连接信息 (host, username, auth_method)。');
|
||||
}
|
||||
if (input.auth_method === 'password' && !input.password) {
|
||||
throw new Error('密码认证方式需要提供 password。');
|
||||
@@ -97,7 +98,7 @@ export const createConnection = async (input: CreateConnectionInput): Promise<Co
|
||||
|
||||
// 3. Prepare data for repository
|
||||
const connectionData = {
|
||||
name: input.name,
|
||||
name: input.name || '', // Use empty string '' if name is empty or undefined
|
||||
host: input.host,
|
||||
port: input.port ?? 22, // Default port
|
||||
username: input.username,
|
||||
@@ -142,7 +143,7 @@ export const updateConnection = async (id: number, input: UpdateConnectionInput)
|
||||
let newAuthMethod = input.auth_method || currentFullConnection.auth_method;
|
||||
|
||||
// Update non-credential fields
|
||||
if (input.name !== undefined) dataToUpdate.name = input.name;
|
||||
if (input.name !== undefined) dataToUpdate.name = input.name || ''; // Use empty string '' if name is empty string or null/undefined
|
||||
if (input.host !== undefined) dataToUpdate.host = input.host;
|
||||
if (input.port !== undefined) dataToUpdate.port = input.port;
|
||||
if (input.username !== undefined) dataToUpdate.username = input.username;
|
||||
|
||||
@@ -223,8 +223,17 @@ export class StatusMonitorService {
|
||||
* @returns Promise<NetworkStats | null> 解析后的网络统计信息或 null
|
||||
*/
|
||||
private async parseProcNetDev(sshClient: Client): Promise<NetworkStats | null> {
|
||||
let output: string;
|
||||
try {
|
||||
// 将命令执行放入 try...catch
|
||||
output = await this.executeSshCommand(sshClient, 'cat /proc/net/dev');
|
||||
} catch (error) {
|
||||
// 如果命令失败,记录警告并返回 null
|
||||
console.warn("[StatusMonitor] Failed to execute 'cat /proc/net/dev':", error);
|
||||
return null;
|
||||
}
|
||||
// 如果命令成功,继续解析
|
||||
try {
|
||||
const output = await this.executeSshCommand(sshClient, 'cat /proc/net/dev');
|
||||
const lines = output.split('\n').slice(2); // Skip header lines
|
||||
const stats: NetworkStats = {};
|
||||
for (const line of lines) {
|
||||
@@ -238,8 +247,9 @@ export class StatusMonitorService {
|
||||
}
|
||||
}
|
||||
return Object.keys(stats).length > 0 ? stats : null;
|
||||
} catch (error) {
|
||||
console.error("[StatusMonitor] Error parsing /proc/net/dev:", error);
|
||||
} catch (parseError) {
|
||||
// 如果解析失败,记录错误并返回 null
|
||||
console.error("[StatusMonitor] Error parsing /proc/net/dev output:", parseError);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -253,14 +263,18 @@ export class StatusMonitorService {
|
||||
try {
|
||||
// 使用 ip route 命令查找默认路由对应的接口
|
||||
const output = await this.executeSshCommand(sshClient, "ip route get 1.1.1.1 | grep -oP 'dev\\s+\\K\\S+'");
|
||||
return output.trim() || null;
|
||||
const interfaceName = output.trim();
|
||||
if (interfaceName) return interfaceName;
|
||||
// 如果 ip route 没返回有效接口名,也尝试 fallback
|
||||
console.warn("[StatusMonitor] 'ip route' did not return a valid interface name. Falling back...");
|
||||
|
||||
} catch (error) {
|
||||
console.warn("[StatusMonitor] Failed to get default interface using 'ip route':", error);
|
||||
// Fallback: 尝试查找第一个非 lo 接口
|
||||
try {
|
||||
const netDevOutput = await this.executeSshCommand(sshClient, 'cat /proc/net/dev');
|
||||
const lines = netDevOutput.split('\n').slice(2);
|
||||
for (const line of lines) {
|
||||
console.warn("[StatusMonitor] Failed to get default interface using 'ip route', falling back:", error);
|
||||
// Fallback: 尝试查找第一个非 lo 接口
|
||||
try {
|
||||
const netDevOutput = await this.executeSshCommand(sshClient, 'cat /proc/net/dev');
|
||||
const lines = netDevOutput.split('\n').slice(2);
|
||||
for (const line of lines) {
|
||||
const iface = line.trim().split(':')[0];
|
||||
if (iface && iface !== 'lo') {
|
||||
return iface;
|
||||
@@ -269,8 +283,12 @@ export class StatusMonitorService {
|
||||
} catch (fallbackError) {
|
||||
console.error("[StatusMonitor] Failed to fallback to /proc/net/dev for interface:", fallbackError);
|
||||
}
|
||||
// Ensure null is returned if both primary and fallback fail within the outer catch
|
||||
return null;
|
||||
}
|
||||
// This part should ideally not be reached if the first try succeeded or the catch block returned.
|
||||
// Adding a final return null for safety and to satisfy TS if logic paths are complex.
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user