feat: 状态监视器添加IP地址显示
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { ipBlacklistService } from '../services/ip-blacklist.service';
|
||||
import { settingsService } from '../services/settings.service'; // <-- Import settingsService
|
||||
import { settingsService } from '../services/settings.service';
|
||||
|
||||
/**
|
||||
* IP 黑名单检查中间件
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Request, Response } from 'express';
|
||||
import * as ConnectionService from '../services/connection.service';
|
||||
import * as SshService from '../services/ssh.service';
|
||||
import * as GuacamoleService from '../services/guacamole.service'; // 导入 GuacamoleService
|
||||
import * as GuacamoleService from '../services/guacamole.service';
|
||||
import * as ImportExportService from '../services/import-export.service';
|
||||
import * as ConnectionRepository from '../repositories/connection.repository'; // +++ 导入 ConnectionRepository +++
|
||||
import * as ConnectionRepository from '../repositories/connection.repository';
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -11,11 +11,11 @@ import {
|
||||
testUnsavedConnection,
|
||||
exportConnections,
|
||||
importConnections,
|
||||
getRdpSessionToken, // Import the new controller function
|
||||
getVncSessionToken, // Import the VNC session token controller function
|
||||
cloneConnection, // +++ Import the clone controller function +++
|
||||
// updateConnectionTags, // No longer directly used by primary flow
|
||||
addTagToConnections // +++ Import the new controller function for adding tag to multiple connections +++
|
||||
getRdpSessionToken,
|
||||
getVncSessionToken,
|
||||
cloneConnection,
|
||||
|
||||
addTagToConnections
|
||||
} from './connections.controller';
|
||||
|
||||
const router = Router();
|
||||
|
||||
@@ -2,9 +2,9 @@ import { Database } from 'sqlite3';
|
||||
import * as schemaSql from './schema';
|
||||
import * as appearanceRepository from '../repositories/appearance.repository';
|
||||
import * as terminalThemeRepository from '../repositories/terminal-theme.repository';
|
||||
import * as settingsRepository from '../repositories/settings.repository'; // <-- Import settings repository
|
||||
import * as settingsRepository from '../repositories/settings.repository';
|
||||
import { presetTerminalThemes } from '../config/preset-themes-definition';
|
||||
import { runDb } from './connection'; // Import runDb for init functions
|
||||
import { runDb } from './connection';
|
||||
|
||||
/**
|
||||
* Interface describing a database table definition for initialization.
|
||||
@@ -12,12 +12,12 @@ import { runDb } from './connection'; // Import runDb for init functions
|
||||
export interface TableDefinition {
|
||||
name: string;
|
||||
sql: string;
|
||||
init?: (db: Database) => Promise<void>; // Optional initialization function
|
||||
init?: (db: Database) => Promise<void>;
|
||||
}
|
||||
|
||||
// --- Initialization Functions ---
|
||||
|
||||
// Remove the old initSettingsTable function, as the logic is now in the repository
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Initializes preset terminal themes.
|
||||
|
||||
@@ -26,15 +26,15 @@ if (dataConfigResultGlobal.error && (dataConfigResultGlobal.error as NodeJS.Errn
|
||||
} else if (!dataConfigResultGlobal.error) {
|
||||
console.log(`[ENV Init Early] Loaded environment variables from data .env file: ${dataEnvPathGlobal}`);
|
||||
}
|
||||
// --- 结束环境变量的早期加载 ---
|
||||
|
||||
|
||||
import express = require('express');
|
||||
import { Request, Response, NextFunction, RequestHandler } from 'express';
|
||||
import http from 'http';
|
||||
// import fs from 'fs'; // Moved up
|
||||
// import path from 'path'; // Moved up
|
||||
|
||||
|
||||
import crypto from 'crypto';
|
||||
// import dotenv from 'dotenv'; // Moved up
|
||||
|
||||
import session from 'express-session';
|
||||
import sessionFileStore from 'session-file-store';
|
||||
import { getDbInstance } from './database/connection';
|
||||
@@ -50,18 +50,18 @@ import commandHistoryRoutes from './command-history/command-history.routes';
|
||||
import quickCommandsRoutes from './quick-commands/quick-commands.routes';
|
||||
import terminalThemeRoutes from './terminal-themes/terminal-theme.routes';
|
||||
import appearanceRoutes from './appearance/appearance.routes';
|
||||
import sshKeysRouter from './ssh_keys/ssh_keys.routes'; // +++ Import SSH Key routes +++
|
||||
import quickCommandTagRoutes from './quick-command-tags/quick-command-tag.routes'; // +++ Import Quick Command Tag routes +++
|
||||
import sshSuspendRouter from './ssh-suspend/ssh-suspend.routes'; // +++ Import SSH Suspend routes +++
|
||||
import sshKeysRouter from './ssh_keys/ssh_keys.routes';
|
||||
import quickCommandTagRoutes from './quick-command-tags/quick-command-tag.routes';
|
||||
import sshSuspendRouter from './ssh-suspend/ssh-suspend.routes';
|
||||
import { initializeWebSocket } from './websocket';
|
||||
import { ipWhitelistMiddleware } from './auth/ipWhitelist.middleware';
|
||||
|
||||
// --- 初始化通知系统 (导入即初始化单例) ---
|
||||
import './services/event.service'; // 确保事件服务被加载
|
||||
import './services/notification.processor.service'; // 确保处理器被加载并监听事件
|
||||
import './services/notification.dispatcher.service'; // 确保分发器被加载并监听处理器事件
|
||||
// --- 结束通知系统初始化 ---
|
||||
// --- 环境变量和密钥初始化 ---
|
||||
|
||||
import './services/event.service';
|
||||
import './services/notification.processor.service';
|
||||
import './services/notification.dispatcher.service';
|
||||
|
||||
|
||||
|
||||
// --- 全局错误处理 ---
|
||||
// 捕获未处理的 Promise Rejection
|
||||
|
||||
@@ -181,7 +181,7 @@ export const assignTagToCommands = async (req: Request, res: Response): Promise<
|
||||
|
||||
try {
|
||||
// 调用 Service 函数处理批量分配
|
||||
console.log(`[Controller] assignTagToCommands: Received commandIds: ${JSON.stringify(commandIds)}, tagId: ${tagId}`); // +++ 添加日志 +++
|
||||
console.log(`[Controller] assignTagToCommands: Received commandIds: ${JSON.stringify(commandIds)}, tagId: ${tagId}`);
|
||||
await QuickCommandsService.assignTagToCommands(commandIds, tagId);
|
||||
res.status(200).json({ success: true, message: `标签 ${tagId} 已成功尝试关联到 ${commandIds.length} 个指令。` });
|
||||
} catch (error: any) {
|
||||
|
||||
@@ -261,9 +261,9 @@ export const ensureDefaultSettingsExist = async (db: sqlite3.Database): Promise<
|
||||
statusMonitorIntervalSeconds: '3',
|
||||
[SIDEBAR_CONFIG_KEY]: JSON.stringify(defaultSidebarPanesStructure),
|
||||
[CAPTCHA_CONFIG_KEY]: JSON.stringify(defaultCaptchaSettings),
|
||||
timezone: 'UTC', // 添加时区默认值
|
||||
terminalScrollbackLimit: '5000', // 添加终端回滚行数默认值
|
||||
terminalEnableRightClickPaste: 'true', // 添加终端右键粘贴默认值
|
||||
timezone: 'UTC', // 时区默认值
|
||||
terminalScrollbackLimit: '5000', // 终端回滚行数默认值
|
||||
terminalEnableRightClickPaste: 'true', // 终端右键粘贴默认值
|
||||
};
|
||||
const nowSeconds = Math.floor(Date.now() / 1000);
|
||||
const sqlInsertOrIgnore = `INSERT OR IGNORE INTO settings (key, value, created_at, updated_at) VALUES (?, ?, ?, ?)`;
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import * as ConnectionRepository from '../repositories/connection.repository';
|
||||
import { encrypt, decrypt } from '../utils/crypto';
|
||||
import { AuditLogService } from './audit.service';
|
||||
import * as SshKeyService from './ssh_key.service'; // +++ Import SshKeyService +++
|
||||
import * as SshKeyService from './ssh_key.service';
|
||||
import {
|
||||
ConnectionBase,
|
||||
ConnectionWithTags,
|
||||
CreateConnectionInput,
|
||||
UpdateConnectionInput,
|
||||
FullConnectionData
|
||||
} from '../types/connection.types'; // 从集中类型文件导入
|
||||
} from '../types/connection.types';
|
||||
|
||||
export type { ConnectionBase, ConnectionWithTags, CreateConnectionInput, UpdateConnectionInput };
|
||||
|
||||
|
||||
const auditLogService = new AuditLogService(); // 实例化 AuditLogService
|
||||
const auditLogService = new AuditLogService();
|
||||
|
||||
/**
|
||||
* 获取所有连接(包含标签)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
|
||||
import * as ConnectionRepository from '../repositories/connection.repository';
|
||||
import * as ProxyRepository from '../repositories/proxy.repository';
|
||||
import * as TagService from '../services/tag.service'; // +++ 导入标签服务 +++
|
||||
import * as TagService from '../services/tag.service';
|
||||
import { getDbInstance, runDb, getDb as getDbRow, allDb } from '../database/connection';
|
||||
import { decrypt, getEncryptionKeyBuffer as getCryptoKeyBuffer } from '../utils/crypto'; // For decrypting connection details
|
||||
import { getAllDecryptedSshKeys, DecryptedSshKeyDetails } from '../services/ssh_key.service'; // 静态导入, SshKeyData -> DecryptedSshKeyDetails
|
||||
import { decrypt, getEncryptionKeyBuffer as getCryptoKeyBuffer } from '../utils/crypto';
|
||||
import { getAllDecryptedSshKeys, DecryptedSshKeyDetails } from '../services/ssh_key.service';
|
||||
import archiver from 'archiver';
|
||||
archiver.registerFormat('zip-encrypted', require("archiver-zip-encrypted"));
|
||||
|
||||
@@ -13,7 +13,7 @@ archiver.registerFormat('zip-encrypted', require("archiver-zip-encrypted"));
|
||||
|
||||
interface ImportedConnectionData {
|
||||
name: string;
|
||||
type: 'SSH' | 'RDP' | 'VNC'; // Add type field
|
||||
type: 'SSH' | 'RDP' | 'VNC';
|
||||
host: string;
|
||||
port: number;
|
||||
username: string;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import notificationProcessorService, { ProcessedNotification } from './notification.processor.service'; // 导入导出的接口
|
||||
import notificationProcessorService, { ProcessedNotification } from './notification.processor.service';
|
||||
import { NotificationChannelType, NotificationChannelConfig } from '../types/notification.types';
|
||||
|
||||
// 1. 定义通知发送器接口
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as QuickCommandsRepository from '../repositories/quick-commands.repository';
|
||||
import { QuickCommandWithTags } from '../repositories/quick-commands.repository'; // Import the type with tags
|
||||
import * as QuickCommandTagRepository from '../repositories/quick-command-tag.repository'; // Import the new tag repository
|
||||
import { QuickCommandWithTags } from '../repositories/quick-commands.repository';
|
||||
import * as QuickCommandTagRepository from '../repositories/quick-command-tag.repository';
|
||||
|
||||
// 定义排序类型
|
||||
export type QuickCommandSortBy = 'name' | 'usage_count';
|
||||
@@ -119,7 +119,7 @@ export const assignTagToCommands = async (commandIds: number[], tagId: number):
|
||||
|
||||
// 调用 Repository 函数执行批量关联
|
||||
// 注意:这里需要导入 QuickCommandTagRepository
|
||||
console.log(`[Service] assignTagToCommands: Calling repo with commandIds: ${JSON.stringify(commandIds)}, tagId: ${tagId}`); // +++ 添加日志 +++
|
||||
console.log(`[Service] assignTagToCommands: Calling repo with commandIds: ${JSON.stringify(commandIds)}, tagId: ${tagId}`);
|
||||
await QuickCommandTagRepository.addTagToCommands(commandIds, tagId);
|
||||
console.log(`[Service] assignTagToCommands: Repo call finished for tag ${tagId}.`); // +++ 修改日志 +++
|
||||
// 可以在这里添加额外的业务逻辑,例如发送事件通知等
|
||||
|
||||
@@ -33,7 +33,8 @@ const DEFAULT_STATUS_MONITOR_INTERVAL_SECONDS = 3; // 默认状态监控间隔
|
||||
const IP_BLACKLIST_ENABLED_KEY = 'ipBlacklistEnabled'; // IP 黑名单启用设置键
|
||||
const SHOW_CONNECTION_TAGS_KEY = 'showConnectionTags'; // 连接标签显示设置键
|
||||
const SHOW_QUICK_COMMAND_TAGS_KEY = 'showQuickCommandTags'; // 快捷指令标签显示设置键
|
||||
|
||||
const SHOW_STATUS_MONITOR_IP_ADDRESS_KEY = 'showStatusMonitorIpAddress'; // 状态监视器IP显示设置键
|
||||
|
||||
export const settingsService = {
|
||||
/**
|
||||
* 获取所有设置项
|
||||
@@ -127,7 +128,7 @@ export const settingsService = {
|
||||
// 出错时返回默认值 true (安全起见,默认启用)
|
||||
return true;
|
||||
}
|
||||
}, // *** 确保这里有逗号 ***
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取焦点切换顺序
|
||||
@@ -190,7 +191,7 @@ export const settingsService = {
|
||||
console.error(`[Service] Error calling settingsRepository.setSetting for key ${FOCUS_SEQUENCE_KEY}:`, error);
|
||||
throw new Error('Failed to save focus switcher sequence.');
|
||||
}
|
||||
}, // *** 确保这里有逗号 ***
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取导航栏可见性设置
|
||||
@@ -208,7 +209,7 @@ export const settingsService = {
|
||||
// 出错时返回默认值 true
|
||||
return true;
|
||||
}
|
||||
}, // *** 确保这里有逗号 ***
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置导航栏可见性
|
||||
@@ -225,7 +226,7 @@ export const settingsService = {
|
||||
console.error(`[Service] Error calling settingsRepository.setSetting for key ${NAV_BAR_VISIBLE_KEY}:`, error);
|
||||
throw new Error('Failed to save nav bar visibility setting.');
|
||||
}
|
||||
}, // *** 确保这里有逗号 ***
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取布局树设置
|
||||
@@ -241,7 +242,7 @@ export const settingsService = {
|
||||
console.error(`[Service] Error getting layout tree setting (key: ${LAYOUT_TREE_KEY}):`, error);
|
||||
return null; // 出错时返回 null
|
||||
}
|
||||
}, // *** 确保这里有逗号 ***
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置布局树
|
||||
@@ -265,7 +266,7 @@ export const settingsService = {
|
||||
console.error(`[Service] Error calling settingsRepository.setSetting for key ${LAYOUT_TREE_KEY}:`, error);
|
||||
throw new Error('Failed to save layout tree setting.');
|
||||
}
|
||||
}, // *** 确保这里有逗号 ***
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取终端选中自动复制设置
|
||||
@@ -283,7 +284,7 @@ export const settingsService = {
|
||||
// 出错时返回默认值 false
|
||||
return false;
|
||||
}
|
||||
}, // *** 确保这里有逗号 ***
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置终端选中自动复制
|
||||
@@ -300,7 +301,7 @@ export const settingsService = {
|
||||
console.error(`[Service] Error calling settingsRepository.setSetting for key ${AUTO_COPY_ON_SELECT_KEY}:`, error);
|
||||
throw new Error('Failed to save auto copy on select setting.');
|
||||
}
|
||||
}, // *** 确保这里有逗号 ***
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取状态监控轮询间隔 (秒)
|
||||
@@ -327,7 +328,7 @@ export const settingsService = {
|
||||
}
|
||||
// 返回默认值
|
||||
return DEFAULT_STATUS_MONITOR_INTERVAL_SECONDS;
|
||||
}, // *** 确保这里有逗号 ***
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置状态监控轮询间隔 (秒)
|
||||
@@ -349,7 +350,7 @@ export const settingsService = {
|
||||
console.error(`[Service] Error calling settingsRepository.setSetting for key ${STATUS_MONITOR_INTERVAL_SECONDS_KEY}:`, error);
|
||||
throw new Error('Failed to save status monitor interval setting.');
|
||||
}
|
||||
}, // *** 确保这里有逗号 ***
|
||||
},
|
||||
|
||||
// --- Sidebar Config Specific Functions ---
|
||||
|
||||
@@ -493,9 +494,9 @@ export const settingsService = {
|
||||
return valueStr !== 'false';
|
||||
} catch (error) {
|
||||
console.error(`[Service] Error getting show connection tags setting (key: ${SHOW_CONNECTION_TAGS_KEY}):`, error);
|
||||
return true; // 默认返回 true
|
||||
return true;
|
||||
}
|
||||
}, // *** 确保这里有逗号 ***
|
||||
},
|
||||
|
||||
async setShowConnectionTags(enabled: boolean): Promise<void> {
|
||||
console.log(`[Service] setShowConnectionTags called with: ${enabled}`);
|
||||
@@ -508,7 +509,7 @@ export const settingsService = {
|
||||
console.error(`[Service] Error calling settingsRepository.setSetting for key ${SHOW_CONNECTION_TAGS_KEY}:`, error);
|
||||
throw new Error('Failed to save show connection tags setting.');
|
||||
}
|
||||
}, // *** 确保这里有逗号 ***
|
||||
},
|
||||
|
||||
// --- Show Quick Command Tags ---
|
||||
async getShowQuickCommandTags(): Promise<boolean> {
|
||||
@@ -520,9 +521,9 @@ export const settingsService = {
|
||||
return valueStr !== 'false';
|
||||
} catch (error) {
|
||||
console.error(`[Service] Error getting show quick command tags setting (key: ${SHOW_QUICK_COMMAND_TAGS_KEY}):`, error);
|
||||
return true; // 默认返回 true
|
||||
return true;
|
||||
}
|
||||
}, // *** 确保这里有逗号 ***
|
||||
},
|
||||
|
||||
async setShowQuickCommandTags(enabled: boolean): Promise<void> {
|
||||
console.log(`[Service] setShowQuickCommandTags called with: ${enabled}`);
|
||||
@@ -535,6 +536,29 @@ export const settingsService = {
|
||||
console.error(`[Service] Error calling settingsRepository.setSetting for key ${SHOW_QUICK_COMMAND_TAGS_KEY}:`, error);
|
||||
throw new Error('Failed to save show quick command tags setting.');
|
||||
}
|
||||
} // <-- No comma after the last method
|
||||
},
|
||||
|
||||
// --- Show Status Monitor IP Address ---
|
||||
async getShowStatusMonitorIpAddress(): Promise<boolean> {
|
||||
console.log(`[Service] Attempting to get setting for key: ${SHOW_STATUS_MONITOR_IP_ADDRESS_KEY}`);
|
||||
try {
|
||||
const valueStr = await settingsRepository.getSetting(SHOW_STATUS_MONITOR_IP_ADDRESS_KEY);
|
||||
// 默认显示 (true),所以只有当值为 'false' 时才返回 false
|
||||
return valueStr !== 'false';
|
||||
} catch (error) {
|
||||
console.error(`[Service] Error getting show status monitor IP address setting (key: ${SHOW_STATUS_MONITOR_IP_ADDRESS_KEY}):`, error);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
async setShowStatusMonitorIpAddress(enabled: boolean): Promise<void> {
|
||||
try {
|
||||
const valueStr = String(enabled);
|
||||
await settingsRepository.setSetting(SHOW_STATUS_MONITOR_IP_ADDRESS_KEY, valueStr);
|
||||
} catch (error) {
|
||||
console.error(`[Service] Error calling settingsRepository.setSetting for key ${SHOW_STATUS_MONITOR_IP_ADDRESS_KEY}:`, error);
|
||||
throw new Error('Failed to save show status monitor IP address setting.');
|
||||
}
|
||||
}
|
||||
|
||||
}; // <-- End of settingsService object definition
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Client, SFTPWrapper, Stats, WriteStream } from 'ssh2'; // Import WriteStream (Removed Dirent)
|
||||
import { Client, SFTPWrapper, Stats, WriteStream } from 'ssh2';
|
||||
import { WebSocket } from 'ws';
|
||||
import { ClientState, AuthenticatedWebSocket } from '../websocket/types'; // 导入统一的 ClientState 和 AuthenticatedWebSocket
|
||||
import * as pathModule from 'path'; // +++ Import path module +++
|
||||
import * as jschardet from 'jschardet'; // +++ Import jschardet +++
|
||||
import * as iconv from 'iconv-lite'; // +++ Import iconv-lite +++
|
||||
import { ClientState, AuthenticatedWebSocket } from '../websocket/types';
|
||||
import * as pathModule from 'path';
|
||||
import * as jschardet from 'jschardet';
|
||||
import * as iconv from 'iconv-lite';
|
||||
// +++ 导入新类型 +++
|
||||
import {
|
||||
SftpCompressRequestPayload,
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { settingsService } from '../services/settings.service';
|
||||
import { AuditLogService } from '../services/audit.service';
|
||||
import { NotificationService } from '../services/notification.service'; // 添加导入
|
||||
import { NotificationService } from '../services/notification.service';
|
||||
import { ipBlacklistService } from '../services/ip-blacklist.service';
|
||||
import { exportConnectionsAsEncryptedZip } from '../services/import-export.service'; // Import the new export service
|
||||
import { UpdateSidebarConfigDto, UpdateCaptchaSettingsDto, CaptchaSettings } from '../types/settings.types'; // <-- Import CAPTCHA types
|
||||
import i18next from '../i18n'; // +++ Import i18next +++
|
||||
import { exportConnectionsAsEncryptedZip } from '../services/import-export.service';
|
||||
import { UpdateSidebarConfigDto, UpdateCaptchaSettingsDto, CaptchaSettings } from '../types/settings.types';
|
||||
import i18next from '../i18n';
|
||||
|
||||
const auditLogService = new AuditLogService();
|
||||
const notificationService = new NotificationService(); // 添加实例
|
||||
const notificationService = new NotificationService();
|
||||
|
||||
export const settingsController = {
|
||||
/**
|
||||
@@ -39,23 +39,24 @@ export const settingsController = {
|
||||
'language', 'ipWhitelist', 'maxLoginAttempts', 'loginBanDuration',
|
||||
'showPopupFileEditor', 'shareFileEditorTabs', 'ipWhitelistEnabled',
|
||||
'autoCopyOnSelect', 'dockerStatusIntervalSeconds', 'dockerDefaultExpand',
|
||||
'statusMonitorIntervalSeconds', // +++ 添加状态监控间隔键 +++
|
||||
'workspaceSidebarPersistent', // +++ 添加侧边栏固定键 +++
|
||||
'showPopupFileManager', // +++ 添加弹窗文件管理器设置键 +++
|
||||
'sidebarPaneWidths', // +++ 添加侧边栏宽度对象键 +++
|
||||
'fileManagerRowSizeMultiplier', // +++ 添加文件管理器行大小键 +++
|
||||
'fileManagerColWidths', // +++ 添加文件管理器列宽键 +++
|
||||
'commandInputSyncTarget', // +++ 添加命令输入同步目标键 +++
|
||||
'timezone', // 添加时区键
|
||||
'rdpModalWidth', // 添加 RDP 模态框宽度键
|
||||
'rdpModalHeight', // 添加 RDP 模态框高度键
|
||||
'vncModalWidth', // 添加 VNC 模态框宽度键
|
||||
'vncModalHeight', // 添加 VNC 模态框高度键
|
||||
'statusMonitorIntervalSeconds', // +++ 状态监控间隔键 +++
|
||||
'workspaceSidebarPersistent', // +++ 侧边栏固定键 +++
|
||||
'showPopupFileManager', // +++ 弹窗文件管理器设置键 +++
|
||||
'sidebarPaneWidths', // +++ 侧边栏宽度对象键 +++
|
||||
'fileManagerRowSizeMultiplier', // +++ 文件管理器行大小键 +++
|
||||
'fileManagerColWidths', // +++ 文件管理器列宽键 +++
|
||||
'commandInputSyncTarget', // +++ 命令输入同步目标键 +++
|
||||
'timezone', // 时区键
|
||||
'rdpModalWidth', // RDP 模态框宽度键
|
||||
'rdpModalHeight', // RDP 模态框高度键
|
||||
'vncModalWidth', // VNC 模态框宽度键
|
||||
'vncModalHeight', // VNC 模态框高度键
|
||||
'ipBlacklistEnabled', // <-- 添加 IP 黑名单启用键
|
||||
'layoutLocked', // +++ 添加布局锁定键 +++
|
||||
'terminalScrollbackLimit', // 添加终端回滚行数键
|
||||
'fileManagerShowDeleteConfirmation', // 添加文件管理器删除确认键
|
||||
'terminalEnableRightClickPaste' // 添加终端右键粘贴键
|
||||
'layoutLocked', // +++ 布局锁定键 +++
|
||||
'terminalScrollbackLimit', // 终端回滚行数键
|
||||
'fileManagerShowDeleteConfirmation', // 文件管理器删除确认键
|
||||
'terminalEnableRightClickPaste', // 终端右键粘贴键
|
||||
'showStatusMonitorIpAddress' // 添加状态监视器IP显示键 (与服务层和前端统一)
|
||||
];
|
||||
const filteredSettings: Record<string, string> = {};
|
||||
for (const key in settingsToUpdate) {
|
||||
@@ -89,9 +90,7 @@ export const settingsController = {
|
||||
*/
|
||||
async getFocusSwitcherSequence(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
console.log('[控制器] 收到获取焦点切换顺序的请求。');
|
||||
const sequence = await settingsService.getFocusSwitcherSequence();
|
||||
console.log('[控制器] 向客户端发送焦点切换顺序:', JSON.stringify(sequence));
|
||||
res.json(sequence);
|
||||
} catch (error: any) {
|
||||
console.error('[控制器] 获取焦点切换顺序时出错:', error);
|
||||
@@ -103,7 +102,6 @@ export const settingsController = {
|
||||
* 设置焦点切换顺序
|
||||
*/
|
||||
async setFocusSwitcherSequence(req: Request, res: Response): Promise<void> {
|
||||
console.log('[控制器] 收到设置焦点切换顺序的请求。');
|
||||
try {
|
||||
// +++ 修改:获取请求体并验证其是否符合 FocusSwitcherFullConfig 结构 +++
|
||||
const fullConfig = req.body;
|
||||
@@ -121,14 +119,14 @@ export const settingsController = {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[控制器] 使用验证后的完整配置调用 settingsService.setFocusSwitcherSequence...');
|
||||
|
||||
// +++ 传递验证后的 fullConfig 给服务层 +++
|
||||
await settingsService.setFocusSwitcherSequence(fullConfig);
|
||||
console.log('[控制器] settingsService.setFocusSwitcherSequence 成功完成。');
|
||||
|
||||
|
||||
console.log('[控制器] 记录审计操作: FOCUS_SWITCHER_SEQUENCE_UPDATED');
|
||||
|
||||
|
||||
console.log('[控制器] 发送成功响应。');
|
||||
|
||||
res.status(200).json({ message: '焦点切换顺序已成功更新' });
|
||||
} catch (error: any) {
|
||||
console.error('[控制器] 设置焦点切换顺序时出错:', error);
|
||||
@@ -145,9 +143,7 @@ export const settingsController = {
|
||||
*/
|
||||
async getNavBarVisibility(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
console.log('[控制器] 收到获取导航栏可见性的请求。');
|
||||
const isVisible = await settingsService.getNavBarVisibility();
|
||||
console.log(`[控制器] 向客户端发送导航栏可见性: ${isVisible}`);
|
||||
res.json({ visible: isVisible });
|
||||
} catch (error: any) {
|
||||
console.error('[控制器] 获取导航栏可见性时出错:', error);
|
||||
@@ -159,7 +155,6 @@ export const settingsController = {
|
||||
* 设置导航栏可见性
|
||||
*/
|
||||
async setNavBarVisibility(req: Request, res: Response): Promise<void> {
|
||||
console.log('[控制器] 收到设置导航栏可见性的请求。');
|
||||
try {
|
||||
const { visible } = req.body;
|
||||
console.log('[控制器] 请求体 visible:', visible);
|
||||
@@ -170,12 +165,12 @@ export const settingsController = {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[控制器] 调用 settingsService.setNavBarVisibility...');
|
||||
|
||||
await settingsService.setNavBarVisibility(visible);
|
||||
console.log('[控制器] settingsService.setNavBarVisibility 成功完成。');
|
||||
|
||||
|
||||
|
||||
console.log('[控制器] 发送成功响应。');
|
||||
|
||||
res.status(200).json({ message: '导航栏可见性已成功更新' });
|
||||
} catch (error: any) {
|
||||
console.error('[控制器] 设置导航栏可见性时出错:', error);
|
||||
@@ -188,19 +183,18 @@ export const settingsController = {
|
||||
*/
|
||||
async getLayoutTree(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
console.log('[控制器] 收到获取布局树的请求。');
|
||||
const layoutJson = await settingsService.getLayoutTree();
|
||||
if (layoutJson) {
|
||||
try {
|
||||
const layout = JSON.parse(layoutJson);
|
||||
console.log('[控制器] 向客户端发送布局树。');
|
||||
|
||||
res.json(layout);
|
||||
} catch (parseError) {
|
||||
console.error('[控制器] 从数据库解析布局树 JSON 失败:', parseError);
|
||||
res.status(500).json({ message: '获取布局树失败:存储的数据格式无效' });
|
||||
}
|
||||
} else {
|
||||
console.log('[控制器] 在设置中未找到布局树,发送 null。');
|
||||
|
||||
res.json(null);
|
||||
}
|
||||
} catch (error: any) {
|
||||
@@ -213,7 +207,6 @@ export const settingsController = {
|
||||
* 设置布局树
|
||||
*/
|
||||
async setLayoutTree(req: Request, res: Response): Promise<void> {
|
||||
console.log('[控制器] 收到设置布局树的请求。');
|
||||
try {
|
||||
const layoutTree = req.body;
|
||||
|
||||
@@ -225,13 +218,13 @@ export const settingsController = {
|
||||
|
||||
const layoutJson = JSON.stringify(layoutTree);
|
||||
|
||||
console.log('[控制器] 调用 settingsService.setLayoutTree...');
|
||||
|
||||
await settingsService.setLayoutTree(layoutJson);
|
||||
console.log('[控制器] settingsService.setLayoutTree 成功完成。');
|
||||
|
||||
|
||||
// auditLogService.logAction('LAYOUT_TREE_UPDATED');
|
||||
|
||||
console.log('[控制器] 发送成功响应。');
|
||||
|
||||
res.status(200).json({ message: '布局树已成功更新' });
|
||||
} catch (error: any) {
|
||||
console.error('[控制器] 设置布局树时出错:', error);
|
||||
@@ -281,9 +274,7 @@ export const settingsController = {
|
||||
*/
|
||||
async getAutoCopyOnSelect(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
console.log('[控制器] 收到获取“选中时自动复制”设置的请求。');
|
||||
const isEnabled = await settingsService.getAutoCopyOnSelect();
|
||||
console.log(`[控制器] 向客户端发送“选中时自动复制”设置: ${isEnabled}`);
|
||||
res.json({ enabled: isEnabled });
|
||||
} catch (error: any) {
|
||||
console.error('[控制器] 获取终端选中自动复制设置时出错:', error);
|
||||
@@ -295,7 +286,6 @@ export const settingsController = {
|
||||
* 设置终端选中自动复制
|
||||
*/
|
||||
async setAutoCopyOnSelect(req: Request, res: Response): Promise<void> {
|
||||
console.log('[控制器] 收到设置“选中时自动复制”设置的请求。');
|
||||
try {
|
||||
const { enabled } = req.body;
|
||||
console.log('[控制器] 请求体 enabled:', enabled);
|
||||
@@ -306,12 +296,12 @@ export const settingsController = {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[控制器] 调用 settingsService.setAutoCopyOnSelect...');
|
||||
|
||||
await settingsService.setAutoCopyOnSelect(enabled);
|
||||
console.log('[控制器] settingsService.setAutoCopyOnSelect 成功完成。');
|
||||
|
||||
|
||||
|
||||
console.log('[控制器] 发送成功响应。');
|
||||
|
||||
res.status(200).json({ message: '终端选中自动复制设置已成功更新' });
|
||||
} catch (error: any) {
|
||||
console.error('[控制器] 设置终端选中自动复制时出错:', error);
|
||||
@@ -325,7 +315,6 @@ export const settingsController = {
|
||||
*/
|
||||
async getSidebarConfig(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
console.log('[控制器] 收到获取侧边栏配置的请求。');
|
||||
const config = await settingsService.getSidebarConfig();
|
||||
console.log('[控制器] 向客户端发送侧边栏配置:', config);
|
||||
res.json(config);
|
||||
@@ -339,7 +328,6 @@ export const settingsController = {
|
||||
* 设置侧栏配置
|
||||
*/
|
||||
async setSidebarConfig(req: Request, res: Response): Promise<void> {
|
||||
console.log('[控制器] 收到设置侧边栏配置的请求。');
|
||||
try {
|
||||
const configDto: UpdateSidebarConfigDto = req.body;
|
||||
console.log('[控制器] 请求体:', configDto);
|
||||
@@ -352,12 +340,12 @@ export const settingsController = {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[控制器] 调用 settingsService.setSidebarConfig...');
|
||||
|
||||
await settingsService.setSidebarConfig(configDto);
|
||||
console.log('[控制器] settingsService.setSidebarConfig 成功完成。');
|
||||
|
||||
|
||||
|
||||
console.log('[控制器] 发送成功响应。');
|
||||
|
||||
res.status(200).json({ message: '侧栏配置已成功更新' });
|
||||
} catch (error: any) {
|
||||
console.error('[控制器] 设置侧栏配置时出错:', error);
|
||||
@@ -375,7 +363,6 @@ export const settingsController = {
|
||||
*/
|
||||
async getCaptchaConfig(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
console.log('[控制器] 收到获取 CAPTCHA 配置的请求。');
|
||||
const fullConfig = await settingsService.getCaptchaConfig();
|
||||
|
||||
const publicConfig = {
|
||||
@@ -397,7 +384,6 @@ async getCaptchaConfig(req: Request, res: Response): Promise<void> {
|
||||
* 设置 CAPTCHA 配置
|
||||
*/
|
||||
async setCaptchaConfig(req: Request, res: Response): Promise<void> {
|
||||
console.log('[控制器] 收到设置 CAPTCHA 配置的请求。');
|
||||
try {
|
||||
const configDto: UpdateCaptchaSettingsDto = req.body;
|
||||
console.log('[控制器] 请求体 (DTO, 密钥已屏蔽):', { ...configDto, hcaptchaSecretKey: '***', recaptchaSecretKey: '***' });
|
||||
@@ -409,12 +395,12 @@ async setCaptchaConfig(req: Request, res: Response): Promise<void> {
|
||||
}
|
||||
|
||||
|
||||
console.log('[控制器] 调用 settingsService.setCaptchaConfig...');
|
||||
|
||||
await settingsService.setCaptchaConfig(configDto);
|
||||
console.log('[控制器] settingsService.setCaptchaConfig 成功完成。');
|
||||
|
||||
|
||||
|
||||
console.log('[控制器] 发送成功响应。');
|
||||
|
||||
res.status(200).json({ message: 'CAPTCHA 配置已成功更新' });
|
||||
} catch (error: any) {
|
||||
console.error('[控制器] 设置 CAPTCHA 配置时出错:', error);
|
||||
@@ -430,9 +416,7 @@ async setCaptchaConfig(req: Request, res: Response): Promise<void> {
|
||||
// --- Show Connection Tags ---
|
||||
async getShowConnectionTags(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
console.log('[控制器] 收到获取“显示连接标签”设置的请求。');
|
||||
const isEnabled = await settingsService.getShowConnectionTags();
|
||||
console.log(`[控制器] 向客户端发送“显示连接标签”设置: ${isEnabled}`);
|
||||
res.json({ enabled: isEnabled });
|
||||
} catch (error: any) {
|
||||
console.error('[控制器] 获取“显示连接标签”设置时出错:', error);
|
||||
@@ -441,7 +425,6 @@ async setCaptchaConfig(req: Request, res: Response): Promise<void> {
|
||||
}, // *** 确保这里有逗号 ***
|
||||
|
||||
async setShowConnectionTags(req: Request, res: Response): Promise<void> {
|
||||
console.log('[控制器] 收到设置“显示连接标签”设置的请求。');
|
||||
try {
|
||||
const { enabled } = req.body;
|
||||
console.log('[控制器] 请求体 enabled:', enabled);
|
||||
@@ -452,14 +435,14 @@ async setCaptchaConfig(req: Request, res: Response): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[控制器] 调用 settingsService.setShowConnectionTags...');
|
||||
|
||||
await settingsService.setShowConnectionTags(enabled);
|
||||
console.log('[控制器] settingsService.setShowConnectionTags 成功完成。');
|
||||
|
||||
|
||||
auditLogService.logAction('SETTINGS_UPDATED', { updatedKeys: ['showConnectionTags'] });
|
||||
notificationService.sendNotification('SETTINGS_UPDATED', { updatedKeys: ['showConnectionTags'] });
|
||||
|
||||
console.log('[控制器] 发送成功响应。');
|
||||
|
||||
res.status(200).json({ message: '“显示连接标签”设置已成功更新' });
|
||||
} catch (error: any) {
|
||||
console.error('[控制器] 设置“显示连接标签”时出错:', error);
|
||||
@@ -470,9 +453,7 @@ async setCaptchaConfig(req: Request, res: Response): Promise<void> {
|
||||
// --- Show Quick Command Tags ---
|
||||
async getShowQuickCommandTags(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
console.log('[控制器] 收到获取“显示快捷指令标签”设置的请求。');
|
||||
const isEnabled = await settingsService.getShowQuickCommandTags();
|
||||
console.log(`[控制器] 向客户端发送“显示快捷指令标签”设置: ${isEnabled}`);
|
||||
res.json({ enabled: isEnabled });
|
||||
} catch (error: any) {
|
||||
console.error('[控制器] 获取“显示快捷指令标签”设置时出错:', error);
|
||||
@@ -481,7 +462,6 @@ async setCaptchaConfig(req: Request, res: Response): Promise<void> {
|
||||
}, // *** 确保这里有逗号 ***
|
||||
|
||||
async setShowQuickCommandTags(req: Request, res: Response): Promise<void> {
|
||||
console.log('[控制器] 收到设置“显示快捷指令标签”设置的请求。');
|
||||
try {
|
||||
const { enabled } = req.body;
|
||||
console.log('[控制器] 请求体 enabled:', enabled);
|
||||
@@ -492,27 +472,63 @@ async setCaptchaConfig(req: Request, res: Response): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[控制器] 调用 settingsService.setShowQuickCommandTags...');
|
||||
|
||||
await settingsService.setShowQuickCommandTags(enabled);
|
||||
console.log('[控制器] settingsService.setShowQuickCommandTags 成功完成。');
|
||||
|
||||
|
||||
auditLogService.logAction('SETTINGS_UPDATED', { updatedKeys: ['showQuickCommandTags'] });
|
||||
notificationService.sendNotification('SETTINGS_UPDATED', { updatedKeys: ['showQuickCommandTags'] });
|
||||
|
||||
console.log('[控制器] 发送成功响应。');
|
||||
|
||||
res.status(200).json({ message: '“显示快捷指令标签”设置已成功更新' });
|
||||
} catch (error: any) {
|
||||
console.error('[控制器] 设置“显示快捷指令标签”时出错:', error);
|
||||
res.status(500).json({ message: '设置“显示快捷指令标签”失败', error: error.message });
|
||||
}
|
||||
}, // <-- Add comma here for the new method
|
||||
},
|
||||
|
||||
// --- Show Status Monitor IP Address ---
|
||||
async getShowStatusMonitorIpAddress(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const isEnabled = await settingsService.getShowStatusMonitorIpAddress();
|
||||
res.json({ enabled: isEnabled });
|
||||
} catch (error: any) {
|
||||
console.error('[控制器] 获取“显示状态监视器IP地址”设置时出错:', error);
|
||||
res.status(500).json({ message: '获取“显示状态监视器IP地址”设置失败', error: error.message });
|
||||
}
|
||||
},
|
||||
|
||||
async setShowStatusMonitorIpAddress(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { enabled } = req.body;
|
||||
console.log('[控制器] 请求体 enabled:', enabled);
|
||||
|
||||
if (typeof enabled !== 'boolean') {
|
||||
console.warn('[控制器] 收到无效的 enabled 格式:', enabled);
|
||||
res.status(400).json({ message: '无效的请求体,"enabled" 必须是一个布尔值' });
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
await settingsService.setShowStatusMonitorIpAddress(enabled);
|
||||
|
||||
|
||||
auditLogService.logAction('SETTINGS_UPDATED', { updatedKeys: ['showStatusMonitorIpAddress'] });
|
||||
notificationService.sendNotification('SETTINGS_UPDATED', { updatedKeys: ['showStatusMonitorIpAddress'] });
|
||||
|
||||
|
||||
res.status(200).json({ message: '“显示状态监视器IP地址”设置已成功更新' });
|
||||
} catch (error: any) {
|
||||
console.error('[控制器] 设置“显示状态监视器IP地址”时出错:', error);
|
||||
res.status(500).json({ message: '设置“显示状态监视器IP地址”失败', error: error.message });
|
||||
}
|
||||
}, // <-- Add comma here
|
||||
|
||||
/**
|
||||
* 导出所有连接配置为加密的 ZIP 文件
|
||||
*/
|
||||
async exportAllConnections(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
console.log('[控制器] 收到导出所有连接的请求。');
|
||||
const encryptedZipBuffer = await exportConnectionsAsEncryptedZip(true);
|
||||
|
||||
res.setHeader('Content-Type', 'application/zip');
|
||||
@@ -520,7 +536,7 @@ async setCaptchaConfig(req: Request, res: Response): Promise<void> {
|
||||
res.send(encryptedZipBuffer);
|
||||
|
||||
// auditLogService.logAction('CONNECTIONS_EXPORTED', { userId: (req.user as any)?.id || 'unknown' }); // 移除审计日志
|
||||
console.log('[控制器] 成功发送加密的连接导出文件。');
|
||||
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('[控制器] 导出所有连接时出错:', error);
|
||||
|
||||
@@ -68,8 +68,13 @@ router.put('/show-quick-command-tags', settingsController.setShowQuickCommandTag
|
||||
// +++ 导出所有连接路由 +++
|
||||
// GET /api/v1/settings/export-connections - 导出所有连接为加密的 ZIP 文件
|
||||
router.get('/export-connections', settingsController.exportAllConnections);
|
||||
|
||||
|
||||
|
||||
// +++ 显示状态监视器IP地址路由 +++
|
||||
// GET /api/v1/settings/show-status-monitor-ip-address - 获取设置
|
||||
router.get('/show-status-monitor-ip-address', settingsController.getShowStatusMonitorIpAddress);
|
||||
// PUT /api/v1/settings/show-status-monitor-ip-address - 更新设置
|
||||
router.put('/show-status-monitor-ip-address', settingsController.setShowStatusMonitorIpAddress);
|
||||
|
||||
export default router;
|
||||
|
||||
// +++ CAPTCHA 配置路由 (需要认证更新) +++
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { sshSuspendService } from '../services/ssh-suspend.service'; // 导入单例服务
|
||||
import { SuspendedSessionInfo } from '../types/ssh-suspend.types'; // 导入类型
|
||||
import { sshSuspendService } from '../services/ssh-suspend.service';
|
||||
import { SuspendedSessionInfo } from '../types/ssh-suspend.types';
|
||||
|
||||
export class SshSuspendController {
|
||||
// private sshSuspendService: SshSuspendService; // 不再需要,直接使用导入的单例
|
||||
|
||||
|
||||
constructor() {
|
||||
// this.sshSuspendService = new SshSuspendService(); // 不再需要实例化
|
||||
// 绑定方法到当前实例,以确保 'this' 上下文正确
|
||||
this.getSuspendedSshSessions = this.getSuspendedSshSessions.bind(this);
|
||||
this.terminateAndRemoveSession = this.terminateAndRemoveSession.bind(this);
|
||||
this.removeSessionEntry = this.removeSessionEntry.bind(this);
|
||||
this.editSessionNameHttp = this.editSessionNameHttp.bind(this); // 绑定新方法
|
||||
this.exportSessionLog = this.exportSessionLog.bind(this); // +++ 绑定导出日志方法 +++
|
||||
this.editSessionNameHttp = this.editSessionNameHttp.bind(this);
|
||||
this.exportSessionLog = this.exportSessionLog.bind(this);
|
||||
}
|
||||
|
||||
public async getSuspendedSshSessions(req: Request, res: Response): Promise<void> {
|
||||
|
||||
@@ -66,6 +66,7 @@ export interface UpdateCaptchaSettingsDto {
|
||||
export interface AppSettings {
|
||||
sidebar?: SidebarConfig;
|
||||
captcha?: CaptchaSettings;
|
||||
showStatusMonitorIpAddress?: boolean; // 新增:是否在状态监视器中显示IP地址
|
||||
// 可以添加其他设置模块,例如:
|
||||
// security?: SecuritySettings;
|
||||
// general?: GeneralSettings;
|
||||
|
||||
@@ -5,8 +5,8 @@ import { initializeHeartbeat } from './websocket/heartbeat';
|
||||
import { initializeUpgradeHandler } from './websocket/upgrade';
|
||||
import { initializeConnectionHandler } from './websocket/connection';
|
||||
import { clientStates } from './websocket/state';
|
||||
import { sshSuspendService } from './services/ssh-suspend.service'; // 导入实例
|
||||
import { SftpService } from './services/sftp.service'; // +++ 导入 SftpService +++
|
||||
import { sshSuspendService } from './services/ssh-suspend.service';
|
||||
import { SftpService } from './services/sftp.service';
|
||||
import { cleanupClientConnection } from './websocket/utils';
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user