52b797837e
Refs #28
170 lines
9.9 KiB
TypeScript
170 lines
9.9 KiB
TypeScript
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;
|
|
case 'sftp:compress':
|
|
if (Array.isArray(payload?.sources) && payload?.destination && payload?.format && requestId) {
|
|
const destinationPath = payload.destination as string;
|
|
// 从 destinationPath 中提取 targetDirectory 和 destinationArchiveName
|
|
// pathModule.posix 总是使用 / 作为分隔符
|
|
const pathModule = await import('path'); // 动态导入 path 模块
|
|
const targetDirectory = pathModule.posix.dirname(destinationPath);
|
|
const destinationArchiveName = pathModule.posix.basename(destinationPath);
|
|
|
|
const compressPayload = {
|
|
sources: payload.sources as string[],
|
|
destinationArchiveName: destinationArchiveName,
|
|
format: payload.format as 'zip' | 'targz' | 'tarbz2',
|
|
targetDirectory: targetDirectory,
|
|
requestId: requestId
|
|
};
|
|
sftpService.compress(sessionId, compressPayload);
|
|
} else throw new Error("Missing 'sources' (array), 'destination', 'format', or 'requestId' in payload for compress");
|
|
break;
|
|
case 'sftp:decompress':
|
|
if (payload?.source && requestId) {
|
|
const decompressPayload = {
|
|
archivePath: payload.source as string,
|
|
// destinationDirectory: payload.destination as string, // sftpService.decompress 目前不使用此参数
|
|
requestId: requestId
|
|
};
|
|
sftpService.decompress(sessionId, decompressPayload);
|
|
} else throw new Error("Missing 'source' or 'requestId' in payload for decompress");
|
|
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);
|
|
} |