refactor: 重构 WebSocket 模块实现解耦
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
import { AuthenticatedWebSocket } from '../types';
|
||||
import { clientStates, sftpService } from '../state';
|
||||
import WebSocket from 'ws';
|
||||
|
||||
export async function handleSftpOperation(
|
||||
ws: AuthenticatedWebSocket,
|
||||
type: string,
|
||||
payload: any,
|
||||
requestId?: string
|
||||
): Promise<void> {
|
||||
const sessionId = ws.sessionId;
|
||||
const state = sessionId ? clientStates.get(sessionId) : undefined;
|
||||
|
||||
if (!sessionId || !state) {
|
||||
console.warn(`WebSocket: 收到来自 ${ws.username} 的 SFTP 请求 (${type}),但无活动会话。`);
|
||||
const errPayload: { message: string; requestId?: string } = { message: '无效的会话' };
|
||||
if (requestId) errPayload.requestId = requestId;
|
||||
if (ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify({ type: 'sftp_error', payload: errPayload }));
|
||||
return;
|
||||
}
|
||||
if (!requestId) {
|
||||
console.error(`WebSocket: 收到来自 ${ws.username} (会话: ${sessionId}) 的 SFTP 请求 (${type}),但缺少 requestId。`);
|
||||
if (ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify({ type: 'sftp_error', payload: { message: `SFTP 操作 ${type} 缺少 requestId` } }));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
switch (type) {
|
||||
case 'sftp:readdir':
|
||||
if (payload?.path) sftpService.readdir(sessionId, payload.path, requestId);
|
||||
else throw new Error("Missing 'path' in payload for readdir");
|
||||
break;
|
||||
case 'sftp:stat':
|
||||
if (payload?.path) sftpService.stat(sessionId, payload.path, requestId);
|
||||
else throw new Error("Missing 'path' in payload for stat");
|
||||
break;
|
||||
case 'sftp:readfile':
|
||||
if (payload?.path) {
|
||||
const requestedEncoding = payload?.encoding;
|
||||
sftpService.readFile(sessionId, payload.path, requestId, requestedEncoding);
|
||||
} else {
|
||||
throw new Error("Missing 'path' in payload for readfile");
|
||||
}
|
||||
break;
|
||||
case 'sftp:writefile':
|
||||
const fileContent = payload?.content ?? payload?.data ?? '';
|
||||
const encoding = payload?.encoding;
|
||||
if (payload?.path) {
|
||||
const dataToSend = (typeof fileContent === 'string') ? fileContent : '';
|
||||
sftpService.writefile(sessionId, payload.path, dataToSend, requestId, encoding);
|
||||
} else throw new Error("Missing 'path' in payload for writefile");
|
||||
break;
|
||||
case 'sftp:mkdir':
|
||||
if (payload?.path) sftpService.mkdir(sessionId, payload.path, requestId);
|
||||
else throw new Error("Missing 'path' in payload for mkdir");
|
||||
break;
|
||||
case 'sftp:rmdir':
|
||||
if (payload?.path) sftpService.rmdir(sessionId, payload.path, requestId);
|
||||
else throw new Error("Missing 'path' in payload for rmdir");
|
||||
break;
|
||||
case 'sftp:unlink':
|
||||
if (payload?.path) sftpService.unlink(sessionId, payload.path, requestId);
|
||||
else throw new Error("Missing 'path' in payload for unlink");
|
||||
break;
|
||||
case 'sftp:rename':
|
||||
if (payload?.oldPath && payload?.newPath) sftpService.rename(sessionId, payload.oldPath, payload.newPath, requestId);
|
||||
else throw new Error("Missing 'oldPath' or 'newPath' in payload for rename");
|
||||
break;
|
||||
case 'sftp:chmod':
|
||||
if (payload?.path && typeof payload?.mode === 'number') sftpService.chmod(sessionId, payload.path, payload.mode, requestId);
|
||||
else throw new Error("Missing 'path' or invalid 'mode' in payload for chmod");
|
||||
break;
|
||||
case 'sftp:realpath':
|
||||
if (payload?.path) sftpService.realpath(sessionId, payload.path, requestId);
|
||||
else throw new Error("Missing 'path' in payload for realpath");
|
||||
break;
|
||||
case 'sftp:copy':
|
||||
if (Array.isArray(payload?.sources) && payload?.destination) {
|
||||
sftpService.copy(sessionId, payload.sources, payload.destination, requestId);
|
||||
} else throw new Error("Missing 'sources' (array) or 'destination' in payload for copy");
|
||||
break;
|
||||
case 'sftp:move':
|
||||
if (Array.isArray(payload?.sources) && payload?.destination) {
|
||||
sftpService.move(sessionId, payload.sources, payload.destination, requestId);
|
||||
} else throw new Error("Missing 'sources' (array) or 'destination' in payload for move");
|
||||
break;
|
||||
default:
|
||||
console.warn(`WebSocket: Received unhandled SFTP message type in sftp.handler: ${type}`);
|
||||
if (ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify({ type: 'sftp_error', payload: { message: `内部未处理的 SFTP 类型: ${type}`, requestId } }));
|
||||
throw new Error(`Unhandled SFTP type: ${type}`);
|
||||
}
|
||||
} catch (sftpCallError: any) {
|
||||
console.error(`WebSocket: Error preparing/calling SFTP service for ${type} (Request ID: ${requestId}):`, sftpCallError);
|
||||
if (ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify({ type: 'sftp_error', payload: { message: `处理 SFTP 请求 ${type} 时出错: ${sftpCallError.message}`, requestId } }));
|
||||
}
|
||||
}
|
||||
|
||||
export function handleSftpUploadStart(ws: AuthenticatedWebSocket, payload: any): void {
|
||||
const sessionId = ws.sessionId;
|
||||
const state = sessionId ? clientStates.get(sessionId) : undefined;
|
||||
|
||||
if (!sessionId || !state) {
|
||||
console.warn(`WebSocket: 收到来自 ${ws.username} 的 SFTP 上传开始请求,但无活动会话。`);
|
||||
if (ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify({ type: 'sftp:upload:error', payload: { uploadId: payload?.uploadId, message: '无效的会话' } }));
|
||||
return;
|
||||
}
|
||||
if (!payload?.uploadId || !payload?.remotePath || typeof payload?.size !== 'number') {
|
||||
console.error(`WebSocket: 收到来自 ${ws.username} (会话: ${sessionId}) 的 sftp:upload:start 请求,但缺少 uploadId, remotePath 或 size。`);
|
||||
if (ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify({ type: 'sftp:upload:error', payload: { uploadId: payload?.uploadId, message: '缺少 uploadId, remotePath 或 size' } }));
|
||||
return;
|
||||
}
|
||||
const relativePath = payload?.relativePath;
|
||||
console.log(`WebSocket: SFTP Upload Start - Session: ${sessionId}, UploadID: ${payload.uploadId}, RemotePath: ${payload.remotePath}, Size: ${payload.size}, RelativePath: ${relativePath}`);
|
||||
sftpService.startUpload(sessionId, payload.uploadId, payload.remotePath, payload.size, relativePath);
|
||||
}
|
||||
|
||||
export async function handleSftpUploadChunk(ws: AuthenticatedWebSocket, payload: any): Promise<void> {
|
||||
const sessionId = ws.sessionId;
|
||||
const state = sessionId ? clientStates.get(sessionId) : undefined;
|
||||
if (!sessionId || !state) return; // Silently ignore if session is gone
|
||||
|
||||
if (!payload?.uploadId || typeof payload?.chunkIndex !== 'number' || !payload?.data) {
|
||||
console.error(`WebSocket: 收到来自 ${ws.username} (会话: ${sessionId}) 的 sftp:upload:chunk 请求,但缺少 uploadId, chunkIndex 或 data。`);
|
||||
// Optionally send error to client, but be mindful of flooding for many chunks
|
||||
return;
|
||||
}
|
||||
await sftpService.handleUploadChunk(sessionId, payload.uploadId, payload.chunkIndex, payload.data);
|
||||
}
|
||||
|
||||
export function handleSftpUploadCancel(ws: AuthenticatedWebSocket, payload: any): void {
|
||||
const sessionId = ws.sessionId;
|
||||
const state = sessionId ? clientStates.get(sessionId) : undefined;
|
||||
if (!sessionId || !state) return; // Silently ignore
|
||||
|
||||
if (!payload?.uploadId) {
|
||||
console.error(`WebSocket: 收到来自 ${ws.username} (会话: ${sessionId}) 的 sftp:upload:cancel 请求,但缺少 uploadId。`);
|
||||
if (ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify({ type: 'sftp:upload:error', payload: { uploadId: payload?.uploadId, message: '缺少 uploadId' } }));
|
||||
return;
|
||||
}
|
||||
sftpService.cancelUpload(sessionId, payload.uploadId);
|
||||
}
|
||||
Reference in New Issue
Block a user