This commit is contained in:
Baobhan Sith
2025-04-27 10:32:18 +08:00
parent 2983864e8a
commit 37b7bffaa4
7 changed files with 41 additions and 45 deletions
+6 -6
View File
@@ -12,16 +12,16 @@ try {
dynamicSupportedLngs = entries dynamicSupportedLngs = entries
.filter(dirent => dirent.isFile() && dirent.name.endsWith('.json')) .filter(dirent => dirent.isFile() && dirent.name.endsWith('.json'))
.map(dirent => dirent.name.replace('.json', '')); // Extract lang code from filename .map(dirent => dirent.name.replace('.json', '')); // Extract lang code from filename
console.log('[i18next] Dynamically detected languages:', dynamicSupportedLngs); console.log('[i18next] 动态检测到的语言:', dynamicSupportedLngs);
} catch (err) { } catch (err) {
console.error('[i18next] Error reading locales directory:', err); console.error('[i18next] 读取 locales 目录时出错:', err);
dynamicSupportedLngs = ['en-US']; // Fallback dynamicSupportedLngs = ['en-US']; // Fallback
} }
export const defaultLng = 'en-US'; export const defaultLng = 'en-US';
if (!dynamicSupportedLngs.includes(defaultLng)) { if (!dynamicSupportedLngs.includes(defaultLng)) {
dynamicSupportedLngs.push(defaultLng); dynamicSupportedLngs.push(defaultLng);
console.warn(`[i18next] Default language '${defaultLng}' not found in detected files, adding it to supported list.`); console.warn(`[i18next] 在检测到的文件中未找到默认语言 '${defaultLng}',将其添加到支持列表中。`);
} }
export const supportedLngs = dynamicSupportedLngs; export const supportedLngs = dynamicSupportedLngs;
// --- 结束动态确定 --- // --- 结束动态确定 ---
@@ -33,7 +33,7 @@ const i18nInitializationPromise = new Promise<void>((resolve, reject) => {
i18next i18next
.use(Backend) .use(Backend)
.init({ .init({
debug: process.env.NODE_ENV === 'development', debug: false, // 强制禁用 i18next 调试日志
supportedLngs: supportedLngs, supportedLngs: supportedLngs,
fallbackLng: defaultLng, fallbackLng: defaultLng,
preload: supportedLngs, preload: supportedLngs,
@@ -46,11 +46,11 @@ const i18nInitializationPromise = new Promise<void>((resolve, reject) => {
}, },
}, (err, t) => { // Init callback }, (err, t) => { // Init callback
if (err) { if (err) {
console.error('[i18next] Error during initialization:', err); console.error('[i18next] 初始化过程中出错:', err);
i18nInitialized = false; // Mark as not initialized on error i18nInitialized = false; // Mark as not initialized on error
return reject(err); // Reject the promise on error return reject(err); // Reject the promise on error
} }
console.log('[i18next] Initialization complete. Loaded languages:', Object.keys(i18next.store.data || {})); // Safe access to store.data console.log('[i18next] 初始化完成。已加载语言:', Object.keys(i18next.store.data || {})); // Safe access to store.data
i18nInitialized = true; // Mark as initialized i18nInitialized = true; // Mark as initialized
resolve(); // Resolve the promise on success resolve(); // Resolve the promise on success
}); });
@@ -155,7 +155,7 @@ const findAndSetDefaultThemeIdIfNull = async (db: sqlite3.Database): Promise<voi
const sqlReplace = `INSERT OR REPLACE INTO ${TABLE_NAME} (key, value, updated_at) VALUES (?, ?, ?)`; const sqlReplace = `INSERT OR REPLACE INTO ${TABLE_NAME} (key, value, updated_at) VALUES (?, ?, ?)`;
await runDb(db, sqlReplace, ['activeTerminalThemeId', String(defaultThemeIdNum), Math.floor(Date.now() / 1000)]); await runDb(db, sqlReplace, ['activeTerminalThemeId', String(defaultThemeIdNum), Math.floor(Date.now() / 1000)]);
} else { } else {
console.warn("[AppearanceRepo] 未找到名为 'default' 的预设终端主题,无法设置默认 activeTerminalThemeId。"); // console.warn("[AppearanceRepo] 未找到名为 'default' 的预设终端主题,无法设置默认 activeTerminalThemeId。");
} }
} }
// 如果 activeTerminalThemeId 已设置或键不存在,则不执行任何操作 // 如果 activeTerminalThemeId 已设置或键不存在,则不执行任何操作
@@ -272,12 +272,10 @@ export const ensureDefaultSettingsExist = async (db: sqlite3.Database): Promise<
const nowSeconds = Math.floor(Date.now() / 1000); const nowSeconds = Math.floor(Date.now() / 1000);
const sqlInsertOrIgnore = `INSERT OR IGNORE INTO settings (key, value, created_at, updated_at) VALUES (?, ?, ?, ?)`; const sqlInsertOrIgnore = `INSERT OR IGNORE INTO settings (key, value, created_at, updated_at) VALUES (?, ?, ?, ?)`;
console.log('[设置仓库] 确保默认设置存在...');
try { try {
for (const [key, value] of Object.entries(defaultSettings)) { for (const [key, value] of Object.entries(defaultSettings)) {
await runDb(db, sqlInsertOrIgnore, [key, value, nowSeconds, nowSeconds]); await runDb(db, sqlInsertOrIgnore, [key, value, nowSeconds, nowSeconds]);
} }
console.log('[设置仓库] 默认设置检查完成。');
} catch (err: any) { } catch (err: any) {
console.error(`[设置仓库] 确保默认设置时出错:`, err.message); console.error(`[设置仓库] 确保默认设置时出错:`, err.message);
throw new Error(`确保默认设置失败: ${err.message}`); throw new Error(`确保默认设置失败: ${err.message}`);
@@ -233,7 +233,7 @@ export const initializePresetThemes = async (db: Database, presets: Array<Omit<T
for (const preset of presets) { for (const preset of presets) {
// 在循环开始时添加日志,显示正在处理哪个主题 // 在循环开始时添加日志,显示正在处理哪个主题
console.log(`[DB Init] 正在处理预设主题: "${preset.name}"`); // console.log(`[DB Init] 正在处理预设主题: "${preset.name}"`);
try { try {
const existing = await getDb<{ id: number }>(db, `SELECT id FROM terminal_themes WHERE name = ? AND theme_type = 'preset'`, [preset.name]); const existing = await getDb<{ id: number }>(db, `SELECT id FROM terminal_themes WHERE name = ? AND theme_type = 'preset'`, [preset.name]);
@@ -260,9 +260,9 @@ export const initializePresetThemes = async (db: Database, presets: Array<Omit<T
VALUES (${placeholders}) VALUES (${placeholders})
`; `;
await runDb(db, insertSql, values); await runDb(db, insertSql, values);
console.log(`[DB Init] 预设主题 "${preset.name}" 已初始化到数据库。`); // console.log(`[DB Init] 预设主题 "${preset.name}" 已初始化到数据库。`);
} else { } else {
console.log(`[DB Init] 预设主题 "${preset.name}" 已存在,跳过初始化。`); // console.log(`[DB Init] 预设主题 "${preset.name}" 已存在,跳过初始化。`);
} }
} catch (err: any) { } catch (err: any) {
console.error(`[DB Init] 处理预设主题 "${preset.name}" 时出错:`, err.message); console.error(`[DB Init] 处理预设主题 "${preset.name}" 时出错:`, err.message);
@@ -33,10 +33,10 @@ class NotificationDispatcherService {
*/ */
registerSender(channelType: NotificationChannelType, sender: INotificationSender) { registerSender(channelType: NotificationChannelType, sender: INotificationSender) {
if (this.senders.has(channelType)) { if (this.senders.has(channelType)) {
console.warn(`[NotificationDispatcher] Sender for channel type '${channelType}' is already registered. Overwriting.`); console.warn(`[NotificationDispatcher] 通道类型 '${channelType}' 的发送器已注册。将进行覆盖。`);
} }
this.senders.set(channelType, sender); this.senders.set(channelType, sender);
console.log(`[NotificationDispatcher] Registered sender for channel type '${channelType}'.`); console.log(`[NotificationDispatcher] 已为通道类型 '${channelType}' 注册发送器。`);
} }
private listenForNotifications() { private listenForNotifications() {
@@ -44,27 +44,27 @@ class NotificationDispatcherService {
// 使用 setImmediate 避免阻塞 // 使用 setImmediate 避免阻塞
setImmediate(() => { setImmediate(() => {
this.dispatchNotification(processedNotification).catch(error => { this.dispatchNotification(processedNotification).catch(error => {
console.error(`[NotificationDispatcher] Error dispatching notification for channel ${processedNotification.channelType}:`, error); console.error(`[NotificationDispatcher] 分发通道 ${processedNotification.channelType} 的通知时出错:`, error);
}); });
}); });
}); });
console.log('[NotificationDispatcher] Listening for processed notifications.'); console.log('[NotificationDispatcher] 正在监听处理后的通知。');
} }
private async dispatchNotification(notification: ProcessedNotification) { private async dispatchNotification(notification: ProcessedNotification) {
const sender = this.senders.get(notification.channelType); const sender = this.senders.get(notification.channelType);
if (!sender) { if (!sender) {
console.warn(`[NotificationDispatcher] No sender registered for channel type: ${notification.channelType}. Skipping notification.`); console.warn(`[NotificationDispatcher] 没有为通道类型注册发送器: ${notification.channelType}。跳过通知。`);
return; return;
} }
console.log(`[NotificationDispatcher] Dispatching notification via ${notification.channelType}`); console.log(`[NotificationDispatcher] 正在通过 ${notification.channelType} 分发通知`);
try { try {
await sender.send(notification); await sender.send(notification);
console.log(`[NotificationDispatcher] Successfully sent notification via ${notification.channelType}`); console.log(`[NotificationDispatcher] 已成功通过 ${notification.channelType} 发送通知`);
} catch (error) { } catch (error) {
console.error(`[NotificationDispatcher] Failed to send notification via ${notification.channelType}:`, error); console.error(`[NotificationDispatcher] 通过 ${notification.channelType} 发送通知失败:`, error);
// 这里可以添加失败重试或记录失败状态的逻辑 // 这里可以添加失败重试或记录失败状态的逻辑
} }
} }
@@ -27,21 +27,21 @@ class NotificationProcessorService extends EventEmitter {
private async initialize(): Promise<void> { private async initialize(): Promise<void> {
try { try {
console.log('[NotificationProcessor] Waiting for i18n initialization...'); console.log('[NotificationProcessor] 等待 i18n 初始化...');
await i18nInitializationPromise; await i18nInitializationPromise;
console.log('[NotificationProcessor] i18n initialized. Registering event listeners...'); console.log('[NotificationProcessor] i18n 初始化完成。正在注册事件监听器...');
this.registerEventListeners(); this.registerEventListeners();
this.isInitialized = true; this.isInitialized = true;
console.log('[NotificationProcessor] Initialization complete.'); console.log('[NotificationProcessor] 初始化完成。');
} catch (error) { } catch (error) {
console.error('[NotificationProcessor] Failed to initialize due to i18n error:', error); console.error('[NotificationProcessor] 因 i18n 错误导致初始化失败:', error);
} }
} }
private registerEventListeners() { private registerEventListeners() {
if (this.isInitialized) { if (this.isInitialized) {
console.warn('[NotificationProcessor] Attempted to register listeners multiple times.'); console.warn('[NotificationProcessor] 尝试多次注册监听器。');
return; return;
} }
// 监听所有 AppEventType 事件 // 监听所有 AppEventType 事件
@@ -51,7 +51,7 @@ class NotificationProcessorService extends EventEmitter {
// 使用 setImmediate 或 process.nextTick 避免阻塞事件循环 // 使用 setImmediate 或 process.nextTick 避免阻塞事件循环
setImmediate(() => { setImmediate(() => {
this.processStandardEvent(eventType, payload).catch(error => { this.processStandardEvent(eventType, payload).catch(error => {
console.error(`[NotificationProcessor] Error processing event ${eventType}:`, error); console.error(`[NotificationProcessor] 处理事件 ${eventType} 时出错:`, error);
}); });
}); });
}); });
@@ -60,24 +60,24 @@ class NotificationProcessorService extends EventEmitter {
eventService.onEvent(AppEventType.TestNotification, (payload) => { eventService.onEvent(AppEventType.TestNotification, (payload) => {
setImmediate(() => { setImmediate(() => {
this.processTestEvent(payload).catch(error => { this.processTestEvent(payload).catch(error => {
console.error(`[NotificationProcessor] Error processing test event:`, error); console.error(`[NotificationProcessor] 处理测试事件时出错:`, error);
}); });
}); });
}); });
console.log('[NotificationProcessor] Registered listeners.'); console.log('[NotificationProcessor] 已注册监听器。');
} }
private async processStandardEvent(eventType: AppEventType, payload: AppEventPayload) { private async processStandardEvent(eventType: AppEventType, payload: AppEventPayload) {
if (!this.isInitialized) { if (!this.isInitialized) {
console.warn(`[NotificationProcessor] Received event ${eventType} before initialization. Skipping.`); console.warn(`[NotificationProcessor] 在初始化完成前收到事件 ${eventType}。跳过处理。`);
return; return;
} }
console.log(`[NotificationProcessor] Received standard event: ${eventType}`, payload); console.log(`[NotificationProcessor] 收到标准事件: ${eventType}`, payload);
const eventKey = eventType as NotificationEvent; // 类型转换,假设 AppEventType 和 NotificationEvent 对应 const eventKey = eventType as NotificationEvent; // 类型转换,假设 AppEventType 和 NotificationEvent 对应
try { try {
const applicableSettings = await this.repository.getEnabledByEvent(eventKey); const applicableSettings = await this.repository.getEnabledByEvent(eventKey);
console.log(`[NotificationProcessor] Found ${applicableSettings.length} applicable settings for event ${eventKey}`); console.log(`[NotificationProcessor] 找到 ${applicableSettings.length} 个适用于事件 ${eventKey} 的设置`);
if (applicableSettings.length === 0) { if (applicableSettings.length === 0) {
return; // 没有配置需要处理 return; // 没有配置需要处理
@@ -94,20 +94,20 @@ class NotificationProcessorService extends EventEmitter {
this.processSingleSetting(setting, eventType, payload, translatedEvent, userLang); this.processSingleSetting(setting, eventType, payload, translatedEvent, userLang);
} }
} catch (error) { } catch (error) {
console.error(`[NotificationProcessor] Failed to fetch settings for event ${eventKey}:`, error); console.error(`[NotificationProcessor] 获取事件 ${eventKey} 的设置失败:`, error);
} }
} }
private async processTestEvent(payload: AppEventPayload) { private async processTestEvent(payload: AppEventPayload) {
if (!this.isInitialized) { if (!this.isInitialized) {
console.warn(`[NotificationProcessor] Received test event before initialization. Skipping.`); console.warn(`[NotificationProcessor] 在初始化完成前收到测试事件。跳过处理。`);
return; return;
} }
console.log(`[NotificationProcessor] Received test event`, payload); console.log(`[NotificationProcessor] 收到测试事件`, payload);
const { testTargetConfig, testTargetChannelType } = payload.details || {}; const { testTargetConfig, testTargetChannelType } = payload.details || {};
if (!testTargetConfig || !testTargetChannelType) { if (!testTargetConfig || !testTargetChannelType) {
console.error('[NotificationProcessor] Test event payload missing testTargetConfig or testTargetChannelType.'); console.error('[NotificationProcessor] 测试事件负载缺少 testTargetConfig testTargetChannelType');
return; return;
} }
@@ -146,10 +146,10 @@ class NotificationProcessorService extends EventEmitter {
if (processedNotification) { if (processedNotification) {
this.emit('sendNotification', processedNotification); this.emit('sendNotification', processedNotification);
console.log(`[NotificationProcessor] Emitting sendNotification for ${setting.channel_type} (Setting ID: ${setting.id}, Event: ${eventType})`); console.log(`[NotificationProcessor] 正在为 ${setting.channel_type} 发送 sendNotification (设置 ID: ${setting.id}, 事件: ${eventType})`);
} }
} catch (error) { } catch (error) {
console.error(`[NotificationProcessor] Error preparing notification for setting ID ${setting.id} and event ${eventType}:`, error); console.error(`[NotificationProcessor] 为设置 ID ${setting.id} 和事件 ${eventType} 准备通知时出错:`, error);
} }
} }
@@ -206,7 +206,7 @@ class NotificationProcessorService extends EventEmitter {
break; break;
default: default:
console.warn(`[NotificationProcessor] Unsupported channel type: ${setting.channel_type}`); console.warn(`[NotificationProcessor] 不支持的通道类型: ${setting.channel_type}`);
return null; return null;
} }
+5 -7
View File
@@ -196,7 +196,7 @@ const fetchRemoteDockerStatus = async (state: ClientState): Promise<{ available:
try { try {
const versionCommand = "docker version --format '{{.Server.Version}}'"; const versionCommand = "docker version --format '{{.Server.Version}}'";
console.log(`[fetchRemoteDockerStatus] Executing: ${versionCommand} on session ${state.ws.sessionId}`); // console.log(`[fetchRemoteDockerStatus] Executing: ${versionCommand} on session ${state.ws.sessionId}`);
const { stdout: versionStdout, stderr: versionStderr } = await new Promise<{ stdout: string; stderr: string }>((resolve, reject) => { const { stdout: versionStdout, stderr: versionStderr } = await new Promise<{ stdout: string; stderr: string }>((resolve, reject) => {
let stdout = ''; let stdout = '';
let stderr = ''; let stderr = '';
@@ -225,7 +225,7 @@ const fetchRemoteDockerStatus = async (state: ClientState): Promise<{ available:
if (versionStdout.trim()) { if (versionStdout.trim()) {
console.log(`[fetchRemoteDockerStatus] Docker version check successful on session ${state.ws.sessionId}. Version: ${versionStdout.trim()}`); // console.log(`[fetchRemoteDockerStatus] Docker version check successful on session ${state.ws.sessionId}. Version: ${versionStdout.trim()}`);
isDockerCmdAvailable = true; isDockerCmdAvailable = true;
} else { } else {
@@ -244,7 +244,7 @@ const fetchRemoteDockerStatus = async (state: ClientState): Promise<{ available:
try { try {
const psCommand = "docker ps -a --no-trunc --format '{{json .}}'"; const psCommand = "docker ps -a --no-trunc --format '{{json .}}'";
console.log(`[fetchRemoteDockerStatus] Executing: ${psCommand} on session ${state.ws.sessionId}`); // console.log(`[fetchRemoteDockerStatus] Executing: ${psCommand} on session ${state.ws.sessionId}`);
const { stdout: psStdout, stderr: psStderr } = await new Promise<{ stdout: string; stderr: string }>((resolve, reject) => { const { stdout: psStdout, stderr: psStderr } = await new Promise<{ stdout: string; stderr: string }>((resolve, reject) => {
let stdout = ''; let stdout = '';
let stderr = ''; let stderr = '';
@@ -316,7 +316,7 @@ const fetchRemoteDockerStatus = async (state: ClientState): Promise<{ available:
try { try {
const statsCommand = `docker stats ${runningContainerIds.join(' ')} --no-stream --format '{{json .}}'`; const statsCommand = `docker stats ${runningContainerIds.join(' ')} --no-stream --format '{{json .}}'`;
console.log(`[fetchRemoteDockerStatus] Executing: ${statsCommand} on session ${state.ws.sessionId}`); // console.log(`[fetchRemoteDockerStatus] Executing: ${statsCommand} on session ${state.ws.sessionId}`);
const { stdout: statsStdout, stderr: statsStderr } = await new Promise<{ stdout: string; stderr: string }>((resolve, reject) => { const { stdout: statsStdout, stderr: statsStderr } = await new Promise<{ stdout: string; stderr: string }>((resolve, reject) => {
let stdout = ''; let stdout = '';
let stderr = ''; let stderr = '';
@@ -354,7 +354,7 @@ const fetchRemoteDockerStatus = async (state: ClientState): Promise<{ available:
console.warn(`[fetchRemoteDockerStatus] Error executing docker stats for session ${state.ws.sessionId}:`, error); console.warn(`[fetchRemoteDockerStatus] Error executing docker stats for session ${state.ws.sessionId}:`, error);
} }
} else { } else {
console.log(`[fetchRemoteDockerStatus] No running containers found on session ${state.ws.sessionId}, skipping docker stats.`); // console.log(`[fetchRemoteDockerStatus] No running containers found on session ${state.ws.sessionId}, skipping docker stats.`);
} }
@@ -838,8 +838,6 @@ export const initializeWebSocket = async (server: http.Server, sessionParser: Re
ws.send(JSON.stringify({ type: 'sftp_error', payload: { message: `SFTP 操作 ${type} 缺少 requestId` } })); ws.send(JSON.stringify({ type: 'sftp_error', payload: { message: `SFTP 操作 ${type} 缺少 requestId` } }));
return; return;
} }
// TODO: 在这里或 SftpService 内部添加 SFTP 操作的审计日志记录 (可选)
// 例如: auditLogService.logAction('SFTP_ACTION', { type, path: payload?.path, userId: ws.userId, ip: state.ipAddress });
try { try {
switch (type) { switch (type) {
case 'sftp:readdir': case 'sftp:readdir':