This commit is contained in:
Baobhan Sith
2025-05-10 01:23:47 +08:00
parent c36e961426
commit 61e7b64333
13 changed files with 653 additions and 276 deletions
@@ -1,5 +1,6 @@
import { ref, readonly, type Ref, ComputedRef } from 'vue';
import { useI18n } from 'vue-i18n'; // +++ Add import for useI18n +++
import { useI18n } from 'vue-i18n';
import { sessions as globalSessionsRef } from '../stores/session/state'; // +++ 导入全局 sessions state +++
// import { useWebSocketConnection } from './useWebSocketConnection'; // 移除全局导入
import type { Terminal } from 'xterm';
import type { SearchAddon, ISearchOptions } from '@xterm/addon-search'; // *** 移除 ISearchResult 导入 ***
@@ -74,18 +75,40 @@ export function createSshTerminalManager(sessionId: string, wsDeps: SshTerminalD
// }
// --- 添加日志:检查缓冲区处理 ---
console.log(`[会话 ${sessionId}][SSH前端] handleTerminalReady: 准备处理缓冲区,缓冲区长度: ${terminalOutputBuffer.value.length}`);
if (terminalOutputBuffer.value.length > 0) {
console.log(`[会话 ${sessionId}][SSH前端] handleTerminalReady: 缓冲区内容 (前100字符):`, terminalOutputBuffer.value.map(d => d.substring(0, 100)).join(' | '));
}
// console.log(`[会话 ${sessionId}][SSH前端] handleTerminalReady: 准备处理缓冲区,缓冲区长度: ${terminalOutputBuffer.value.length}`);
// if (terminalOutputBuffer.value.length > 0) {
// console.log(`[会话 ${sessionId}][SSH前端] handleTerminalReady: 内部缓冲区内容 (前100字符):`, terminalOutputBuffer.value.map(d => d.substring(0, 100)).join(' | '));
// }
// ---------------------------------
// 将缓冲区的输出写入终端
terminalOutputBuffer.value.forEach(data => {
console.log(`[会话 ${sessionId}][SSH前端] handleTerminalReady: 正在写入缓冲数据 (前100字符):`, data.substring(0, 100));
term.write(data);
});
console.log(`[会话 ${sessionId}][SSH前端] handleTerminalReady: 缓冲区处理完成。`);
terminalOutputBuffer.value = []; // 清空缓冲区
// 1. 处理 SessionState.pendingOutput (来自 SSH_OUTPUT_CACHED_CHUNK 的早期数据)
const currentSessionState = globalSessionsRef.value.get(sessionId);
if (currentSessionState && currentSessionState.pendingOutput && currentSessionState.pendingOutput.length > 0) {
// console.log(`[会话 ${sessionId}][SSH终端模块] 发现 SessionState.pendingOutput,长度: ${currentSessionState.pendingOutput.length}。正在写入...`);
currentSessionState.pendingOutput.forEach(data => {
term.write(data);
});
currentSessionState.pendingOutput = []; // 清空
// console.log(`[会话 ${sessionId}][SSH终端模块] SessionState.pendingOutput 处理完毕。`);
// 如果之前因为 pendingOutput 而将 isResuming 保持为 true,现在可以考虑更新
if (currentSessionState.isResuming) {
// 检查 isLastChunk 是否已收到 (这部分逻辑在 handleSshOutputCachedChunk 中,这里仅作标记清除)
// 假设所有缓存块都已处理完毕
// console.log(`[会话 ${sessionId}][SSH终端模块] 所有 pendingOutput 已写入,清除 isResuming 标记。`);
currentSessionState.isResuming = false;
}
}
// 2. 将此管理器内部缓冲的输出 (terminalOutputBuffer, 来自 ssh:output) 写入终端
if (terminalOutputBuffer.value.length > 0) {
terminalOutputBuffer.value.forEach(data => {
// console.log(`[会话 ${sessionId}][SSH前端] handleTerminalReady: 正在写入内部缓冲数据 (前100字符):`, data.substring(0, 100));
term.write(data);
});
// console.log(`[会话 ${sessionId}][SSH前端] handleTerminalReady: 内部缓冲区处理完成。`);
terminalOutputBuffer.value = []; // 清空内部缓冲区
}
// 可以在这里自动聚焦或执行其他初始化操作
// term.focus(); // 也许在 ssh:connected 时聚焦更好
};
@@ -13,12 +13,20 @@ export type WsConnectionStatus = WsConnectionStatusType;
* @param {string} sessionId - 此 WebSocket 连接关联的会话 ID (用于日志记录)。
* @param {string} dbConnectionId - 此 WebSocket 连接关联的数据库连接 ID (用于后端识别)。
* @param {Function} t - i18n 翻译函数,从父组件传入
* @param {object} [options] - 可选参数对象
* @param {boolean} [options.isResumeFlow=false] - 指示此连接是否用于 SSH 恢复流程
* @returns 一个包含状态和方法的 WebSocket 连接管理器对象。
*/
export function createWebSocketConnectionManager(sessionId: string, dbConnectionId: string, t: ReturnType<typeof useI18n>['t']) { // +++ Update type of t +++
export function createWebSocketConnectionManager(
sessionId: string,
dbConnectionId: string,
t: ReturnType<typeof useI18n>['t'],
options?: { isResumeFlow?: boolean }
) {
// --- Instance State ---
// 每个实例拥有独立的 WebSocket 对象、状态和消息处理器
const ws = shallowRef<WebSocket | null>(null); // WebSocket 实例
const isResumeFlow = options?.isResumeFlow ?? false; // 获取恢复流程标志
const connectionStatus = ref<WsConnectionStatus>('disconnected'); // 连接状态 (使用导出的类型)
const statusMessage = ref<string>(''); // 状态描述文本
const isSftpReady = ref<boolean>(false); // SFTP 是否就绪
@@ -167,15 +175,24 @@ export function createWebSocketConnectionManager(sessionId: string, dbConnection
reconnectAttempts = 0; // 连接成功,重置尝试次数
statusMessage.value = getStatusText('wsConnected');
// 状态保持 'connecting' 直到收到 ssh:connected
// 发送后端所需的初始连接消息,包含数据库连接 ID
sendMessage({ type: 'ssh:connect', payload: { connectionId: instanceDbConnectionId } });
if (!isResumeFlow) {
// 对于普通连接,发送 ssh:connect 并等待 ssh:connected 来更新状态
sendMessage({ type: 'ssh:connect', payload: { connectionId: instanceDbConnectionId } });
} else {
// 对于恢复流程,WebSocket 打开即表示连接基础已建立
// 后续的 SSH_SUSPEND_RESUME_REQUEST 会完成会话的恢复
connectionStatus.value = 'connected';
console.log(`[WebSocket ${instanceSessionId}] 恢复流程:WebSocket 打开,状态直接设为 connected。`);
}
dispatchMessage('internal:opened', {}, { type: 'internal:opened' }); // 触发内部打开事件
};
ws.value.onmessage = (event: MessageEvent) => {
try {
const message: WebSocketMessage = JSON.parse(event.data);
// console.debug(`[WebSocket ${instanceSessionId}] 收到:`, message.type);
const rawData = event.data;
// console.log(`[WebSocket ${instanceSessionId}] onmessage: 收到原始数据 (类型: ${typeof rawData}, 长度: ${rawData.toString().length}) 前100字符:`, rawData.toString().substring(0, 100));
const message: WebSocketMessage = JSON.parse(rawData.toString());
// console.log(`[WebSocket ${instanceSessionId}] onmessage: 解析后消息类型: ${message.type}, 会话ID (消息内): ${message.sessionId || 'N/A'}, Payload keys: ${message.payload ? Object.keys(message.payload).join(', ') : 'N/A'}`);
// --- 更新此实例的连接状态 ---
if (message.type === 'ssh:connected') {
@@ -293,8 +310,9 @@ export function createWebSocketConnectionManager(sessionId: string, dbConnection
if (ws.value && ws.value.readyState === WebSocket.OPEN) {
try {
const messageString = JSON.stringify(message);
// console.debug(`[WebSocket ${instanceSessionId}] 发送:`, message.type);
// console.log(`[WebSocket ${instanceSessionId}] sendMessage: 准备发送消息。类型: ${message.type}, 会话ID (消息内): ${message.sessionId || 'N/A'}, Payload keys: ${message.payload ? Object.keys(message.payload).join(', ') : 'N/A'}`);
ws.value.send(messageString);
// console.log(`[WebSocket ${instanceSessionId}] sendMessage: 消息已发送。类型: ${message.type}`);
} catch (e) {
console.error(`[WebSocket ${instanceSessionId}] 序列化或发送消息失败:`, e, message);
}