feat: 添加挂起会话导出日志功能
This commit is contained in:
@@ -403,6 +403,50 @@ export class SshSuspendService extends EventEmitter {
|
||||
// 确保所有已缓冲的日志已尝试写入 (通常由 'data' 事件处理,这里是最终状态确认)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定挂起会话的日志内容。
|
||||
* 允许导出 'disconnected_by_backend' 和 'hanging' 状态的会话日志。
|
||||
* @param userId 用户ID。
|
||||
* @param suspendSessionId 要导出日志的挂起会话ID。
|
||||
* @returns Promise<{ content: string, filename: string } | null> 日志内容和建议的文件名,如果会话不符合条件或读取失败则返回null。
|
||||
*/
|
||||
async getSessionLogContent(userId: number, suspendSessionId: string): Promise<{ content: string, filename: string } | null> {
|
||||
console.log(`[SshSuspendService][用户: ${userId}] getSessionLogContent 调用,suspendSessionId: ${suspendSessionId}`);
|
||||
const userSessions = this.getUserSessions(userId);
|
||||
const session = userSessions.get(suspendSessionId);
|
||||
|
||||
if (!session) {
|
||||
console.warn(`[SshSuspendService][用户: ${userId}] getSessionLogContent: 未找到挂起的会话 ${suspendSessionId}。`);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (session.backendSshStatus !== 'disconnected_by_backend' && session.backendSshStatus !== 'hanging') {
|
||||
console.warn(`[SshSuspendService][用户: ${userId}] getSessionLogContent: 会话 ${suspendSessionId} 状态为 ${session.backendSshStatus},不符合导出条件 (需要 'disconnected_by_backend' 或 'hanging')。`);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!session.tempLogPath) {
|
||||
console.error(`[SshSuspendService][用户: ${userId}] getSessionLogContent: 会话 ${suspendSessionId} 缺少 tempLogPath。`);
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const logContent = await this.logStorageService.readLog(session.tempLogPath);
|
||||
console.log(`[SshSuspendService][用户: ${userId}] getSessionLogContent: 已读取挂起会话 ${suspendSessionId} (日志: ${session.tempLogPath}) 的数据,长度: ${logContent.length}`);
|
||||
|
||||
const baseName = session.customSuspendName || session.connectionName || suspendSessionId.substring(0,8);
|
||||
const safeBaseName = baseName.replace(/[^\w.-]/g, '_'); // 替换掉不安全字符为空格或下划线
|
||||
const timestamp = new Date(session.suspendStartTime).toISOString().replace(/[:.]/g, '-');
|
||||
// tempLogPath 通常是 originalSessionId
|
||||
const filename = `ssh_log_${safeBaseName}_${session.tempLogPath}_${timestamp}.log`;
|
||||
|
||||
return { content: logContent, filename };
|
||||
} catch (error) {
|
||||
console.error(`[SshSuspendService][用户: ${userId}] getSessionLogContent: 读取挂起会话 ${suspendSessionId} (日志: ${session.tempLogPath}) 失败:`, error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 单例模式导出
|
||||
|
||||
@@ -12,6 +12,7 @@ export class SshSuspendController {
|
||||
this.terminateAndRemoveSession = this.terminateAndRemoveSession.bind(this);
|
||||
this.removeSessionEntry = this.removeSessionEntry.bind(this);
|
||||
this.editSessionNameHttp = this.editSessionNameHttp.bind(this); // 绑定新方法
|
||||
this.exportSessionLog = this.exportSessionLog.bind(this); // +++ 绑定导出日志方法 +++
|
||||
}
|
||||
|
||||
public async getSuspendedSshSessions(req: Request, res: Response): Promise<void> {
|
||||
@@ -142,4 +143,41 @@ export class SshSuspendController {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async exportSessionLog(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const userId = req.session.userId;
|
||||
const { suspendSessionId } = req.params;
|
||||
|
||||
if (!userId) {
|
||||
res.status(401).json({ message: 'Unauthorized. User ID not found in session.' });
|
||||
return;
|
||||
}
|
||||
if (!suspendSessionId) {
|
||||
res.status(400).json({ message: 'Bad Request. suspendSessionId parameter is missing.' });
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`[SshSuspendController] exportSessionLog called for user ID: ${userId}, suspendSessionId: ${suspendSessionId}`);
|
||||
|
||||
const logData = await sshSuspendService.getSessionLogContent(userId, suspendSessionId);
|
||||
|
||||
if (logData) {
|
||||
res.setHeader('Content-Disposition', `attachment; filename="${logData.filename}"`);
|
||||
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
|
||||
res.send(logData.content);
|
||||
} else {
|
||||
// sshSuspendService.getSessionLogContent 会记录详细的警告/错误
|
||||
// 如果会话不存在,或者状态不符合导出条件,或者读取日志失败
|
||||
res.status(404).json({ message: `Failed to export log for session ${suspendSessionId}. It might not exist, not be in a valid state for export, or log reading failed.` });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`[SshSuspendController] Error exporting session log for user ID: ${req.session.userId}, suspendSessionId: ${req.params.suspendSessionId}:`, error);
|
||||
if (error instanceof Error) {
|
||||
res.status(500).json({ message: 'Failed to export suspended session log', error: error.message });
|
||||
} else {
|
||||
res.status(500).json({ message: 'Failed to export suspended session log', error: 'Unknown error' });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,4 +34,11 @@ router.put(
|
||||
sshSuspendController.editSessionNameHttp // 新的控制器方法
|
||||
);
|
||||
|
||||
// Route to export the log of a suspended session
|
||||
router.get(
|
||||
'/log/:suspendSessionId',
|
||||
isAuthenticated,
|
||||
sshSuspendController.exportSessionLog
|
||||
);
|
||||
|
||||
export default router;
|
||||
Reference in New Issue
Block a user