From 9a7cfc40610c34a528b2132f9d3ba9c269f06f1b Mon Sep 17 00:00:00 2001 From: lijiahangmax Date: Sat, 28 Jun 2025 01:23:53 +0800 Subject: [PATCH 01/19] =?UTF-8?q?:tada:=20=E9=87=8D=E6=9E=84=E4=BC=A0?= =?UTF-8?q?=E8=BE=93=E6=A8=A1=E5=9D=97.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- orion-visor-ui/src/utils/file.ts | 11 ++ .../components/transfer/transfer-drawer.vue | 25 ++- .../{transfer-item.vue => transfer-task.vue} | 61 +++--- .../view/sftp/sftp-upload-modal.vue | 3 +- .../components/view/sftp/sftp-view.vue | 7 +- .../src/views/terminal/interfaces/session.ts | 5 +- .../transfer/base-file-transfer-task.ts | 62 ++++++ .../transfer/sftp-base-transfer-task.ts | 91 +++++++++ .../transfer/sftp-file-download-task.ts | 57 ++++++ .../service/transfer/sftp-file-upload-task.ts | 74 +++++++ .../transfer/sftp-transfer-downloader.ts | 48 ----- .../service/transfer/sftp-transfer-handler.ts | 115 ----------- .../service/transfer/sftp-transfer-manager.ts | 185 ++++++------------ .../transfer/sftp-transfer-uploader.ts | 64 ------ .../src/views/terminal/types/const.ts | 10 + 15 files changed, 427 insertions(+), 391 deletions(-) rename orion-visor-ui/src/views/terminal/components/transfer/{transfer-item.vue => transfer-task.vue} (65%) create mode 100644 orion-visor-ui/src/views/terminal/service/transfer/base-file-transfer-task.ts create mode 100644 orion-visor-ui/src/views/terminal/service/transfer/sftp-base-transfer-task.ts create mode 100644 orion-visor-ui/src/views/terminal/service/transfer/sftp-file-download-task.ts create mode 100644 orion-visor-ui/src/views/terminal/service/transfer/sftp-file-upload-task.ts delete mode 100644 orion-visor-ui/src/views/terminal/service/transfer/sftp-transfer-downloader.ts delete mode 100644 orion-visor-ui/src/views/terminal/service/transfer/sftp-transfer-handler.ts delete mode 100644 orion-visor-ui/src/views/terminal/service/transfer/sftp-transfer-uploader.ts diff --git a/orion-visor-ui/src/utils/file.ts b/orion-visor-ui/src/utils/file.ts index ef1994e7..9fb24bc2 100644 --- a/orion-visor-ui/src/utils/file.ts +++ b/orion-visor-ui/src/utils/file.ts @@ -41,6 +41,17 @@ export function readFileText(e: File, encoding = 'UTF-8'): Promise { }); } +// 关闭 fileReader +export function closeFileReader(reader: FileReader) { + // 清理资源 + if (reader.readyState === FileReader.LOADING) { + reader.abort(); + } + reader.onload = null; + reader.onerror = null; + reader.onabort = null; +} + /** * 解析路径类型 */ diff --git a/orion-visor-ui/src/views/terminal/components/transfer/transfer-drawer.vue b/orion-visor-ui/src/views/terminal/components/transfer/transfer-drawer.vue index fed074de..933547cb 100644 --- a/orion-visor-ui/src/views/terminal/components/transfer/transfer-drawer.vue +++ b/orion-visor-ui/src/views/terminal/components/transfer/transfer-drawer.vue @@ -37,7 +37,7 @@ - {{ transferManager.transferList.filter(s => s.status === option.value).length }} + {{ transferTasks.filter(s => s.state.status === option.value).length }} @@ -48,15 +48,15 @@ max-height="100%" :hoverable="true" :bordered="false" - :data="transferManager.transferList"> + :data="transferTasks"> @@ -70,13 +70,13 @@ diff --git a/orion-visor-ui/src/views/terminal/components/transfer/transfer-item.vue b/orion-visor-ui/src/views/terminal/components/transfer/transfer-task.vue similarity index 65% rename from orion-visor-ui/src/views/terminal/components/transfer/transfer-item.vue rename to orion-visor-ui/src/views/terminal/components/transfer/transfer-task.vue index 691d92e5..43e39d1b 100644 --- a/orion-visor-ui/src/views/terminal/components/transfer/transfer-item.vue +++ b/orion-visor-ui/src/views/terminal/components/transfer/transfer-task.vue @@ -4,46 +4,46 @@
- - + +
- {{ item.name }} + :title="task.fileItem.name" + @click="copy(task.fileItem.name)"> + {{ task.fileItem.name }} - {{ getFileSize(item.currentSize) }} - / + {{ getFileSize(task.state.currentSize) }} + / - {{ getFileSize(item.totalSize) }} + {{ getFileSize(task.state.totalSize) }} - - {{ item.progress }}% + + {{ task.state.progress }}% - {{ item.parentPath }} + :title="task.fileItem.parentPath" + @click="copy(task.fileItem.parentPath)"> + {{ task.fileItem.parentPath }} - + :content="task.state.errorMessage"> - {{ item.errorMessage }} + {{ task.state.errorMessage }}
@@ -52,18 +52,20 @@
- + + + + :status="getDictValue(transferStatusKey, task.state.status, 'status')" + :percent="task.state.currentSize / task.state.totalSize" />
- +
@@ -74,21 +76,21 @@ diff --git a/orion-visor-ui/src/views/terminal/components/view/sftp/sftp-upload-modal.vue b/orion-visor-ui/src/views/terminal/components/view/sftp/sftp-upload-modal.vue index 01aef427..70faef55 100644 --- a/orion-visor-ui/src/views/terminal/components/view/sftp/sftp-upload-modal.vue +++ b/orion-visor-ui/src/views/terminal/components/view/sftp/sftp-upload-modal.vue @@ -121,8 +121,7 @@ // 获取上传的文件 const files = fileList.value.map(s => s.file as File); // 普通上传 - transferManager.addUpload(hostId.value, parentPath.value, files); - Message.success('已开始上传, 点击右侧传输列表查看进度'); + await transferManager.sftp.addUpload(hostId.value, parentPath.value, files); // 清空 handlerClear(); return true; diff --git a/orion-visor-ui/src/views/terminal/components/view/sftp/sftp-view.vue b/orion-visor-ui/src/views/terminal/components/view/sftp/sftp-view.vue index 8631f57d..5d932dbe 100644 --- a/orion-visor-ui/src/views/terminal/components/view/sftp/sftp-view.vue +++ b/orion-visor-ui/src/views/terminal/components/view/sftp/sftp-view.vue @@ -160,7 +160,7 @@ }; // 下载文件 - const downloadFiles = (paths: Array, clear: boolean) => { + const downloadFiles = async (paths: Array, clear: boolean) => { if (!paths.length) { return; } @@ -172,10 +172,9 @@ if (clear) { selectFiles.value = []; } - Message.success('已开始下载, 点击右侧传输列表查看进度'); // 添加普通文件到下载队列 const normalFiles = files.filter(s => !s.isDir); - transferManager.addDownload(props.item.hostId as number, currentPath.value, normalFiles); + await transferManager.sftp.addDownload(props.item.hostId as number, currentPath.value, normalFiles); // 将文件夹展开普通文件 const directoryPaths = files.filter(s => s.isDir).map(s => s.path); if (directoryPaths.length) { @@ -283,7 +282,7 @@ if (!checkResult(result, msg)) { return; } - transferManager.addDownload(props.item.hostId as number, currentPath, list); + transferManager.sftp.addDownload(props.item.hostId as number, currentPath, list); }; // 初始化会话 diff --git a/orion-visor-ui/src/views/terminal/interfaces/session.ts b/orion-visor-ui/src/views/terminal/interfaces/session.ts index 59123bc3..e6b5a1b1 100644 --- a/orion-visor-ui/src/views/terminal/interfaces/session.ts +++ b/orion-visor-ui/src/views/terminal/interfaces/session.ts @@ -113,7 +113,7 @@ export interface ITerminalSession; + + protected constructor(type: string, source: string, + hostId: number, sessionKey: string, + fileItem: FileTransferItem, + state: Partial) { + this.type = type; + this.source = source; + this.fileId = nextId(10); + this.hostId = hostId; + this.sessionKey = sessionKey; + this.fileItem = fileItem; + this.state = reactive({ + currentSize: 0, + totalSize: fileItem.size, + progress: 0, + status: TransferStatus.WAITING, + errorMessage: undefined, + finished: false, + aborted: false, + ...state, + }); + } + + // 开始 + abstract start(): void; + + // 完成 + abstract finish(): void; + + // 失败 + abstract error(): void; + + // 中断 + abstract abort(): void; + + // 传输完成回调 + abstract onFinish(): void; + + // 传输失败回调 + abstract onError(msg: string | undefined): void; + + // 传输中断回调 + abstract onAbort(): void; + +} diff --git a/orion-visor-ui/src/views/terminal/service/transfer/sftp-base-transfer-task.ts b/orion-visor-ui/src/views/terminal/service/transfer/sftp-base-transfer-task.ts new file mode 100644 index 00000000..5466c9e7 --- /dev/null +++ b/orion-visor-ui/src/views/terminal/service/transfer/sftp-base-transfer-task.ts @@ -0,0 +1,91 @@ +import type { ISetTransferClient, FileTransferItem } from '@/views/terminal/interfaces'; +import { TransferOperator, TransferStatus, TerminalMessages, TransferSource } from '../../types/const'; +import { getPath } from '@/utils/file'; +import BaseFileTransferTask from './base-file-transfer-task'; + +// sftp 传输任务一定义 +export default abstract class SftpBaseTransferTask extends BaseFileTransferTask implements ISetTransferClient { + + protected client?: WebSocket; + + protected constructor(type: string, + hostId: number, + fileItem: FileTransferItem) { + super(type, TransferSource.SFTP, hostId, undefined as unknown as string, fileItem, {}); + } + + // 设置传输客户端 + setClient(client: WebSocket) { + this.client = client; + } + + // 开始 + start() { + this.state.status = TransferStatus.TRANSFERRING; + // 发送开始信息 + this.client?.send(JSON.stringify({ + operator: TransferOperator.START, + type: this.type, + hostId: this.hostId, + path: getPath(this.fileItem.parentPath + '/' + this.fileItem.name), + paths: this.fileItem.paths, + })); + }; + + // 完成 + finish() { + this.state.finished = true; + this.state.progress = 100; + this.state.status = TransferStatus.SUCCESS; + // 发送完成的信息 + this.client?.send(JSON.stringify({ + operator: TransferOperator.FINISH, + type: this.type, + hostId: this.hostId, + })); + }; + + // 失败 + error() { + this.state.finished = true; + this.state.status = TransferStatus.ERROR; + // 发送上传失败的信息 + this.client?.send(JSON.stringify({ + operator: TransferOperator.ERROR, + type: this.type, + hostId: this.hostId, + })); + }; + + // 中断 + abort() { + this.state.aborted = true; + // 发送中断的信息 + this.client?.send(JSON.stringify({ + operator: TransferOperator.ABORT, + type: this.type, + hostId: this.hostId, + })); + } + + // 失败回调 + onError(msg: string | undefined) { + this.state.finished = true; + this.state.status = TransferStatus.ERROR; + this.state.errorMessage = msg || TerminalMessages.fileTransferError; + } + + // 完成回调 + onFinish() { + this.state.finished = true; + this.state.progress = 100; + this.state.status = TransferStatus.SUCCESS; + this.state.currentSize = this.state.totalSize; + }; + + // 中断回调 + onAbort() { + this.state.aborted = true; + }; + +} diff --git a/orion-visor-ui/src/views/terminal/service/transfer/sftp-file-download-task.ts b/orion-visor-ui/src/views/terminal/service/transfer/sftp-file-download-task.ts new file mode 100644 index 00000000..65d9c70f --- /dev/null +++ b/orion-visor-ui/src/views/terminal/service/transfer/sftp-file-download-task.ts @@ -0,0 +1,57 @@ +import type { FileTransferItem, IFileDownloadTask } from '@/views/terminal/interfaces'; +import { TransferStatus, TerminalMessages } from '../../types/const'; +import { getFileName, openDownloadFile } from '@/utils/file'; +import { saveAs } from 'file-saver'; +import { getDownloadTransferUrl } from '@/api/terminal/terminal-sftp'; +import SftpBaseTransferTask from './sftp-base-transfer-task'; + +// sftp 下载任务实现 +export default class SftpFileDownloadTask extends SftpBaseTransferTask implements IFileDownloadTask { + + constructor(type: string, hostId: number, fileItem: FileTransferItem) { + super(type, hostId, fileItem); + } + + // 开始回调 + onStart(channelId: string, token: string) { + // 获取下载 url + const url = getDownloadTransferUrl(channelId, token); + // 打开 + openDownloadFile(url); + } + + // 进度回调 + onProgress(totalSize: number | undefined, currentSize: number | undefined) { + if (totalSize) { + this.state.totalSize = totalSize; + } + if (currentSize) { + this.state.currentSize = currentSize; + } + }; + + // 完成回调 + onFinish() { + super.onFinish(); + if (this.state.aborted) { + // 中断则不触发下载 + return; + } + if (this.state.totalSize === 0) { + // 空文件直接触发下载 + try { + // 触发下载 + saveAs(new Blob([], { + type: 'application/octet-stream' + }), getFileName(this.fileItem.name)); + this.state.status = TransferStatus.SUCCESS; + } catch (e) { + this.state.status = TransferStatus.ERROR; + this.state.errorMessage = TerminalMessages.fileSaveError; + } + } else { + this.state.status = TransferStatus.SUCCESS; + } + } + +} diff --git a/orion-visor-ui/src/views/terminal/service/transfer/sftp-file-upload-task.ts b/orion-visor-ui/src/views/terminal/service/transfer/sftp-file-upload-task.ts new file mode 100644 index 00000000..4821534d --- /dev/null +++ b/orion-visor-ui/src/views/terminal/service/transfer/sftp-file-upload-task.ts @@ -0,0 +1,74 @@ +import type { FileTransferItem, IFileUploadTask } from '@/views/terminal/interfaces'; +import { closeFileReader } from '@/utils/file'; +import SftpBaseTransferTask from './sftp-base-transfer-task'; + +// 512 KB +export const PART_SIZE = 512 * 1024; + +// sftp 上传任务实现 +export default class SftpFileUploadTask extends SftpBaseTransferTask implements IFileUploadTask { + + private currentPart: number; + private readonly totalPart: number; + + constructor(type: string, hostId: number, fileItem: FileTransferItem) { + super(type, hostId, fileItem); + this.currentPart = 0; + this.totalPart = Math.ceil(fileItem.size / PART_SIZE); + } + + // 上传完成 + finish() { + super.finish(); + // 释放资源 + this.fileItem.file = undefined; + } + + // 上传下一个分片 + async onNextPart() { + // 完成或者中断直接跳过 + if (this.state.aborted || this.state.finished) { + return; + } + if (this.hasNextPart()) { + try { + // 有下一个分片则上传 + await this.uploadNextPart(); + } catch (e) { + // 读取文件失败 + this.error(); + } + } else { + this.finish(); + } + } + + // 执行上传下一分片 + private async uploadNextPart() { + // 读取数据 + const start = this.currentPart * PART_SIZE; + const end = Math.min(this.fileItem.size, start + PART_SIZE); + const chunk = (this.fileItem.file as File).slice(start, end); + const reader = new FileReader(); + try { + const arrayBuffer = await new Promise((resolve, reject) => { + reader.onload = () => resolve(reader.result); + reader.onerror = (error) => reject(error); + reader.readAsArrayBuffer(chunk); + }); + // 发送数据 + this.client?.send(arrayBuffer as ArrayBuffer); + this.currentPart++; + this.state.currentSize += (end - start); + } finally { + // 释放资源 + closeFileReader(reader); + } + } + + // 是否有下一个分片 + private hasNextPart() { + return this.currentPart < this.totalPart; + } + +} diff --git a/orion-visor-ui/src/views/terminal/service/transfer/sftp-transfer-downloader.ts b/orion-visor-ui/src/views/terminal/service/transfer/sftp-transfer-downloader.ts deleted file mode 100644 index 62c67469..00000000 --- a/orion-visor-ui/src/views/terminal/service/transfer/sftp-transfer-downloader.ts +++ /dev/null @@ -1,48 +0,0 @@ -import type { SftpTransferItem } from '@/views/terminal/interfaces'; -import { TransferStatus } from '../../types/const'; -import { getFileName, openDownloadFile } from '@/utils/file'; -import { saveAs } from 'file-saver'; -import { getDownloadTransferUrl } from '@/api/terminal/terminal-sftp'; -import SftpTransferHandler from './sftp-transfer-handler'; - -// sftp 下载器实现 -export default class SftpTransferDownloader extends SftpTransferHandler { - - constructor(type: string, item: SftpTransferItem, client: WebSocket) { - super(type, item, client); - } - - // 开始回调 - onStart(channelId: string, token: string) { - super.onStart(channelId, token); - // 获取下载 url - const url = getDownloadTransferUrl(channelId, token); - // 打开 - openDownloadFile(url); - } - - // 完成回调 - onFinish() { - super.onFinish(); - if (this.aborted) { - // 中断则不触发下载 - return; - } - if (this.item.totalSize === 0) { - // 空文件直接触发下载 - try { - // 触发下载 - saveAs(new Blob([], { - type: 'application/octet-stream' - }), getFileName(this.item.name)); - this.item.status = TransferStatus.SUCCESS; - } catch (e) { - this.item.status = TransferStatus.ERROR; - this.item.errorMessage = '保存失败'; - } - } else { - this.item.status = TransferStatus.SUCCESS; - } - } - -} diff --git a/orion-visor-ui/src/views/terminal/service/transfer/sftp-transfer-handler.ts b/orion-visor-ui/src/views/terminal/service/transfer/sftp-transfer-handler.ts deleted file mode 100644 index f195cc47..00000000 --- a/orion-visor-ui/src/views/terminal/service/transfer/sftp-transfer-handler.ts +++ /dev/null @@ -1,115 +0,0 @@ -import type { ISftpTransferHandler, SftpTransferItem } from '@/views/terminal/interfaces'; -import { TransferOperator, TransferStatus } from '../../types/const'; -import { getPath } from '@/utils/file'; - -// sftp 传输处理器定义 -export default abstract class SftpTransferHandler implements ISftpTransferHandler { - - public type: string; - public finished: boolean; - public aborted: boolean; - protected client: WebSocket; - protected item: SftpTransferItem; - - protected constructor(type: string, item: SftpTransferItem, client: WebSocket) { - this.type = type; - this.finished = false; - this.aborted = false; - this.item = item; - this.client = client; - } - - // 开始 - start() { - this.item.status = TransferStatus.TRANSFERRING; - // 发送开始信息 - this.client?.send(JSON.stringify({ - operator: TransferOperator.START, - type: this.type, - path: getPath(this.item.parentPath + '/' + this.item.name), - hostId: this.item.hostId, - paths: this.item.paths, - })); - }; - - // 完成 - finish() { - this.finished = true; - this.item.status = TransferStatus.SUCCESS; - // 发送完成的信息 - this.client?.send(JSON.stringify({ - operator: TransferOperator.FINISH, - type: this.type, - hostId: this.item.hostId - })); - }; - - // 失败 - error() { - this.finished = true; - this.item.status = TransferStatus.ERROR; - // 发送上传失败的信息 - this.client?.send(JSON.stringify({ - operator: TransferOperator.ERROR, - type: this.type, - hostId: this.item.hostId - })); - }; - - // 中断 - abort() { - this.aborted = true; - // 发送中断的信息 - this.client?.send(JSON.stringify({ - operator: TransferOperator.ABORT, - type: this.type, - hostId: this.item.hostId - })); - } - - // 是否有下一个分片 - hasNextPart() { - return false; - }; - - // 下一页分片回调 - onNextPart() { - return undefined as unknown as any; - }; - - // 开始回调 - onStart(channelId: string, token: string) { - }; - - // 进度回调 - onProgress(totalSize: number | undefined, currentSize: number | undefined) { - if (this.item) { - if (totalSize) { - this.item.totalSize = totalSize; - } - if (currentSize) { - this.item.currentSize = currentSize; - } - } - }; - - // 失败回调 - onError(msg: string | undefined) { - this.finished = true; - this.item.status = TransferStatus.ERROR; - this.item.errorMessage = msg || '传输失败'; - } - - // 完成回调 - onFinish() { - this.finished = true; - this.item.status = TransferStatus.SUCCESS; - this.item.currentSize = this.item.totalSize; - }; - - // 中断回调 - onAbort() { - this.aborted = true; - }; - -} diff --git a/orion-visor-ui/src/views/terminal/service/transfer/sftp-transfer-manager.ts b/orion-visor-ui/src/views/terminal/service/transfer/sftp-transfer-manager.ts index 2ae31cff..1f1d3c34 100644 --- a/orion-visor-ui/src/views/terminal/service/transfer/sftp-transfer-manager.ts +++ b/orion-visor-ui/src/views/terminal/service/transfer/sftp-transfer-manager.ts @@ -1,105 +1,89 @@ -import type { ISftpTransferHandler, ISftpTransferManager, SftpFile, SftpTransferItem, TransferOperatorResponse } from '@/views/terminal/interfaces'; +import type { + FileTransferTaskType, + ISetTransferClient, + ISftpTransferManager, + MaybeFileTransferTask, + SftpFile, + TransferOperatorResponse +} from '@/views/terminal/interfaces'; import { TerminalMessages, TransferReceiver, TransferStatus, TransferType } from '../../types/const'; import { Message } from '@arco-design/web-vue'; import { getTerminalTransferToken, openTerminalTransferChannel } from '@/api/terminal/terminal'; -import { nextId } from '@/utils'; -import SftpTransferUploader from './sftp-transfer-uploader'; -import SftpTransferDownloader from './sftp-transfer-downloader'; +import BaseTransferManager from './base-transfer-manager'; +import SftpFileUploadTask from './sftp-file-upload-task'; +import SftpFileDownloadTask from './sftp-file-download-task'; // sftp 传输管理器实现 -export default class SftpTransferManager implements ISftpTransferManager { +export default class SftpTransferManager extends BaseTransferManager implements ISftpTransferManager { private client?: WebSocket; private run: boolean; - private progressIntervalId?: any; - - private currentItem?: SftpTransferItem; - - private currentTransfer?: ISftpTransferHandler; - - public transferList: Array; + private currentTask?: FileTransferTaskType; constructor() { + super(); this.run = false; - this.transferList = []; } // 添加上传任务 - addUpload(hostId: number, parentPath: string, files: Array) { - // 转为上传任务 - const items = files.map(s => { - return { - fileId: nextId(10), - type: TransferType.UPLOAD, - hostId: hostId, - name: s.webkitRelativePath || s.name, - currentSize: 0, - totalSize: s.size, - progress: 0, - status: TransferStatus.WAITING, + async addUpload(hostId: number, parentPath: string, files: Array) { + Message.info(TerminalMessages.fileUploading); + // 创建任务 + for (let file of files) { + const task = new SftpFileUploadTask(TransferType.UPLOAD, hostId, { + name: file.webkitRelativePath || file.name, parentPath: parentPath, - file: s - }; - }); + size: file.size, + file, + }); + this.tasks.push(task); + } // 开始传输 - this.startTransfer(items); + await this.startTransfer(); } // 添加下载任务 - addDownload(hostId: number, currentPath: string, files: Array) { + async addDownload(hostId: number, currentPath: string, files: Array) { + Message.info(TerminalMessages.fileDownloading); let pathIndex = currentPath === '/' ? 1 : currentPath.length + 1; - // 转为下载文件 - const items = files.map(s => { - return { - fileId: nextId(10), - type: TransferType.DOWNLOAD, - hostId: hostId, - name: s.path.substring(pathIndex), + for (let file of files) { + // 创建任务 + const task = new SftpFileDownloadTask(TransferType.DOWNLOAD, hostId, { + name: file.path.substring(pathIndex), parentPath: currentPath, - currentSize: 0, - totalSize: s.size, - progress: 0, - status: TransferStatus.WAITING, - }; - }) as Array; + size: file.size, + }); + this.tasks.push(task); + } // 开始传输 - this.startTransfer(items); + await this.startTransfer(); } // 开始传输 - private startTransfer(items: Array) { - this.transferList.push(...items); + private async startTransfer() { // 开始传输 if (!this.run) { - this.openClient(); + await this.openClient(); } + // 开始计算进度 + this.resetProgressTimer(); } // 取消传输 cancelTransfer(fileId: string): void { - const index = this.transferList.findIndex(s => s.fileId === fileId); + const index = this.tasks.findIndex(s => s.fileId === fileId); if (index === -1) { return; } - const item = this.transferList[index]; - if (item.status === TransferStatus.TRANSFERRING) { + const task = this.tasks[index]; + if (task.state.status === TransferStatus.TRANSFERRING) { // 传输中则中断传输 - this.currentTransfer?.abort(); + this.currentTask?.abort(); } // 从列表中移除 - this.transferList.splice(index, 1); - } - - // 取消全部传输 - cancelAllTransfer(): void { - // 从列表中移除非传输中的元素 - this.transferList.reduceRight((_, value: SftpTransferItem, index: number) => { - if (value.status !== TransferStatus.TRANSFERRING) { - this.transferList.splice(index, 1); - } - }, null as any); + this.tasks.splice(index, 1); } // 打开会话 @@ -115,11 +99,10 @@ export default class SftpTransferManager implements ISftpTransferManager { Message.error('会话打开失败'); console.error('transfer error', e); // 将等待中和传输中任务修改为失败状态 - this.transferList.filter(s => { - return s.status === TransferStatus.WAITING - || s.status === TransferStatus.TRANSFERRING; + this.tasks.filter(s => { + return s.state.status === TransferStatus.WAITING || s.state.status === TransferStatus.TRANSFERRING; }).forEach(s => { - s.status = TransferStatus.ERROR; + s.state.status = TransferStatus.ERROR; }); // 关闭会话 this.close(); @@ -132,100 +115,62 @@ export default class SftpTransferManager implements ISftpTransferManager { }; // 处理消息 this.client.onmessage = this.resolveMessage.bind(this); - // 计算传输进度 - this.progressIntervalId = setInterval(this.calcProgress.bind(this), 500); // 打开后自动传输下一个任务 this.transferNextItem(); } - // 计算传输进度 - private calcProgress() { - this.transferList.forEach(item => { - if (item.totalSize != 0) { - item.progress = (item.currentSize / item.totalSize * 100).toFixed(2); - } - }); - } - // 传输下一条任务 private transferNextItem() { - this.currentTransfer = undefined; - // 释放内存 - if (this.currentItem) { - this.currentItem.file = null as unknown as File; - } // 获取任务 - this.currentItem = this.transferList.find(s => s.status === TransferStatus.WAITING); - if (this.currentItem) { - // 创建传输器 - this.currentTransfer = this.createTransfer(); + this.currentTask = this.tasks.find(s => s.state.status === TransferStatus.WAITING); + if (this.currentTask) { + // 设置 client + (this.currentTask as unknown as ISetTransferClient).setClient(this.client as WebSocket); // 开始 - this.currentTransfer?.start(); + this.currentTask?.start(); } else { // 无任务关闭会话 this.client?.close(); } } - // 创建传输器 - private createTransfer(): ISftpTransferHandler | undefined { - if (!this.currentItem) { - return undefined; - } - if (this.currentItem.type === TransferType.UPLOAD) { - // 上传 - return new SftpTransferUploader(TransferType.UPLOAD, this.currentItem, this.client as WebSocket); - } else if (this.currentItem.type === TransferType.DOWNLOAD) { - // 下载 - return new SftpTransferDownloader(TransferType.DOWNLOAD, this.currentItem, this.client as WebSocket); - } - } - // 接收消息 private async resolveMessage(message: MessageEvent) { // 文本消息 const data = JSON.parse(message.data) as TransferOperatorResponse; if (data.type === TransferReceiver.NEXT_PART) { // 接收下一块数据回调 - await this.currentTransfer?.onNextPart(); + await (this.currentTask as MaybeFileTransferTask)?.onNextPart?.(); } else if (data.type === TransferReceiver.START) { - // 开始回调 - this.currentTransfer?.onStart(data.channelId as string, data.transferToken as string); + // 开始下载回调 + (this.currentTask as MaybeFileTransferTask)?.onStart?.(data.channelId as string, data.transferToken as string); } else if (data.type === TransferReceiver.PROGRESS) { - // 进度回调 - this.currentTransfer?.onProgress(data.totalSize, data.currentSize); + // 下载进度回调 + (this.currentTask as MaybeFileTransferTask)?.onProgress?.(data.totalSize, data.currentSize); } else if (data.type === TransferReceiver.FINISH) { // 完成回调 - this.currentTransfer?.onFinish(); + this.currentTask?.onFinish(); // 开始下一个传输任务 this.transferNextItem(); } else if (data.type === TransferReceiver.ERROR) { // 失败回调 - this.currentTransfer?.onError(data.msg); + this.currentTask?.onError(data.msg); // 开始下一个传输任务 this.transferNextItem(); } else if (data.type === TransferReceiver.ABORT) { // 中断回调 - this.currentTransfer?.onAbort(); + this.currentTask?.onAbort(); // 开始下一个传输任务 this.transferNextItem(); } } // 关闭 释放资源 - private close() { + protected close() { // 重置 run this.run = false; - // 关闭传输进度 - clearInterval(this.progressIntervalId); - // 进行中和等待中的文件改为失败 - this.transferList.forEach(s => { - if (s.status === TransferStatus.WAITING || - s.status === TransferStatus.TRANSFERRING) { - s.status = TransferStatus.ERROR; - s.errorMessage = TerminalMessages.sessionClosed; - } - }); + // 关闭 + super.close(); } } diff --git a/orion-visor-ui/src/views/terminal/service/transfer/sftp-transfer-uploader.ts b/orion-visor-ui/src/views/terminal/service/transfer/sftp-transfer-uploader.ts deleted file mode 100644 index c507b1d4..00000000 --- a/orion-visor-ui/src/views/terminal/service/transfer/sftp-transfer-uploader.ts +++ /dev/null @@ -1,64 +0,0 @@ -import type { SftpTransferItem } from '@/views/terminal/interfaces'; -import SftpTransferHandler from './sftp-transfer-handler'; - -// 512 KB -export const PART_SIZE = 512 * 1024; - -// sftp 上传器实现 -export default class SftpTransferUploader extends SftpTransferHandler { - - private currentPart: number; - private readonly totalPart: number; - private file: File; - - constructor(type: string, item: SftpTransferItem, client: WebSocket) { - super(type, item, client); - this.file = item.file; - this.currentPart = 0; - this.totalPart = Math.ceil(item.file.size / PART_SIZE); - } - - // 是否有下一个分片 - hasNextPart() { - return this.currentPart < this.totalPart; - } - - // 上传下一个分片 - async onNextPart() { - super.onNextPart(); - // 完成或者中断直接跳过 - if (this.aborted || this.finished) { - return; - } - if (this.hasNextPart()) { - try { - // 有下一个分片则上传 - await this.doUploadNextPart(); - } catch (e) { - // 读取文件失败 - this.error(); - } - } else { - this.finish(); - } - } - - // 执行上传下一分片 - private async doUploadNextPart() { - // 读取数据 - const start = this.currentPart * PART_SIZE; - const end = Math.min(this.file.size, start + PART_SIZE); - const chunk = this.file.slice(start, end); - const reader = new FileReader(); - const arrayBuffer = await new Promise((resolve, reject) => { - reader.onload = () => resolve(reader.result); - reader.onerror = (error) => reject(error); - reader.readAsArrayBuffer(chunk); - }); - // 发送数据 - this.client?.send(arrayBuffer as ArrayBuffer); - this.currentPart++; - this.item.currentSize += (end - start); - } - -} diff --git a/orion-visor-ui/src/views/terminal/types/const.ts b/orion-visor-ui/src/views/terminal/types/const.ts index 66327d8b..4750ebc2 100644 --- a/orion-visor-ui/src/views/terminal/types/const.ts +++ b/orion-visor-ui/src/views/terminal/types/const.ts @@ -98,6 +98,10 @@ export const TerminalMessages = { waitingReconnect: '输入回车重新连接...', loggedElsewhere: '该账号已在另一台设备登录', rdpConnectTimeout: '请检查远程计算机网络及其他配置是否正常', + fileTransferError: '传输失败', + fileSaveError: '保存失败', + fileUploading: '已开始上传, 点击右侧传输列表查看进度', + fileDownloading: '已开始下载, 点击右侧传输列表查看进度', }; // 文件类型 @@ -457,6 +461,12 @@ export const TransferType = { DOWNLOAD: 'download', }; +// 传输来源 +export const TransferSource = { + SFTP: 'sftp', + RDP: 'rdp', +}; + // 传输操作 export const TransferOperator = { START: 'start', From 13288941884a06a507a89722828bfa36ab606546 Mon Sep 17 00:00:00 2001 From: lijiahangmax Date: Sat, 28 Jun 2025 01:25:54 +0800 Subject: [PATCH 02/19] =?UTF-8?q?:hammer:=20=E6=B7=BB=E5=8A=A0=20RDP=20?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E5=8A=9F=E8=83=BD.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/store/modules/terminal/index.ts | 4 +- .../src/store/modules/terminal/types.ts | 4 +- .../components/view/rdp/rdp-action-bar.vue | 7 +- .../src/views/terminal/interfaces/transfer.ts | 137 +++++++++----- .../service/transfer/base-transfer-manager.ts | 67 +++++++ .../service/transfer/rdp-file-upload-task.ts | 174 ++++++++++++++++++ .../service/transfer/rdp-transfer-manager.ts | 68 +++++++ 7 files changed, 407 insertions(+), 54 deletions(-) create mode 100644 orion-visor-ui/src/views/terminal/service/transfer/base-transfer-manager.ts create mode 100644 orion-visor-ui/src/views/terminal/service/transfer/rdp-file-upload-task.ts create mode 100644 orion-visor-ui/src/views/terminal/service/transfer/rdp-transfer-manager.ts diff --git a/orion-visor-ui/src/store/modules/terminal/index.ts b/orion-visor-ui/src/store/modules/terminal/index.ts index 3e32a5fb..a934a692 100644 --- a/orion-visor-ui/src/store/modules/terminal/index.ts +++ b/orion-visor-ui/src/store/modules/terminal/index.ts @@ -34,7 +34,7 @@ import { TerminalSessionTypes, TerminalTabs } from '@/views/terminal/types/const import TerminalTabManager from '@/views/terminal/service/tab/terminal-tab-manager'; import TerminalPanelManager from '@/views/terminal/service/tab/terminal-panel-manager'; import TerminalSessionManager from '@/views/terminal/service/session/terminal-session-manager'; -import SftpTransferManager from '@/views/terminal/service/transfer/sftp-transfer-manager'; +import TerminalTransferManager from '@/views/terminal/service/transfer/terminal-transfer-manager'; // 终端偏好项 export const TerminalPreferenceItem = { @@ -90,7 +90,7 @@ export default defineStore('terminal', { tabManager: new TerminalTabManager(), panelManager: new TerminalPanelManager(), sessionManager: markRaw(new TerminalSessionManager()), - transferManager: new SftpTransferManager(), + transferManager: new TerminalTransferManager(), }), actions: { diff --git a/orion-visor-ui/src/store/modules/terminal/types.ts b/orion-visor-ui/src/store/modules/terminal/types.ts index 7a6e84a6..7c1a5119 100644 --- a/orion-visor-ui/src/store/modules/terminal/types.ts +++ b/orion-visor-ui/src/store/modules/terminal/types.ts @@ -1,4 +1,4 @@ -import type { ISftpTransferManager, ITerminalPanelManager, ITerminalSessionManager, ITerminalTabManager, TerminalTheme } from '@/views/terminal/interfaces'; +import type { ITerminalPanelManager, ITerminalSessionManager, ITerminalTabManager, ITerminalTransferManager, TerminalTheme } from '@/views/terminal/interfaces'; import type { AuthorizedHostQueryResponse } from '@/api/asset/asset-authorized-data'; export interface TerminalState { @@ -8,7 +8,7 @@ export interface TerminalState { tabManager: ITerminalTabManager; panelManager: ITerminalPanelManager; sessionManager: ITerminalSessionManager; - transferManager: ISftpTransferManager; + transferManager: ITerminalTransferManager; } // 终端配置 diff --git a/orion-visor-ui/src/views/terminal/components/view/rdp/rdp-action-bar.vue b/orion-visor-ui/src/views/terminal/components/view/rdp/rdp-action-bar.vue index d3dd25bc..3dd6642b 100644 --- a/orion-visor-ui/src/views/terminal/components/view/rdp/rdp-action-bar.vue +++ b/orion-visor-ui/src/views/terminal/components/view/rdp/rdp-action-bar.vue @@ -138,7 +138,6 @@ } from '@/views/terminal/types/const'; import { computed, ref, watch, onMounted } from 'vue'; import { setAutoFocus } from '@/utils/dom'; - import { Message } from '@arco-design/web-vue'; import { saveAs } from 'file-saver'; import { readText } from '@/hooks/copy'; import { useTerminalStore, useDictStore } from '@/store'; @@ -150,7 +149,7 @@ direction: string; }>(); - const { preference } = useTerminalStore(); + const { preference, transferManager } = useTerminalStore(); const { toOptions, getDictValue } = useDictStore(); const { visible, setVisible } = useVisible(); @@ -276,8 +275,8 @@ // 上传文件 const uploadFile = () => { - // TODO 上传功能 - Message.warning('暂不支持文件上传, 等下版本携带'); + transferManager.rdp.addUpload(props.session, fileList.value[0].file as File); + fileList.value = []; }; // 选择文件回调 diff --git a/orion-visor-ui/src/views/terminal/interfaces/transfer.ts b/orion-visor-ui/src/views/terminal/interfaces/transfer.ts index 4055813d..8f538b71 100644 --- a/orion-visor-ui/src/views/terminal/interfaces/transfer.ts +++ b/orion-visor-ui/src/views/terminal/interfaces/transfer.ts @@ -1,42 +1,84 @@ -import type { SftpFile } from '@/views/terminal/interfaces'; +import type { IRdpSession, SftpFile } from '@/views/terminal/interfaces'; +import type { Reactive } from 'vue'; +import type Guacamole from 'guacamole-common-js'; + +// 终端文件传输管理器定义 +export interface ITerminalTransferManager { + sftp: ISftpTransferManager; + rdp: IRdpTransferManager; +} + +// 文件传输文件项 +export interface FileTransferItem { + name: string; + parentPath: string; + size: number; + file?: File; + paths?: Array; + unknownSize?: boolean; +} + +// 文件传输文件项 +export interface FileTransferReactiveState { + currentSize: number, + totalSize: number; + progress: number | string; + status: string; + errorMessage?: string; + finished: boolean; + aborted: boolean; +} + +// 文件传输管理器定义 +export interface ITransferManager { + // 传输的文件列表 + tasks: Array>; -// sftp 传输管理器定义 -export interface ISftpTransferManager { - transferList: Array; - // 添加上传任务 - addUpload: (hostId: number, parentPath: string, files: Array) => void; - // 添加下载任务 - addDownload: (hostId: number, currentPath: string, files: Array) => void; // 取消传输 cancelTransfer: (fileId: string) => void; - // 取消全部传输 + // 取消全部执行中的传输 cancelAllTransfer: () => void; } -// sftp 传输处理回调定义 -export interface ISftpTransferCallback { - // 下一分片回调 - onNextPart: () => Promise; - // 开始回调 - onStart: (channelId: string, token: string) => void; - // 进度回调 - onProgress: (totalSize: number | undefined, currentSize: number | undefined) => void; - // 失败回调 - onError: (msg: string | undefined) => void; - // 完成回调 - onFinish: () => void; - // 中断回调 - onAbort: () => void; +// SFTP 文件传输管理器定义 +export interface ISftpTransferManager extends ITransferManager { + // 添加上传任务 + addUpload: (hostId: number, parentPath: string, files: Array) => Promise; + // 添加下载任务 + addDownload: (hostId: number, currentPath: string, files: Array) => Promise; } -// sftp 传输处理器定义 -export interface ISftpTransferHandler extends ISftpTransferCallback { - // 类型 +// RDP 文件传输管理器定义 +export interface IRdpTransferManager extends ITransferManager { + // 添加上传任务 + addUpload: (session: IRdpSession, file: File) => Promise; + // 添加下载任务 + addDownload: (session: IRdpSession, stream: Guacamole.InputStream, mimetype: string, name: string) => void; + // 通过 sessionKey 关闭 + closeBySessionKey: (sessionKey: string) => void; +} + +// 文件传输任务类型 +export type FileTransferTaskType = IFileUploadTask | IFileDownloadTask; +export type MaybeFileTransferTask = IFileUploadTask & IFileDownloadTask; + +// 设置传输客户端 +export interface ISetTransferClient { + setClient: (client: T) => void; +} + +// 文件传输任务定义 +export interface IFileTransferTask { type: string; - // 是否完成 - finished: boolean; - // 是否中断 - aborted: boolean; + source: string; + fileId: string; + hostId: number; + sessionKey: string; + // 文件项 + fileItem: FileTransferItem; + // 状态 + state: Reactive; + // 开始 start: () => void; // 完成 @@ -45,24 +87,27 @@ export interface ISftpTransferHandler extends ISftpTransferCallback { error: () => void; // 中断 abort: () => void; - // 是否有下一个分片 - hasNextPart: () => boolean; + + // 传输完成回调 + onFinish: () => void; + // 传输失败回调 + onError: (msg: string | undefined) => void; + // 传输中断回调 + onAbort: () => void; } -// sftp 上传文件项 -export interface SftpTransferItem { - fileId: string; - type: string; - hostId: number; - name: string; - parentPath: string; - currentSize: number, - totalSize: number; - progress: number | string; - status: string; - errorMessage?: string; - file: File; - paths?: Array; +// 文件上传任务定义 +export interface IFileUploadTask extends IFileTransferTask { + // 请求上传下一个分片 + onNextPart: () => Promise; +} + +// 文件下载任务定义 +export interface IFileDownloadTask extends IFileTransferTask { + // 开始下载回调 + onStart: (channelId: string, token: string) => void; + // 下载进度回调 + onProgress: (totalSize: number | undefined, currentSize: number | undefined) => void; } // 传输操作响应 diff --git a/orion-visor-ui/src/views/terminal/service/transfer/base-transfer-manager.ts b/orion-visor-ui/src/views/terminal/service/transfer/base-transfer-manager.ts new file mode 100644 index 00000000..991a3059 --- /dev/null +++ b/orion-visor-ui/src/views/terminal/service/transfer/base-transfer-manager.ts @@ -0,0 +1,67 @@ +import type { ITransferManager, FileTransferTaskType } from '@/views/terminal/interfaces'; +import { TransferStatus, TerminalMessages } from '@/views/terminal/types/const'; + +// 传输管理器基类 +export default abstract class BaseTransferManager implements ITransferManager { + + public tasks: Array; + + protected progressIntervalId?: number; + + protected constructor() { + this.tasks = []; + } + + // 取消传输 + abstract cancelTransfer(fileId: string): void; + + // 取消全部执行中的传输 + cancelAllTransfer(): void { + // 从列表中移除非传输中的元素 + this.tasks.reduceRight((_, value: FileTransferTaskType, index: number) => { + if (value.state.status !== TransferStatus.TRANSFERRING) { + this.tasks.splice(index, 1); + } + }, null as any); + } + + // 计算传输进度 + protected calculateProgress(): void { + let count = 0; + this.tasks.forEach(task => { + const state = task.state; + if (state.totalSize !== 0 + && (state.status === TransferStatus.WAITING || state.status === TransferStatus.TRANSFERRING) + && task.fileItem.unknownSize !== true) { + count++; + state.progress = (state.currentSize / state.totalSize * 100).toFixed(2); + } + }); + // 如果所有任务都已结束则关闭 + if (count === 0) { + clearInterval(this.progressIntervalId); + } + }; + + // 重置进度定时器 + protected resetProgressTimer(): void { + clearInterval(this.progressIntervalId); + this.progressIntervalId = window.setInterval(this.calculateProgress.bind(this), 500); + } + + // 关闭 + protected close(): void { + // 关闭传输进度 + clearInterval(this.progressIntervalId); + // 进行中和等待中的文件改为失败 + this.tasks.forEach(task => { + const state = task.state; + if (state.status === TransferStatus.WAITING || + state.status === TransferStatus.TRANSFERRING) { + state.status = TransferStatus.ERROR; + state.errorMessage = TerminalMessages.sessionClosed; + } + }); + } + +} diff --git a/orion-visor-ui/src/views/terminal/service/transfer/rdp-file-upload-task.ts b/orion-visor-ui/src/views/terminal/service/transfer/rdp-file-upload-task.ts new file mode 100644 index 00000000..c0ac9052 --- /dev/null +++ b/orion-visor-ui/src/views/terminal/service/transfer/rdp-file-upload-task.ts @@ -0,0 +1,174 @@ +import type { FileTransferItem, IFileUploadTask, IRdpSession } from '@/views/terminal/interfaces'; +import { TransferType, TransferSource, TerminalMessages, TransferStatus } from '@/views/terminal/types/const'; +import { closeFileReader } from '@/utils/file'; +import BaseFileTransferTask from './base-file-transfer-task'; +import Guacamole from 'guacamole-common-js'; + +// 6048 +export const PART_SIZE = Guacamole.ArrayBufferWriter.DEFAULT_BLOB_LENGTH; + +// rdp 上传任务实现 +export default class RdpFileUploadTask extends BaseFileTransferTask implements IFileUploadTask { + + private session: IRdpSession; + private writer?: Guacamole.ArrayBufferWriter; + private stream?: Guacamole.OutputStream; + private currentPart: number; + private readonly totalPart: number; + + constructor(session: IRdpSession, fileItem: FileTransferItem) { + super(TransferType.UPLOAD, TransferSource.RDP, session.info.hostId, session.sessionKey, fileItem, {}); + this.session = session; + this.currentPart = 0; + this.totalPart = Math.ceil(fileItem.size / PART_SIZE); + } + + // 开始上传 + start() { + this.state.status = TransferStatus.TRANSFERRING; + try { + const file = this.fileItem.file as File; + const client = this.session.client; + // 创建文件流 + this.stream = client.createFileStream(file.type, file.name); + this.writer = new Guacamole.ArrayBufferWriter(this.stream); + // 分片上传完成回调 + this.writer.onack = ({ code, message }) => { + // 成功继续上传分片 + if (code === Guacamole.Status.Code.SUCCESS) { + this.onNextPart(); + return; + } + // 失败关闭流 + if (this.stream) { + this.stream.sendEnd(); + } + // 响应错误 + this.onError(message); + }; + // 开始上传分片 + this.onNextPart(); + } catch (e) { + this.onError(TerminalMessages.fileTransferError); + } + } + + // 上传中断 + abort() { + this.onAbort(); + } + + // 上传完成 + finish() { + this.onFinish(); + } + + // 上传失败 + error(): void { + this.onError(undefined as unknown as string); + } + + // 上传下一个分片 + async onNextPart() { + // 完成或者中断直接跳过 + if (this.state.aborted || this.state.finished) { + return; + } + if (this.hasNextPart()) { + try { + // 有下一个分片则上传 + await this.uploadNextPart(); + } catch (e) { + // 读取文件失败 + this.error(); + } + } else { + this.finish(); + } + } + + // 执行上传下一分片 + private async uploadNextPart() { + // 读取数据 + const start = this.currentPart * PART_SIZE; + const end = Math.min(this.fileItem.size, start + PART_SIZE); + const chunk = (this.fileItem.file as File).slice(start, end); + const reader = new FileReader(); + try { + const arrayBuffer = await new Promise((resolve, reject) => { + reader.onload = () => resolve(reader.result); + reader.onerror = (error) => reject(error); + reader.readAsArrayBuffer(chunk); + }); + // 发送数据 + this.writer?.sendData(arrayBuffer as ArrayBuffer); + this.currentPart++; + this.state.currentSize += (end - start); + } finally { + // 释放资源 + closeFileReader(reader); + } + } + + // 上传中断 + onAbort() { + if (this.state.aborted || this.state.finished) { + return; + } + this.state.aborted = true; + try { + if (this.session.status.connected) { + // 关闭流 + if (this.stream) { + this.stream.sendEnd(); + } + } + // 触发失败 + this.onError(TerminalMessages.sessionClosed); + } catch (e) { + // 触发失败 + this.onError(TerminalMessages.fileTransferError); + } + } + + // 上传完成 + onFinish() { + if (this.state.aborted || this.state.finished) { + return; + } + this.state.finished = true; + this.state.progress = 100; + this.state.status = TransferStatus.SUCCESS; + // 关闭流 + if (this.stream) { + this.stream.sendEnd(); + } + // 释放资源 + this.releaseResource(); + } + + // 上传错误回调 + onError(msg: string | undefined) { + this.state.finished = true; + this.state.status = TransferStatus.ERROR; + this.state.errorMessage = msg || TerminalMessages.fileTransferError; + // 释放资源 + this.releaseResource(); + } + + // 是否有下一个分片 + private hasNextPart() { + return this.currentPart < this.totalPart; + } + + // 释放资源 + private releaseResource() { + if (this.writer) { + this.writer.onack = null; + } + this.writer = undefined; + this.stream = undefined; + this.fileItem.file = undefined; + } + +} diff --git a/orion-visor-ui/src/views/terminal/service/transfer/rdp-transfer-manager.ts b/orion-visor-ui/src/views/terminal/service/transfer/rdp-transfer-manager.ts new file mode 100644 index 00000000..ed04e56f --- /dev/null +++ b/orion-visor-ui/src/views/terminal/service/transfer/rdp-transfer-manager.ts @@ -0,0 +1,68 @@ +import type { IRdpTransferManager, IRdpSession } from '@/views/terminal/interfaces'; +import type Guacamole from 'guacamole-common-js'; +import { TerminalMessages } from '../../types/const'; +import { Message } from '@arco-design/web-vue'; +import BaseTransferManager from './base-transfer-manager'; +import RdpFileDownloadTask from './rdp-file-download-task'; +import RdpFileUploadTask from './rdp-file-upload-task'; + +// RDP 传输管理器实现 +export default class RdpTransferManager extends BaseTransferManager implements IRdpTransferManager { + + constructor() { + super(); + } + + // 添加上传任务 + async addUpload(session: IRdpSession, file: File) { + Message.info(TerminalMessages.fileUploading); + // 创建任务 + const task = new RdpFileUploadTask(session, { + name: file.webkitRelativePath || file.name, + parentPath: session.fileSystemName, + size: file.size, + file, + }); + this.tasks.push(task); + // 开始上传 + task.start(); + // 开始计算进度 + this.resetProgressTimer(); + } + + // 添加下载任务 + addDownload(session: IRdpSession, stream: Guacamole.InputStream, mimetype: string, name: string) { + Message.info(TerminalMessages.fileDownloading); + // 创建任务 + const task = new RdpFileDownloadTask(session, stream, mimetype, { + name, + parentPath: session.fileSystemName, + size: 0, + unknownSize: true, + }); + this.tasks.push(task); + // 开始下载 + task.start(); + // 开始计算进度 + this.resetProgressTimer(); + } + + // 取消传输 + cancelTransfer(fileId: string): void { + const index = this.tasks.findIndex(s => s.fileId === fileId); + if (index === -1) { + return; + } + // 中断 + this.tasks[index].abort(); + // 从列表中移除 + this.tasks.splice(index, 1); + } + + // 通过 sessionKey 关闭 + closeBySessionKey(sessionKey: string): void { + this.tasks.filter(s => s.sessionKey === sessionKey) + .forEach(s => s.onError(TerminalMessages.sessionClosed)); + } + +} From da766f0b85cd00a3290fe33818eb4a61d699ebd7 Mon Sep 17 00:00:00 2001 From: lijiahangmax Date: Sat, 28 Jun 2025 01:26:27 +0800 Subject: [PATCH 03/19] =?UTF-8?q?:hammer:=20=E6=B7=BB=E5=8A=A0=20RDP=20?= =?UTF-8?q?=E4=B8=8B=E8=BD=BD=E5=8A=9F=E8=83=BD.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../terminal/service/session/rdp-session.ts | 33 ++++- .../transfer/rdp-file-download-task.ts | 127 ++++++++++++++++++ .../transfer/terminal-transfer-manager.ts | 17 +++ 3 files changed, 170 insertions(+), 7 deletions(-) create mode 100644 orion-visor-ui/src/views/terminal/service/transfer/rdp-file-download-task.ts create mode 100644 orion-visor-ui/src/views/terminal/service/transfer/terminal-transfer-manager.ts diff --git a/orion-visor-ui/src/views/terminal/service/session/rdp-session.ts b/orion-visor-ui/src/views/terminal/service/session/rdp-session.ts index 8e063769..93033afd 100644 --- a/orion-visor-ui/src/views/terminal/service/session/rdp-session.ts +++ b/orion-visor-ui/src/views/terminal/service/session/rdp-session.ts @@ -1,15 +1,15 @@ import type { - GuacdInitConfig, - GuacdReactiveSessionStatus, IGuacdChannel, IRdpSession, - IRdpSessionClipboardHandler, + TerminalSessionTabItem, + GuacdInitConfig, IRdpSessionDisplayHandler, - TerminalSessionTabItem + GuacdReactiveSessionStatus, + IRdpSessionClipboardHandler } from '@/views/terminal/interfaces'; import type { OutputPayload } from '@/views/terminal/types/protocol'; import { InputProtocol } from '@/views/terminal/types/protocol'; -import { fitDisplayValue, TerminalCloseCode, TerminalMessages } from '@/views/terminal/types/const'; +import { TerminalMessages, fitDisplayValue, TerminalCloseCode } from '@/views/terminal/types/const'; import { screenshot } from '@/views/terminal/types/utils'; import { useTerminalStore } from '@/store'; import Guacamole from 'guacamole-common-js'; @@ -25,6 +25,8 @@ export const CONNECT_TIMEOUT = 10000; // RDP 会话实现 export default class RdpSession extends BaseSession implements IRdpSession { + public fileSystemName: string; + public config: GuacdInitConfig; public client: Guacamole.Client; @@ -40,6 +42,7 @@ export default class RdpSession extends BaseSession { + if (fileSystemName) { + this.fileSystemName = fileSystemName; + } + }; + // 下载文件回调 + this.client.onfile = (stream, mimetype, filename) => { + useTerminalStore().transferManager.rdp.addDownload(this, stream, mimetype, filename); + }; } // 连接会话 @@ -209,6 +220,14 @@ export default class RdpSession extends BaseSession this.onProgress(undefined, len); + // 结束回调 + this.reader.onend = this.onFinish.bind(this); + } catch (e) { + this.onError(TerminalMessages.fileTransferError); + } + } + + // 进度发生变化 + onProgress(_: number | undefined, currentSize: number | undefined): void { + if (this.state.aborted || this.state.finished) { + return; + } + // 设置进度 + this.state.totalSize += currentSize || 0; + this.state.currentSize += currentSize || 0; + // 发送 ACK + if (this.stream) { + this.stream.sendAck('Received', Guacamole.Status.Code.SUCCESS); + } + } + + // 完成回调 + onFinish(): void { + if (this.state.aborted || this.state.finished) { + return; + } + this.state.finished = true; + this.state.progress = 100; + this.state.status = TransferStatus.SUCCESS; + // 完成下载文件 + const blob = this.reader.getBlob(); + saveAs(blob, this.fileItem.name); + // 释放资源 + this.releaseResource(); + } + + // 下载终端回调 + onAbort(): void { + if (this.state.aborted || this.state.finished) { + return; + } + this.state.aborted = true; + try { + if (this.session.status.connected) { + if (this.stream) { + // 发送 ACK + this.stream.sendAck('Aborted', Guacamole.Status.Code.RESOURCE_CLOSED); + // 关闭流 + this.session.client.endStream(this.stream.index); + } + } + // 触发失败 + this.onError(TerminalMessages.sessionClosed); + } catch (e) { + // 触发失败 + this.onError(TerminalMessages.fileTransferError); + } + } + + // 下载失败回调 + onError(msg: string | undefined): void { + this.state.finished = true; + this.state.status = TransferStatus.ERROR; + this.state.errorMessage = msg || TerminalMessages.fileTransferError; + // 释放资源 + this.releaseResource(); + } + + // 释放资源 + private releaseResource() { + if (this.stream) { + this.stream.onend = null; + this.stream.onblob = null; + } + this.stream = undefined as unknown as Guacamole.InputStream; + this.reader = undefined as unknown as Guacamole.BlobReader; + } + +} diff --git a/orion-visor-ui/src/views/terminal/service/transfer/terminal-transfer-manager.ts b/orion-visor-ui/src/views/terminal/service/transfer/terminal-transfer-manager.ts new file mode 100644 index 00000000..79f78797 --- /dev/null +++ b/orion-visor-ui/src/views/terminal/service/transfer/terminal-transfer-manager.ts @@ -0,0 +1,17 @@ +import type { ITerminalTransferManager, IRdpTransferManager, ISftpTransferManager } from '@/views/terminal/interfaces'; +import SftpTransferManager from './sftp-transfer-manager'; +import RdpTransferManager from './rdp-transfer-manager'; + +// 传输管理器基类 +export default class TerminalTransferManager implements ITerminalTransferManager { + + public sftp: ISftpTransferManager; + + public rdp: IRdpTransferManager; + + constructor() { + this.sftp = new SftpTransferManager(); + this.rdp = new RdpTransferManager(); + } + +} From f1771ce676edfed6c7acf3c3bd4ab38ac44f77bb Mon Sep 17 00:00:00 2001 From: lijiahangmax Date: Sat, 28 Jun 2025 16:37:14 +0800 Subject: [PATCH 04/19] =?UTF-8?q?:hammer:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E9=80=BB=E8=BE=91.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- orion-visor-ui/src/components/app/navbar/index.vue | 2 +- orion-visor-ui/src/components/exec/log/panel/index.vue | 2 +- .../src/components/exec/log/panel/log-appender.ts | 4 ++-- .../src/components/system/uploader/file-uploader.ts | 10 ++++++++-- .../exec/batch-upload/components/upload-panel.vue | 2 +- .../components/exec-command-log-table.vue | 2 +- .../exec-job-log/components/exec-job-log-table.vue | 2 +- .../exec/upload-task/components/upload-task-table.vue | 2 +- .../service/session/terminal-session-manager.ts | 4 ++-- 9 files changed, 18 insertions(+), 12 deletions(-) diff --git a/orion-visor-ui/src/components/app/navbar/index.vue b/orion-visor-ui/src/components/app/navbar/index.vue index 84c446bc..4b022356 100644 --- a/orion-visor-ui/src/components/app/navbar/index.vue +++ b/orion-visor-ui/src/components/app/navbar/index.vue @@ -331,7 +331,7 @@ // 查询未读消息 pullHasUnreadMessage(); // 注册未读消息轮询 - messageIntervalId.value = setInterval(pullHasUnreadMessage, 30000); + messageIntervalId.value = window.setInterval(pullHasUnreadMessage, 30000); }); onUnmounted(() => { diff --git a/orion-visor-ui/src/components/exec/log/panel/index.vue b/orion-visor-ui/src/components/exec/log/panel/index.vue index c6a31209..2375769a 100644 --- a/orion-visor-ui/src/components/exec/log/panel/index.vue +++ b/orion-visor-ui/src/components/exec/log/panel/index.vue @@ -69,7 +69,7 @@ // 等待一秒后先查询一下状态 setTimeout(pullExecStatus, 1000); // 注册状态轮询 - pullIntervalId.value = setInterval(pullExecStatus, 5000); + pullIntervalId.value = window.setInterval(pullExecStatus, 5000); } // 打开日志 nextTick(() => { diff --git a/orion-visor-ui/src/components/exec/log/panel/log-appender.ts b/orion-visor-ui/src/components/exec/log/panel/log-appender.ts index 45597e91..b00c0e79 100644 --- a/orion-visor-ui/src/components/exec/log/panel/log-appender.ts +++ b/orion-visor-ui/src/components/exec/log/panel/log-appender.ts @@ -172,11 +172,11 @@ export default class LogAppender implements ILogAppender { }; this.client.onmessage = this.processMessage.bind(this); // 注册持久化 - this.keepAliveTask = setInterval(() => { + this.keepAliveTask = window.setInterval(() => { if (this.client?.readyState === WebSocket.OPEN) { this.client?.send('p'); } - }, 15000) as unknown as number; + }, 15000); } // 打开日志 diff --git a/orion-visor-ui/src/components/system/uploader/file-uploader.ts b/orion-visor-ui/src/components/system/uploader/file-uploader.ts index c09b3c1f..aeb45cf9 100644 --- a/orion-visor-ui/src/components/system/uploader/file-uploader.ts +++ b/orion-visor-ui/src/components/system/uploader/file-uploader.ts @@ -2,6 +2,7 @@ import type { FileItem } from '@arco-design/web-vue'; import type { IFileUploader, ResponseMessageBody } from './const'; import { UploadOperatorType, UploadReceiverType } from './const'; import { openFileUploadChannel } from '@/api/system/upload'; +import { closeFileReader } from '@/utils/file'; // 512 KB export const PART_SIZE = 512 * 1024; @@ -81,13 +82,14 @@ export default class FileUploader implements IFileUploader { // 上传下一块数据 private async uploadNextPart() { + let reader = undefined as unknown as FileReader; try { if (this.currentPart < this.totalPart) { // 有下一个分片则上传 const start = this.currentPart++ * PART_SIZE; const end = Math.min(this.currentFile.size, start + PART_SIZE); const chunk = this.currentFile.slice(start, end); - const reader = new FileReader(); + reader = new FileReader(); // 读取数据 const arrayBuffer = await new Promise((resolve, reject) => { reader.onload = () => resolve(reader.result); @@ -107,11 +109,15 @@ export default class FileUploader implements IFileUploader { })); } } catch (e) { - // 读取文件失败 + // 发送读取文件失败 this.client?.send(JSON.stringify({ type: UploadOperatorType.ERROR, fileId: this.currentFileItem.uid, })); + // 释放资源 + if (reader) { + closeFileReader(reader); + } } } diff --git a/orion-visor-ui/src/views/exec/batch-upload/components/upload-panel.vue b/orion-visor-ui/src/views/exec/batch-upload/components/upload-panel.vue index ba334b8e..b3b9613b 100644 --- a/orion-visor-ui/src/views/exec/batch-upload/components/upload-panel.vue +++ b/orion-visor-ui/src/views/exec/batch-upload/components/upload-panel.vue @@ -278,7 +278,7 @@ // 设置轮询状态 onMounted(() => { - pullIntervalId.value = setInterval(pullTaskStatus, 5000); + pullIntervalId.value = window.setInterval(pullTaskStatus, 5000); }); // 卸载状态查询 diff --git a/orion-visor-ui/src/views/exec/exec-command-log/components/exec-command-log-table.vue b/orion-visor-ui/src/views/exec/exec-command-log/components/exec-command-log-table.vue index b5beea43..d1bd6bc7 100644 --- a/orion-visor-ui/src/views/exec/exec-command-log/components/exec-command-log-table.vue +++ b/orion-visor-ui/src/views/exec/exec-command-log/components/exec-command-log-table.vue @@ -429,7 +429,7 @@ // 加载数据 fetchTableData(); // 注册状态轮询 - pullIntervalId.value = setInterval(pullExecStatus, 10000); + pullIntervalId.value = window.setInterval(pullExecStatus, 10000); }); onUnmounted(() => { diff --git a/orion-visor-ui/src/views/exec/exec-job-log/components/exec-job-log-table.vue b/orion-visor-ui/src/views/exec/exec-job-log/components/exec-job-log-table.vue index 40bc2bc0..460f37f7 100644 --- a/orion-visor-ui/src/views/exec/exec-job-log/components/exec-job-log-table.vue +++ b/orion-visor-ui/src/views/exec/exec-job-log/components/exec-job-log-table.vue @@ -417,7 +417,7 @@ // 加载数据 fetchTableData(); // 注册状态轮询 - pullIntervalId.value = setInterval(pullJobStatus, 10000); + pullIntervalId.value = window.setInterval(pullJobStatus, 10000); }); onUnmounted(() => { diff --git a/orion-visor-ui/src/views/exec/upload-task/components/upload-task-table.vue b/orion-visor-ui/src/views/exec/upload-task/components/upload-task-table.vue index a9129fde..7a154336 100644 --- a/orion-visor-ui/src/views/exec/upload-task/components/upload-task-table.vue +++ b/orion-visor-ui/src/views/exec/upload-task/components/upload-task-table.vue @@ -324,7 +324,7 @@ // 加载数据 fetchTableData(); // 注册状态轮询 - pullIntervalId.value = setInterval(pullTaskStatus, 10000); + pullIntervalId.value = window.setInterval(pullTaskStatus, 10000); }); onUnmounted(() => { diff --git a/orion-visor-ui/src/views/terminal/service/session/terminal-session-manager.ts b/orion-visor-ui/src/views/terminal/service/session/terminal-session-manager.ts index 017fe9a8..78fa1d38 100644 --- a/orion-visor-ui/src/views/terminal/service/session/terminal-session-manager.ts +++ b/orion-visor-ui/src/views/terminal/service/session/terminal-session-manager.ts @@ -23,7 +23,7 @@ export default class TerminalSessionManager implements ITerminalSessionManager { public sessions: Array; - private readonly keepAliveTaskId?: any; + private readonly keepAliveTaskId: number; private readonly dispatchFitFn: () => void; @@ -33,7 +33,7 @@ export default class TerminalSessionManager implements ITerminalSessionManager { // 注册 resize 事件 addEventListen(window, 'resize', this.dispatchFitFn); // 注册 ping 事件 - this.keepAliveTaskId = setInterval(this.dispatchPing.bind(this), 15000); + this.keepAliveTaskId = window.setInterval(this.dispatchPing.bind(this), 15000); } // 打开 ssh 会话 From eb81a030e36f19d1a8b4e081d856dc732479e9bf Mon Sep 17 00:00:00 2001 From: lijiahangmax Date: Sat, 28 Jun 2025 16:38:15 +0800 Subject: [PATCH 05/19] =?UTF-8?q?:hammer:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E9=80=BB=E8=BE=91.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command-snippet-form-drawer.vue | 4 +- .../command-snippet/types/form.rules.ts | 24 ---------- .../path-bookmark-form-drawer.vue | 7 ++- .../path-bookmark/path-bookmark-item.vue | 3 +- .../components/path-bookmark/types/const.ts | 5 --- .../path-bookmark/types/form.rules.ts | 33 -------------- .../components/view/rdp/rdp-action-bar.vue | 2 +- .../components/view/rdp/rdp-status.vue | 14 +++--- .../view/sftp/sftp-table-header.vue | 8 ++-- .../components/view/sftp/sftp-table.vue | 16 +++---- .../components/view/ssh/ssh-header.vue | 4 +- .../src/views/terminal/interfaces/session.ts | 8 ++-- .../service/channel/base-guacd-channel.ts | 6 +-- .../terminal/service/channel/sftp-channel.ts | 2 +- .../terminal/service/channel/ssh-channel.ts | 6 +-- .../service/handler/ssh-session-handler.ts | 6 +-- .../terminal/service/session/base-session.ts | 34 +++++++------- .../terminal/service/session/rdp-session.ts | 4 +- .../terminal/service/session/sftp-session.ts | 4 +- .../terminal/service/session/ssh-session.ts | 14 +++--- .../transfer/rdp-file-download-task.ts | 2 +- .../service/transfer/rdp-file-upload-task.ts | 2 +- .../src/views/terminal/types/const.ts | 6 +++ .../src/views/terminal/types/form.rules.ts | 44 +++++++++++++++++++ .../view/sftp => }/types/table.columns.ts | 6 +-- 25 files changed, 125 insertions(+), 139 deletions(-) delete mode 100644 orion-visor-ui/src/views/terminal/components/command-snippet/types/form.rules.ts delete mode 100644 orion-visor-ui/src/views/terminal/components/path-bookmark/types/const.ts delete mode 100644 orion-visor-ui/src/views/terminal/components/path-bookmark/types/form.rules.ts create mode 100644 orion-visor-ui/src/views/terminal/types/form.rules.ts rename orion-visor-ui/src/views/terminal/{components/view/sftp => }/types/table.columns.ts (92%) diff --git a/orion-visor-ui/src/views/terminal/components/command-snippet/command-snippet-form-drawer.vue b/orion-visor-ui/src/views/terminal/components/command-snippet/command-snippet-form-drawer.vue index 21065c39..7aa7bc08 100644 --- a/orion-visor-ui/src/views/terminal/components/command-snippet/command-snippet-form-drawer.vue +++ b/orion-visor-ui/src/views/terminal/components/command-snippet/command-snippet-form-drawer.vue @@ -13,7 +13,7 @@ ref="formRef" label-align="right" :auto-label-width="true" - :rules="formRules"> + :rules="commandSnippetFormRules"> ; diff --git a/orion-visor-ui/src/views/terminal/components/path-bookmark/path-bookmark-form-drawer.vue b/orion-visor-ui/src/views/terminal/components/path-bookmark/path-bookmark-form-drawer.vue index 9da50091..2110a4f3 100644 --- a/orion-visor-ui/src/views/terminal/components/path-bookmark/path-bookmark-form-drawer.vue +++ b/orion-visor-ui/src/views/terminal/components/path-bookmark/path-bookmark-form-drawer.vue @@ -13,7 +13,7 @@ ref="formRef" label-align="right" :auto-label-width="true" - :rules="formRules"> + :rules="bookmarkFormRules"> ; diff --git a/orion-visor-ui/src/views/terminal/components/view/rdp/rdp-action-bar.vue b/orion-visor-ui/src/views/terminal/components/view/rdp/rdp-action-bar.vue index 3dd6642b..1003f80e 100644 --- a/orion-visor-ui/src/views/terminal/components/view/rdp/rdp-action-bar.vue +++ b/orion-visor-ui/src/views/terminal/components/view/rdp/rdp-action-bar.vue @@ -1,5 +1,5 @@ @@ -22,10 +22,10 @@ diff --git a/orion-visor-ui/src/views/terminal/components/setting/general/terminal-rdp-graph-block.vue b/orion-visor-ui/src/views/terminal/components/setting/general/terminal-rdp-graph-block.vue index 278c62ef..1d588c2f 100644 --- a/orion-visor-ui/src/views/terminal/components/setting/general/terminal-rdp-graph-block.vue +++ b/orion-visor-ui/src/views/terminal/components/setting/general/terminal-rdp-graph-block.vue @@ -3,7 +3,7 @@

- RDP 设置 + RDP 图形化设置

@@ -28,61 +28,61 @@ - - - - - - + + - - + + - - + + - - + + - - + + + + + + diff --git a/orion-visor-ui/src/views/terminal/components/setting/general/terminal-rdp-session-block.vue b/orion-visor-ui/src/views/terminal/components/setting/general/terminal-rdp-session-block.vue new file mode 100644 index 00000000..892324d3 --- /dev/null +++ b/orion-visor-ui/src/views/terminal/components/setting/general/terminal-rdp-session-block.vue @@ -0,0 +1,66 @@ + + + + + + + diff --git a/orion-visor-ui/src/views/terminal/components/setting/general/terminal-session-block.vue b/orion-visor-ui/src/views/terminal/components/setting/general/terminal-session-block.vue deleted file mode 100644 index a69b6c97..00000000 --- a/orion-visor-ui/src/views/terminal/components/setting/general/terminal-session-block.vue +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - diff --git a/orion-visor-ui/src/views/terminal/components/setting/general/terminal-interact-block.vue b/orion-visor-ui/src/views/terminal/components/setting/general/terminal-ssh-interact-block.vue similarity index 74% rename from orion-visor-ui/src/views/terminal/components/setting/general/terminal-interact-block.vue rename to orion-visor-ui/src/views/terminal/components/setting/general/terminal-ssh-interact-block.vue index 9973f300..9d2bdfbe 100644 --- a/orion-visor-ui/src/views/terminal/components/setting/general/terminal-interact-block.vue +++ b/orion-visor-ui/src/views/terminal/components/setting/general/terminal-ssh-interact-block.vue @@ -3,7 +3,7 @@

- 交互设置 + SSH 交互设置

@@ -56,9 +56,6 @@ - @@ -75,27 +72,49 @@ allow-clear /> + + + + + + + + + + diff --git a/orion-visor-ui/src/views/terminal/components/setting/general/terminal-plugins-block.vue b/orion-visor-ui/src/views/terminal/components/setting/general/terminal-ssh-plugins-block.vue similarity index 86% rename from orion-visor-ui/src/views/terminal/components/setting/general/terminal-plugins-block.vue rename to orion-visor-ui/src/views/terminal/components/setting/general/terminal-ssh-plugins-block.vue index 09e8bf58..520a18f5 100644 --- a/orion-visor-ui/src/views/terminal/components/setting/general/terminal-plugins-block.vue +++ b/orion-visor-ui/src/views/terminal/components/setting/general/terminal-ssh-plugins-block.vue @@ -3,7 +3,7 @@

- 插件设置 + SSH 插件设置

@@ -34,12 +34,12 @@ diff --git a/orion-visor-ui/src/views/terminal/components/setting/theme/terminal-theme-block.vue b/orion-visor-ui/src/views/terminal/components/setting/theme/terminal-ssh-theme-block.vue similarity index 94% rename from orion-visor-ui/src/views/terminal/components/setting/theme/terminal-theme-block.vue rename to orion-visor-ui/src/views/terminal/components/setting/theme/terminal-ssh-theme-block.vue index 317c64b9..4555d603 100644 --- a/orion-visor-ui/src/views/terminal/components/setting/theme/terminal-theme-block.vue +++ b/orion-visor-ui/src/views/terminal/components/setting/theme/terminal-ssh-theme-block.vue @@ -3,7 +3,7 @@

- 主题设置 + SSH 主题设置

@@ -49,7 +49,7 @@ @@ -86,14 +86,14 @@ }); // 同步 currentThemeName.value = theme.name; - await updateTerminalPreference(TerminalPreferenceItem.THEME, theme, true); + await updateTerminalPreference(TerminalPreferenceItem.SSH_THEME, theme, true); }; // 加载用户主题 onMounted(async () => { try { - const { data } = await getPreference>('TERMINAL', [TerminalPreferenceItem.THEME]); - currentThemeName.value = data[TerminalPreferenceItem.THEME]?.name; + const { data } = await getPreference>('TERMINAL', [TerminalPreferenceItem.SSH_THEME]); + currentThemeName.value = data[TerminalPreferenceItem.SSH_THEME]?.name; } catch (e) { } }); diff --git a/orion-visor-ui/src/views/terminal/components/setting/theme/terminal-theme-setting.vue b/orion-visor-ui/src/views/terminal/components/setting/theme/terminal-theme-setting.vue index ec244ab5..278a7e11 100644 --- a/orion-visor-ui/src/views/terminal/components/setting/theme/terminal-theme-setting.vue +++ b/orion-visor-ui/src/views/terminal/components/setting/theme/terminal-theme-setting.vue @@ -3,8 +3,8 @@

主题设置

- - + +
@@ -16,7 +16,7 @@ diff --git a/orion-visor-ui/src/views/terminal/components/view/sftp/sftp-editor.vue b/orion-visor-ui/src/views/terminal/components/view/sftp/sftp-editor.vue index 43b0bb42..9b8a0bb0 100644 --- a/orion-visor-ui/src/views/terminal/components/view/sftp/sftp-editor.vue +++ b/orion-visor-ui/src/views/terminal/components/view/sftp/sftp-editor.vue @@ -2,7 +2,7 @@ + :theme="preference.sshTheme.dark ? 'vs-dark' : 'vs'" /> diff --git a/orion-visor-ui/src/views/terminal/service/session/rdp-session.ts b/orion-visor-ui/src/views/terminal/service/session/rdp-session.ts index bed88a8d..eaafb149 100644 --- a/orion-visor-ui/src/views/terminal/service/session/rdp-session.ts +++ b/orion-visor-ui/src/views/terminal/service/session/rdp-session.ts @@ -20,7 +20,7 @@ import RdpSessionClipboardHandler from '../handler/rdp-session-clipboard-handler export const AUDIO_INPUT_MIMETYPE = 'audio/L16;rate=44100,channels=2'; -export const CONNECT_TIMEOUT = 10000; +export const CONNECT_TIMEOUT = 30000; // RDP 会话实现 export default class RdpSession extends BaseSession implements IRdpSession { From 5b611f20c2c67f7153084bbda25806f2f6d5a6ac Mon Sep 17 00:00:00 2001 From: lijiahangmax Date: Sun, 29 Jun 2025 02:24:50 +0800 Subject: [PATCH 10/19] =?UTF-8?q?:hammer:=20=E5=88=9D=E5=A7=8B=E5=8C=96?= =?UTF-8?q?=E7=A8=8B=E5=BA=8F.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../visor/common/session/config/RdpConnectConfig.java | 3 +++ .../handler/host/extra/model/HostRdpExtraModel.java | 5 +++++ .../asset/service/impl/HostConnectServiceImpl.java | 3 ++- .../terminal/handler/terminal/session/RdpSession.java | 5 +++++ orion-visor-ui/src/api/asset/host-extra.ts | 1 + .../terminal/components/setting/extra/rdp-extra-form.vue | 9 +++++++++ 6 files changed, 25 insertions(+), 1 deletion(-) diff --git a/orion-visor-common/src/main/java/org/dromara/visor/common/session/config/RdpConnectConfig.java b/orion-visor-common/src/main/java/org/dromara/visor/common/session/config/RdpConnectConfig.java index 8a916894..f40e42f3 100644 --- a/orion-visor-common/src/main/java/org/dromara/visor/common/session/config/RdpConnectConfig.java +++ b/orion-visor-common/src/main/java/org/dromara/visor/common/session/config/RdpConnectConfig.java @@ -47,6 +47,9 @@ public class RdpConnectConfig extends BaseConnectConfig { @Schema(description = "低带宽模式") private Boolean lowBandwidthMode; + @Schema(description = "初始化程序") + private String initialProgram; + @Schema(description = "RDP 版本是否大于8.1") private Boolean versionGt81; diff --git a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/extra/model/HostRdpExtraModel.java b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/extra/model/HostRdpExtraModel.java index 2f466be1..4b75d1cb 100644 --- a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/extra/model/HostRdpExtraModel.java +++ b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/extra/model/HostRdpExtraModel.java @@ -56,4 +56,9 @@ public class HostRdpExtraModel implements GenericsDataModel { */ private Boolean lowBandwidthMode; + /** + * 初始化程序 + */ + private String initialProgram; + } diff --git a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/impl/HostConnectServiceImpl.java b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/impl/HostConnectServiceImpl.java index 085e44b1..93240932 100644 --- a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/impl/HostConnectServiceImpl.java +++ b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/impl/HostConnectServiceImpl.java @@ -293,8 +293,9 @@ public class HostConnectServiceImpl implements HostConnectService { // 填充基础主机信息 this.setBaseConnectConfig(connectConfig, host); if (extra != null) { - // 设置低带宽模式 + // 设置额外配置信息 connectConfig.setLowBandwidthMode(extra.getLowBandwidthMode()); + connectConfig.setInitialProgram(extra.getInitialProgram()); // 获取自定义认证方式 HostExtraAuthTypeEnum extraAuthType = HostExtraAuthTypeEnum.of(extra.getAuthType()); if (HostExtraAuthTypeEnum.CUSTOM_IDENTITY.equals(extraAuthType)) { diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/session/RdpSession.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/session/RdpSession.java index f7ba72e4..165e8af8 100644 --- a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/session/RdpSession.java +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/session/RdpSession.java @@ -118,6 +118,11 @@ public class RdpSession extends AbstractGuacdSession i String driveMountPath = DriveMountModeEnum.of(extra.getDriveMountMode()) .getDriveMountPath(props.getUserId(), props.getHostId(), props.getId()); tunnel.setParameter(GuacdConst.DRIVE_PATH, Files1.getPath(guacdConfig.getDrivePath() + "/" + driveMountPath)); + // 初始化程序 + String initialProgram = config.getInitialProgram(); + if (!Strings.isBlank(initialProgram)) { + tunnel.setParameter(GuacdConst.INITIAL_PROGRAM, initialProgram); + } // 预连接 String preConnectionId = config.getPreConnectionId(); if (!Strings.isBlank(preConnectionId)) { diff --git a/orion-visor-ui/src/api/asset/host-extra.ts b/orion-visor-ui/src/api/asset/host-extra.ts index 564d47e2..9fb2b142 100644 --- a/orion-visor-ui/src/api/asset/host-extra.ts +++ b/orion-visor-ui/src/api/asset/host-extra.ts @@ -30,6 +30,7 @@ export interface HostRdpExtraSettingModel { authType: string; identityId: number; lowBandwidthMode: boolean; + initialProgram: string; } // 标签额外配置 diff --git a/orion-visor-ui/src/views/terminal/components/setting/extra/rdp-extra-form.vue b/orion-visor-ui/src/views/terminal/components/setting/extra/rdp-extra-form.vue index 2a4830e8..e4570293 100644 --- a/orion-visor-ui/src/views/terminal/components/setting/extra/rdp-extra-form.vue +++ b/orion-visor-ui/src/views/terminal/components/setting/extra/rdp-extra-form.vue @@ -21,10 +21,19 @@
+ + + + From e1325b3128760c8d78007805ccfe68074ac4f9ed Mon Sep 17 00:00:00 2001 From: lijiahangmax Date: Sun, 29 Jun 2025 20:00:15 +0800 Subject: [PATCH 11/19] =?UTF-8?q?:hammer:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E9=80=BB=E8=BE=91.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/api/terminal/terminal-file-log.ts | 70 +++++++++++++++++++ .../src/api/terminal/terminal-sftp.ts | 69 ------------------ .../src/router/routes/modules/asset-audit.ts | 18 ++--- .../views/asset-audit/sftp-log/types/const.ts | 20 ------ .../components/connect-log-clear-modal.vue | 0 .../components/connect-log-detail-drawer.vue | 0 .../components/connect-log-table.vue | 0 .../index.vue | 2 +- .../types/const.ts | 0 .../types/table.columns.ts | 0 .../components/connect-session-table.vue | 2 +- .../index.vue | 3 +- .../types/const.ts | 0 .../types/table.columns.ts | 0 .../components/file-log-table.vue} | 42 +++++------ .../{sftp-log => terminal-file-log}/index.vue | 6 +- .../terminal-file-log/types/const.ts | 20 ++++++ .../types/table.columns.ts | 2 +- .../components/terminal-connect-table.vue | 2 +- .../components/workplace-statistics.vue | 2 +- 20 files changed, 129 insertions(+), 129 deletions(-) create mode 100644 orion-visor-ui/src/api/terminal/terminal-file-log.ts delete mode 100644 orion-visor-ui/src/views/asset-audit/sftp-log/types/const.ts rename orion-visor-ui/src/views/asset-audit/{connect-log => terminal-connect-log}/components/connect-log-clear-modal.vue (100%) rename orion-visor-ui/src/views/asset-audit/{connect-log => terminal-connect-log}/components/connect-log-detail-drawer.vue (100%) rename orion-visor-ui/src/views/asset-audit/{connect-log => terminal-connect-log}/components/connect-log-table.vue (100%) rename orion-visor-ui/src/views/asset-audit/{connect-log => terminal-connect-log}/index.vue (97%) rename orion-visor-ui/src/views/asset-audit/{connect-log => terminal-connect-log}/types/const.ts (100%) rename orion-visor-ui/src/views/asset-audit/{connect-log => terminal-connect-log}/types/table.columns.ts (100%) rename orion-visor-ui/src/views/asset-audit/{connect-session => terminal-connect-session}/components/connect-session-table.vue (99%) rename orion-visor-ui/src/views/asset-audit/{connect-session => terminal-connect-session}/index.vue (92%) rename orion-visor-ui/src/views/asset-audit/{connect-session => terminal-connect-session}/types/const.ts (100%) rename orion-visor-ui/src/views/asset-audit/{connect-session => terminal-connect-session}/types/table.columns.ts (100%) rename orion-visor-ui/src/views/asset-audit/{sftp-log/components/sftp-log-table.vue => terminal-file-log/components/file-log-table.vue} (86%) rename orion-visor-ui/src/views/asset-audit/{sftp-log => terminal-file-log}/index.vue (83%) create mode 100644 orion-visor-ui/src/views/asset-audit/terminal-file-log/types/const.ts rename orion-visor-ui/src/views/asset-audit/{sftp-log => terminal-file-log}/types/table.columns.ts (99%) diff --git a/orion-visor-ui/src/api/terminal/terminal-file-log.ts b/orion-visor-ui/src/api/terminal/terminal-file-log.ts new file mode 100644 index 00000000..9a73047b --- /dev/null +++ b/orion-visor-ui/src/api/terminal/terminal-file-log.ts @@ -0,0 +1,70 @@ +import type { DataGrid, OrderDirection, Pagination } from '@/types/global'; +import type { TableData } from '@arco-design/web-vue'; +import axios from 'axios'; +import qs from 'query-string'; + +/** + * 终端文件操作日志 查询请求 + */ +export interface TerminalFileLogQueryRequest extends Pagination, OrderDirection { + userId?: number; + hostId?: number; + type?: string; + result?: number; + startTimeRange?: string[]; +} + +/** + * 终端文件操作日志 查询响应 + */ +export interface TerminalFileLogQueryResponse extends TableData { + id: number; + userId: number; + username: number; + hostId: number; + hostName: string; + hostAddress: string; + address: string; + location: string; + userAgent: string; + paths: string[]; + type: string; + result: string; + startTime: number; + extra: TerminalFileLogExtra; +} + +/** + * 终端文件操作日志 拓展对象 + */ +export interface TerminalFileLogExtra { + mod: number; + target: string; + maxCount: number; +} + +/** + * 分页查询终端文件操作日志 + */ +export function getTerminalFileLogPage(request: TerminalFileLogQueryRequest) { + return axios.post>('/terminal/terminal-file-log/query', request); +} + +/** + * 查询终端文件操作日志数量 + */ +export function getTerminalFileLogCount(request: TerminalFileLogQueryRequest) { + return axios.post('/terminal/terminal-file-log/count', request); +} + +/** + * 删除终端文件操作日志 + */ +export function deleteTerminalFileLog(idList: Array) { + return axios.delete('/terminal/terminal-file-log/delete', { + params: { idList }, + paramsSerializer: params => { + return qs.stringify(params, { arrayFormat: 'comma' }); + } + }); +} diff --git a/orion-visor-ui/src/api/terminal/terminal-sftp.ts b/orion-visor-ui/src/api/terminal/terminal-sftp.ts index 07b43053..43f837fd 100644 --- a/orion-visor-ui/src/api/terminal/terminal-sftp.ts +++ b/orion-visor-ui/src/api/terminal/terminal-sftp.ts @@ -1,74 +1,5 @@ -import type { DataGrid, OrderDirection, Pagination } from '@/types/global'; -import type { TableData } from '@arco-design/web-vue'; import { httpBaseUrl } from '@/utils/env'; import axios from 'axios'; -import qs from 'query-string'; - -/** - * SFTP 操作日志 查询请求 - */ -export interface TerminalSftpLogQueryRequest extends Pagination, OrderDirection { - userId?: number; - hostId?: number; - type?: string; - result?: number; - startTimeRange?: string[]; -} - -/** - * SFTP 操作日志 查询响应 - */ -export interface TerminalSftpLogQueryResponse extends TableData { - id: number; - userId: number; - username: number; - hostId: number; - hostName: string; - hostAddress: string; - address: string; - location: string; - userAgent: string; - paths: string[]; - type: string; - result: string; - startTime: number; - extra: TerminalSftpLogExtra; -} - -/** - * SFTP 操作日志 拓展对象 - */ -export interface TerminalSftpLogExtra { - mod: number; - target: string; - maxCount: number; -} - -/** - * 分页查询 SFTP 操作日志 - */ -export function getTerminalSftpLogPage(request: TerminalSftpLogQueryRequest) { - return axios.post>('/terminal/terminal-sftp/query-log', request); -} - -/** - * 查询 SFTP 操作日志数量 - */ -export function getTerminalSftpLogCount(request: TerminalSftpLogQueryRequest) { - return axios.post('/terminal/terminal-sftp/log-count', request); -} - -/** - * 删除 SFTP 操作日志 - */ -export function deleteTerminalSftpLog(idList: Array) { - return axios.delete('/terminal/terminal-sftp/delete-log', { - params: { idList }, - paramsSerializer: params => { - return qs.stringify(params, { arrayFormat: 'comma' }); - } - }); -} /** * 获取 SFTP 文件内容 diff --git a/orion-visor-ui/src/router/routes/modules/asset-audit.ts b/orion-visor-ui/src/router/routes/modules/asset-audit.ts index 37a0aca4..67ba9dac 100644 --- a/orion-visor-ui/src/router/routes/modules/asset-audit.ts +++ b/orion-visor-ui/src/router/routes/modules/asset-audit.ts @@ -8,19 +8,19 @@ const ASSET_AUDIT: AppRouteRecordRaw[] = [ component: DEFAULT_LAYOUT, children: [ { - name: 'connectLog', - path: '/audit/connect-log', - component: () => import('@/views/asset-audit/connect-log/index.vue'), + name: 'terminalConnectLog', + path: '/audit/terminal-connect-log', + component: () => import('@/views/asset-audit/terminal-connect-log/index.vue'), }, { - name: 'connectSession', - path: '/audit/connect-session', - component: () => import('@/views/asset-audit/connect-session/index.vue'), + name: 'terminalConnectSession', + path: '/audit/terminal-connect-session', + component: () => import('@/views/asset-audit/terminal-connect-session/index.vue'), }, { - name: 'sftpLog', - path: '/audit/sftp-log', - component: () => import('@/views/asset-audit/sftp-log/index.vue'), + name: 'terminalFileLog', + path: '/audit/terminal-file-log', + component: () => import('@/views/asset-audit/terminal-file-log/index.vue'), }, ], }, diff --git a/orion-visor-ui/src/views/asset-audit/sftp-log/types/const.ts b/orion-visor-ui/src/views/asset-audit/sftp-log/types/const.ts deleted file mode 100644 index c911439c..00000000 --- a/orion-visor-ui/src/views/asset-audit/sftp-log/types/const.ts +++ /dev/null @@ -1,20 +0,0 @@ -// 表名称 -export const TableName = 'sftp-log'; - -// sftp 操作类型 -export const SftpOperatorType = { - SFTP_MOVE: 'terminal:sftp-move', - SFTP_CHMOD: 'terminal:sftp-chmod', -}; - -// 最大展示数量 -export const showPathMaxCount = 5; - -// sftp 操作类型 字典项 -export const sftpOperatorTypeKey = 'sftpOperatorType'; - -// sftp 操作结果 字典项 -export const sftpOperatorResultKey = 'operatorLogResult'; - -// 加载的字典值 -export const dictKeys = [sftpOperatorTypeKey, sftpOperatorResultKey]; diff --git a/orion-visor-ui/src/views/asset-audit/connect-log/components/connect-log-clear-modal.vue b/orion-visor-ui/src/views/asset-audit/terminal-connect-log/components/connect-log-clear-modal.vue similarity index 100% rename from orion-visor-ui/src/views/asset-audit/connect-log/components/connect-log-clear-modal.vue rename to orion-visor-ui/src/views/asset-audit/terminal-connect-log/components/connect-log-clear-modal.vue diff --git a/orion-visor-ui/src/views/asset-audit/connect-log/components/connect-log-detail-drawer.vue b/orion-visor-ui/src/views/asset-audit/terminal-connect-log/components/connect-log-detail-drawer.vue similarity index 100% rename from orion-visor-ui/src/views/asset-audit/connect-log/components/connect-log-detail-drawer.vue rename to orion-visor-ui/src/views/asset-audit/terminal-connect-log/components/connect-log-detail-drawer.vue diff --git a/orion-visor-ui/src/views/asset-audit/connect-log/components/connect-log-table.vue b/orion-visor-ui/src/views/asset-audit/terminal-connect-log/components/connect-log-table.vue similarity index 100% rename from orion-visor-ui/src/views/asset-audit/connect-log/components/connect-log-table.vue rename to orion-visor-ui/src/views/asset-audit/terminal-connect-log/components/connect-log-table.vue diff --git a/orion-visor-ui/src/views/asset-audit/connect-log/index.vue b/orion-visor-ui/src/views/asset-audit/terminal-connect-log/index.vue similarity index 97% rename from orion-visor-ui/src/views/asset-audit/connect-log/index.vue rename to orion-visor-ui/src/views/asset-audit/terminal-connect-log/index.vue index c1f222d6..957afee4 100644 --- a/orion-visor-ui/src/views/asset-audit/connect-log/index.vue +++ b/orion-visor-ui/src/views/asset-audit/terminal-connect-log/index.vue @@ -14,7 +14,7 @@ diff --git a/orion-visor-ui/src/views/asset-audit/connect-log/types/const.ts b/orion-visor-ui/src/views/asset-audit/terminal-connect-log/types/const.ts similarity index 100% rename from orion-visor-ui/src/views/asset-audit/connect-log/types/const.ts rename to orion-visor-ui/src/views/asset-audit/terminal-connect-log/types/const.ts diff --git a/orion-visor-ui/src/views/asset-audit/connect-log/types/table.columns.ts b/orion-visor-ui/src/views/asset-audit/terminal-connect-log/types/table.columns.ts similarity index 100% rename from orion-visor-ui/src/views/asset-audit/connect-log/types/table.columns.ts rename to orion-visor-ui/src/views/asset-audit/terminal-connect-log/types/table.columns.ts diff --git a/orion-visor-ui/src/views/asset-audit/connect-session/components/connect-session-table.vue b/orion-visor-ui/src/views/asset-audit/terminal-connect-session/components/connect-session-table.vue similarity index 99% rename from orion-visor-ui/src/views/asset-audit/connect-session/components/connect-session-table.vue rename to orion-visor-ui/src/views/asset-audit/terminal-connect-session/components/connect-session-table.vue index 5dff47dd..5aae12ca 100644 --- a/orion-visor-ui/src/views/asset-audit/connect-session/components/connect-session-table.vue +++ b/orion-visor-ui/src/views/asset-audit/terminal-connect-session/components/connect-session-table.vue @@ -46,7 +46,7 @@
- 主机在线会话 + 终端在线会话
diff --git a/orion-visor-ui/src/views/asset-audit/connect-session/index.vue b/orion-visor-ui/src/views/asset-audit/terminal-connect-session/index.vue similarity index 92% rename from orion-visor-ui/src/views/asset-audit/connect-session/index.vue rename to orion-visor-ui/src/views/asset-audit/terminal-connect-session/index.vue index 7adb4ae6..c9c2ee51 100644 --- a/orion-visor-ui/src/views/asset-audit/connect-session/index.vue +++ b/orion-visor-ui/src/views/asset-audit/terminal-connect-session/index.vue @@ -7,7 +7,7 @@ @@ -18,7 +18,6 @@ import ConnectSessionTable from './components/connect-session-table.vue'; const render = ref(false); - const eventDrawer = ref(); // 加载字典配置 onBeforeMount(async () => { diff --git a/orion-visor-ui/src/views/asset-audit/connect-session/types/const.ts b/orion-visor-ui/src/views/asset-audit/terminal-connect-session/types/const.ts similarity index 100% rename from orion-visor-ui/src/views/asset-audit/connect-session/types/const.ts rename to orion-visor-ui/src/views/asset-audit/terminal-connect-session/types/const.ts diff --git a/orion-visor-ui/src/views/asset-audit/connect-session/types/table.columns.ts b/orion-visor-ui/src/views/asset-audit/terminal-connect-session/types/table.columns.ts similarity index 100% rename from orion-visor-ui/src/views/asset-audit/connect-session/types/table.columns.ts rename to orion-visor-ui/src/views/asset-audit/terminal-connect-session/types/table.columns.ts diff --git a/orion-visor-ui/src/views/asset-audit/sftp-log/components/sftp-log-table.vue b/orion-visor-ui/src/views/asset-audit/terminal-file-log/components/file-log-table.vue similarity index 86% rename from orion-visor-ui/src/views/asset-audit/sftp-log/components/sftp-log-table.vue rename to orion-visor-ui/src/views/asset-audit/terminal-file-log/components/file-log-table.vue index 52511515..554e2174 100644 --- a/orion-visor-ui/src/views/asset-audit/sftp-log/components/sftp-log-table.vue +++ b/orion-visor-ui/src/views/asset-audit/terminal-file-log/components/file-log-table.vue @@ -24,14 +24,14 @@ @@ -50,7 +50,7 @@
- 文件操作日志 + 终端文件日志
@@ -61,7 +61,7 @@ position="br" type="warning" @ok="deleteSelectedRows"> - @@ -110,7 +110,7 @@ @@ -127,11 +127,11 @@ {{ path }} - + 移动到 {{ record.extra?.target }} - + 提权 {{ record.extra?.mod }} {{ permission10toString(record.extra?.mod as number) }} @@ -145,8 +145,8 @@ @@ -169,7 +169,7 @@ position="left" type="warning" @ok="deleteRow(record)"> - @@ -184,15 +184,15 @@ @@ -15,7 +15,7 @@ import { ref, onBeforeMount } from 'vue'; import { useDictStore } from '@/store'; import { dictKeys } from './types/const'; - import SftpLogTable from './components/sftp-log-table.vue'; + import FileLogTable from './components/file-log-table.vue'; const render = ref(false); diff --git a/orion-visor-ui/src/views/asset-audit/terminal-file-log/types/const.ts b/orion-visor-ui/src/views/asset-audit/terminal-file-log/types/const.ts new file mode 100644 index 00000000..904201b2 --- /dev/null +++ b/orion-visor-ui/src/views/asset-audit/terminal-file-log/types/const.ts @@ -0,0 +1,20 @@ +// 表名称 +export const TableName = 'file-log'; + +// 终端文件操作类型 +export const TerminalFileOperatorType = { + SFTP_MOVE: 'terminal:sftp-move', + SFTP_CHMOD: 'terminal:sftp-chmod', +}; + +// 最大展示数量 +export const showPathMaxCount = 5; + +// 终端文件操作类型 字典项 +export const terminalFileOperatorTypeKey = 'terminalFileOperatorType'; + +// 操作结果 字典项 +export const operatorResultKey = 'operatorLogResult'; + +// 加载的字典值 +export const dictKeys = [terminalFileOperatorTypeKey, operatorResultKey]; diff --git a/orion-visor-ui/src/views/asset-audit/sftp-log/types/table.columns.ts b/orion-visor-ui/src/views/asset-audit/terminal-file-log/types/table.columns.ts similarity index 99% rename from orion-visor-ui/src/views/asset-audit/sftp-log/types/table.columns.ts rename to orion-visor-ui/src/views/asset-audit/terminal-file-log/types/table.columns.ts index 146ee855..79822974 100644 --- a/orion-visor-ui/src/views/asset-audit/sftp-log/types/table.columns.ts +++ b/orion-visor-ui/src/views/asset-audit/terminal-file-log/types/table.columns.ts @@ -30,7 +30,7 @@ const columns = [ title: '操作类型', dataIndex: 'type', slotName: 'type', - width: 116, + width: 146, align: 'left', default: true, }, { diff --git a/orion-visor-ui/src/views/dashboard/workplace/components/terminal-connect-table.vue b/orion-visor-ui/src/views/dashboard/workplace/components/terminal-connect-table.vue index d484e547..cc4c375a 100644 --- a/orion-visor-ui/src/views/dashboard/workplace/components/terminal-connect-table.vue +++ b/orion-visor-ui/src/views/dashboard/workplace/components/terminal-connect-table.vue @@ -6,7 +6,7 @@ + @click="router.push({ name: 'terminalConnectLog', query: { action: 'self' } })"> 详情 diff --git a/orion-visor-ui/src/views/dashboard/workplace/components/workplace-statistics.vue b/orion-visor-ui/src/views/dashboard/workplace/components/workplace-statistics.vue index bc3b0123..b449436b 100644 --- a/orion-visor-ui/src/views/dashboard/workplace/components/workplace-statistics.vue +++ b/orion-visor-ui/src/views/dashboard/workplace/components/workplace-statistics.vue @@ -99,7 +99,7 @@ background: isDark.value ? '#354276' : '#E8F3FF', iconColor: isDark.value ? '#4A7FF7' : '#165DFF', }, - go: () => router.push({ name: 'connectLog', query: { action: 'self' } }) + go: () => router.push({ name: 'terminalConnectLog', query: { action: 'self' } }) }, { title: '今日批量执行次数', value: props.data.exec?.todayExecCommandCount || 0, From 3d0250dd5fca9b83e724d363947774177b910dce Mon Sep 17 00:00:00 2001 From: lijiahangmax Date: Sun, 29 Jun 2025 20:20:16 +0800 Subject: [PATCH 12/19] =?UTF-8?q?:hammer:=20=E7=BB=88=E7=AB=AF=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E6=93=8D=E4=BD=9C=E6=97=A5=E5=BF=97.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AuthenticationController.java | 3 +- .../controller/TerminalFileLogController.java | 89 ++++++++++++ .../controller/TerminalSftpController.java | 33 ----- .../convert/TerminalFileLogConvert.java | 46 ++++++ .../operator/TerminalFileLogOperatorType.java | 69 +++++++++ .../define/operator/TerminalOperatorType.java | 25 +--- .../terminal/TerminalFileLogQueryRequest.java | 65 +++++++++ .../terminal/entity/vo/TerminalFileLogVO.java | 93 +++++++++++++ .../service/TerminalFileLogService.java | 64 +++++++++ .../terminal/service/TerminalSftpService.java | 28 ---- .../impl/TerminalFileLogServiceImpl.java | 131 ++++++++++++++++++ .../service/impl/TerminalSftpServiceImpl.java | 87 ------------ 12 files changed, 564 insertions(+), 169 deletions(-) create mode 100644 orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/controller/TerminalFileLogController.java create mode 100644 orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/convert/TerminalFileLogConvert.java create mode 100644 orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/define/operator/TerminalFileLogOperatorType.java create mode 100644 orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/entity/request/terminal/TerminalFileLogQueryRequest.java create mode 100644 orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/entity/vo/TerminalFileLogVO.java create mode 100644 orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/service/TerminalFileLogService.java create mode 100644 orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/service/impl/TerminalFileLogServiceImpl.java diff --git a/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/controller/AuthenticationController.java b/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/controller/AuthenticationController.java index 2f219af2..4c1ab5ec 100644 --- a/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/controller/AuthenticationController.java +++ b/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/controller/AuthenticationController.java @@ -26,6 +26,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; import org.dromara.visor.framework.biz.operator.log.core.annotation.OperatorLog; +import org.dromara.visor.framework.biz.operator.log.core.enums.ReturnType; import org.dromara.visor.framework.log.core.annotation.IgnoreLog; import org.dromara.visor.framework.log.core.enums.IgnoreLogMode; import org.dromara.visor.framework.web.core.annotation.RestWrapper; @@ -58,7 +59,7 @@ public class AuthenticationController { @Resource private AuthenticationService authenticationService; - @OperatorLog(AuthenticationOperatorType.LOGIN) + @OperatorLog(value = AuthenticationOperatorType.LOGIN, ret = ReturnType.IGNORE) @PermitAll @Operation(summary = "登录") @PostMapping("/login") diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/controller/TerminalFileLogController.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/controller/TerminalFileLogController.java new file mode 100644 index 00000000..8e9b2a81 --- /dev/null +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/controller/TerminalFileLogController.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2023 - present Dromara, All rights reserved. + * + * https://visor.dromara.org + * https://visor.dromara.org.cn + * https://visor.orionsec.cn + * + * Members: + * Jiahang Li - ljh1553488six@139.com - author + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.dromara.visor.module.terminal.controller; + +import cn.orionsec.kit.lang.define.wrapper.DataGrid; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.dromara.visor.common.validator.group.Page; +import org.dromara.visor.framework.biz.operator.log.core.annotation.OperatorLog; +import org.dromara.visor.framework.log.core.annotation.IgnoreLog; +import org.dromara.visor.framework.log.core.enums.IgnoreLogMode; +import org.dromara.visor.framework.web.core.annotation.RestWrapper; +import org.dromara.visor.module.terminal.define.operator.TerminalFileLogOperatorType; +import org.dromara.visor.module.terminal.entity.request.terminal.TerminalFileLogQueryRequest; +import org.dromara.visor.module.terminal.entity.vo.TerminalFileLogVO; +import org.dromara.visor.module.terminal.service.TerminalFileLogService; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.List; + +/** + * 终端文件日志操作服务 api + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2023-12-26 22:09 + */ +@Tag(name = "terminal - 终端文件日志操作服务") +@Slf4j +@Validated +@RestWrapper +@RestController +@RequestMapping("/terminal/terminal-file-log") +public class TerminalFileLogController { + + @Resource + private TerminalFileLogService terminalFileLogService; + + @IgnoreLog(IgnoreLogMode.RET) + @PostMapping("/query") + @Operation(summary = "分页查询终端文件操作日志") + @PreAuthorize("@ss.hasAnyPermission('infra:operator-log:query', 'terminal:terminal-file-log:management:query')") + public DataGrid getTerminalFileLogPage(@Validated(Page.class) @RequestBody TerminalFileLogQueryRequest request) { + return terminalFileLogService.getTerminalFileLogPage(request); + } + + @IgnoreLog(IgnoreLogMode.RET) + @PostMapping("/count") + @Operation(summary = "查询终端文件操作日志数量") + @PreAuthorize("@ss.hasAnyPermission('infra:operator-log:query', 'terminal:terminal-file-log:management:query')") + public Long getTerminalFileLogCount(@Validated @RequestBody TerminalFileLogQueryRequest request) { + return terminalFileLogService.getTerminalFileLogCount(request); + } + + @OperatorLog(TerminalFileLogOperatorType.DELETE) + @DeleteMapping("/delete") + @Operation(summary = "删除终端文件操作日志") + @Parameter(name = "idList", description = "idList", required = true) + @PreAuthorize("@ss.hasAnyPermission('infra:operator-log:delete', 'terminal:terminal-file-log:management:delete')") + public Integer deleteTerminalFileLog(@RequestParam("idList") List idList) { + return terminalFileLogService.deleteTerminalFileLog(idList); + } + +} diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/controller/TerminalSftpController.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/controller/TerminalSftpController.java index 62386b2e..3cfc4a95 100644 --- a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/controller/TerminalSftpController.java +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/controller/TerminalSftpController.java @@ -22,22 +22,15 @@ */ package org.dromara.visor.module.terminal.controller; -import cn.orionsec.kit.lang.define.wrapper.DataGrid; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; -import org.dromara.visor.common.validator.group.Page; -import org.dromara.visor.framework.biz.operator.log.core.annotation.OperatorLog; import org.dromara.visor.framework.log.core.annotation.IgnoreLog; import org.dromara.visor.framework.log.core.enums.IgnoreLogMode; import org.dromara.visor.framework.web.core.annotation.IgnoreWrapper; import org.dromara.visor.framework.web.core.annotation.RestWrapper; -import org.dromara.visor.module.terminal.define.operator.TerminalOperatorType; -import org.dromara.visor.module.terminal.entity.request.terminal.TerminalSftpLogQueryRequest; -import org.dromara.visor.module.terminal.entity.vo.TerminalSftpLogVO; import org.dromara.visor.module.terminal.service.TerminalSftpService; -import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; @@ -46,7 +39,6 @@ import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBo import javax.annotation.Resource; import javax.annotation.security.PermitAll; import javax.servlet.http.HttpServletResponse; -import java.util.List; /** * SFTP 操作服务 api @@ -66,31 +58,6 @@ public class TerminalSftpController { @Resource private TerminalSftpService terminalSftpService; - @IgnoreLog(IgnoreLogMode.RET) - @PostMapping("/query-log") - @Operation(summary = "分页查询 SFTP 操作日志") - @PreAuthorize("@ss.hasAnyPermission('infra:operator-log:query', 'terminal:terminal-sftp-log:management:query')") - public DataGrid getTerminalSftpLogPage(@Validated(Page.class) @RequestBody TerminalSftpLogQueryRequest request) { - return terminalSftpService.getTerminalSftpLogPage(request); - } - - @IgnoreLog(IgnoreLogMode.RET) - @PostMapping("/log-count") - @Operation(summary = "查询 SFTP 操作日志数量") - @PreAuthorize("@ss.hasAnyPermission('infra:operator-log:query', 'terminal:terminal-sftp-log:management:query')") - public Long getTerminalSftpLogCount(@Validated @RequestBody TerminalSftpLogQueryRequest request) { - return terminalSftpService.getTerminalSftpLogCount(request); - } - - @OperatorLog(TerminalOperatorType.DELETE_SFTP_LOG) - @DeleteMapping("/delete-log") - @Operation(summary = "删除 SFTP 操作日志") - @Parameter(name = "idList", description = "idList", required = true) - @PreAuthorize("@ss.hasAnyPermission('infra:operator-log:delete', 'terminal:terminal-sftp-log:management:delete')") - public Integer deleteTerminalSftpLog(@RequestParam("idList") List idList) { - return terminalSftpService.deleteTerminalSftpLog(idList); - } - @IgnoreLog(IgnoreLogMode.RET) @GetMapping("/get-content") @Operation(summary = "获取文件内容") diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/convert/TerminalFileLogConvert.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/convert/TerminalFileLogConvert.java new file mode 100644 index 00000000..38bae427 --- /dev/null +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/convert/TerminalFileLogConvert.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023 - present Dromara, All rights reserved. + * + * https://visor.dromara.org + * https://visor.dromara.org.cn + * https://visor.orionsec.cn + * + * Members: + * Jiahang Li - ljh1553488six@139.com - author + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.dromara.visor.module.terminal.convert; + +import org.dromara.visor.module.infra.entity.dto.operator.OperatorLogDTO; +import org.dromara.visor.module.terminal.entity.vo.TerminalFileLogVO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * 终端文件操作日志 内部对象转换器 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2023-12-26 22:09 + */ +@Mapper +public interface TerminalFileLogConvert { + + TerminalFileLogConvert MAPPER = Mappers.getMapper(TerminalFileLogConvert.class); + + @Mapping(target = "extra", ignore = true) + TerminalFileLogVO to(OperatorLogDTO request); + +} diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/define/operator/TerminalFileLogOperatorType.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/define/operator/TerminalFileLogOperatorType.java new file mode 100644 index 00000000..005cb09d --- /dev/null +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/define/operator/TerminalFileLogOperatorType.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2023 - present Dromara, All rights reserved. + * + * https://visor.dromara.org + * https://visor.dromara.org.cn + * https://visor.orionsec.cn + * + * Members: + * Jiahang Li - ljh1553488six@139.com - author + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.dromara.visor.module.terminal.define.operator; + +import cn.orionsec.kit.lang.utils.collect.Lists; +import org.dromara.visor.framework.biz.operator.log.core.annotation.Module; +import org.dromara.visor.framework.biz.operator.log.core.enums.OperatorRiskLevel; +import org.dromara.visor.framework.biz.operator.log.core.factory.InitializingOperatorTypes; +import org.dromara.visor.framework.biz.operator.log.core.model.OperatorType; + +import java.util.List; + +/** + * 终端文件日志 操作日志类型 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2024/3/2 14:37 + */ +@Module("terminal:terminal-file-log") +public class TerminalFileLogOperatorType extends InitializingOperatorTypes { + + public static final String DELETE = "terminal-file-log:delete"; + + public static final List TYPES = Lists.of( + TerminalOperatorType.SFTP_MKDIR, + TerminalOperatorType.SFTP_TOUCH, + TerminalOperatorType.SFTP_MOVE, + TerminalOperatorType.SFTP_REMOVE, + TerminalOperatorType.SFTP_TRUNCATE, + TerminalOperatorType.SFTP_CHMOD, + TerminalOperatorType.SFTP_CHOWN, + TerminalOperatorType.SFTP_CHGRP, + TerminalOperatorType.SFTP_GET_CONTENT, + TerminalOperatorType.SFTP_SET_CONTENT, + TerminalOperatorType.SFTP_UPLOAD, + TerminalOperatorType.SFTP_DOWNLOAD, + TerminalOperatorType.RDP_UPLOAD, + TerminalOperatorType.RDP_DOWNLOAD + ); + + @Override + public OperatorType[] types() { + return new OperatorType[]{ + new OperatorType(OperatorRiskLevel.H, DELETE, "删除文件操作日志 ${count} 条"), + }; + } + +} diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/define/operator/TerminalOperatorType.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/define/operator/TerminalOperatorType.java index 4535b49f..162c6e59 100644 --- a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/define/operator/TerminalOperatorType.java +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/define/operator/TerminalOperatorType.java @@ -22,14 +22,11 @@ */ package org.dromara.visor.module.terminal.define.operator; -import cn.orionsec.kit.lang.utils.collect.Lists; import org.dromara.visor.framework.biz.operator.log.core.annotation.Module; import org.dromara.visor.framework.biz.operator.log.core.enums.OperatorRiskLevel; import org.dromara.visor.framework.biz.operator.log.core.factory.InitializingOperatorTypes; import org.dromara.visor.framework.biz.operator.log.core.model.OperatorType; -import java.util.List; - /** * 终端 操作日志类型 * @@ -42,8 +39,6 @@ public class TerminalOperatorType extends InitializingOperatorTypes { public static final String CONNECT = "terminal:connect"; - public static final String DELETE_SFTP_LOG = "terminal:delete-sftp-log"; - public static final String SFTP_MKDIR = "terminal:sftp-mkdir"; public static final String SFTP_TOUCH = "terminal:sftp-touch"; @@ -68,26 +63,14 @@ public class TerminalOperatorType extends InitializingOperatorTypes { public static final String SFTP_DOWNLOAD = "terminal:sftp-download"; - public static final List SFTP_TYPES = Lists.of( - SFTP_MKDIR, - SFTP_TOUCH, - SFTP_MOVE, - SFTP_REMOVE, - SFTP_TRUNCATE, - SFTP_CHMOD, - SFTP_CHOWN, - SFTP_CHGRP, - SFTP_GET_CONTENT, - SFTP_SET_CONTENT, - SFTP_UPLOAD, - SFTP_DOWNLOAD - ); + public static final String RDP_UPLOAD = "terminal:rdp-upload"; + + public static final String RDP_DOWNLOAD = "terminal:rdp-download"; @Override public OperatorType[] types() { return new OperatorType[]{ new OperatorType(OperatorRiskLevel.L, CONNECT, "连接主机 ${connectType} ${hostName}"), - new OperatorType(OperatorRiskLevel.H, DELETE_SFTP_LOG, "删除 SFTP 操作日志 ${count} 条"), new OperatorType(OperatorRiskLevel.L, SFTP_MKDIR, "创建文件夹 ${hostName} ${path}"), new OperatorType(OperatorRiskLevel.L, SFTP_TOUCH, "创建文件 ${hostName} ${path}"), new OperatorType(OperatorRiskLevel.M, SFTP_MOVE, "移动文件 ${hostName} ${path}${target}"), @@ -100,6 +83,8 @@ public class TerminalOperatorType extends InitializingOperatorTypes { new OperatorType(OperatorRiskLevel.M, SFTP_SET_CONTENT, "修改文件内容 ${hostName} ${path}"), new OperatorType(OperatorRiskLevel.M, SFTP_UPLOAD, "上传文件 ${hostName} ${path}"), new OperatorType(OperatorRiskLevel.M, SFTP_DOWNLOAD, "下载文件 ${hostName} ${path}"), + new OperatorType(OperatorRiskLevel.M, RDP_UPLOAD, "上传文件 ${hostName} ${path}"), + new OperatorType(OperatorRiskLevel.M, RDP_DOWNLOAD, "下载文件 ${hostName} ${path}"), }; } diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/entity/request/terminal/TerminalFileLogQueryRequest.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/entity/request/terminal/TerminalFileLogQueryRequest.java new file mode 100644 index 00000000..cf8aeb14 --- /dev/null +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/entity/request/terminal/TerminalFileLogQueryRequest.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2023 - present Dromara, All rights reserved. + * + * https://visor.dromara.org + * https://visor.dromara.org.cn + * https://visor.orionsec.cn + * + * Members: + * Jiahang Li - ljh1553488six@139.com - author + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.dromara.visor.module.terminal.entity.request.terminal; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import org.dromara.visor.common.entity.BaseQueryRequest; + +import javax.validation.constraints.Size; +import java.util.Date; + +/** + * 终端文件操作日志 查询请求对象 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2024-3-4 22:59 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +@Schema(name = "TerminalFileLogQueryRequest", description = "终端文件操作日志 查询请求对象") +public class TerminalFileLogQueryRequest extends BaseQueryRequest { + + @Schema(description = "用户id") + private Long userId; + + @Schema(description = "hostId") + private Long hostId; + + @Size(max = 64) + @Schema(description = "操作类型") + private String type; + + @Schema(description = "操作结果 0失败 1成功") + private Integer result; + + @Schema(description = "开始时间-区间") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date[] startTimeRange; + +} diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/entity/vo/TerminalFileLogVO.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/entity/vo/TerminalFileLogVO.java new file mode 100644 index 00000000..1d640622 --- /dev/null +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/entity/vo/TerminalFileLogVO.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2023 - present Dromara, All rights reserved. + * + * https://visor.dromara.org + * https://visor.dromara.org.cn + * https://visor.orionsec.cn + * + * Members: + * Jiahang Li - ljh1553488six@139.com - author + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.dromara.visor.module.terminal.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.Date; +import java.util.Map; + +/** + * 终端文件操作日志 实体对象 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2023-10-10 17:08 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Schema(name = "TerminalFileLogVO", description = "终端文件操作日志 实体对象") +public class TerminalFileLogVO implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + + @Schema(description = "用户id") + private Long userId; + + @Schema(description = "用户名") + private String username; + + @Schema(description = "主机id") + private Long hostId; + + @Schema(description = "主机名称") + private String hostName; + + @Schema(description = "主机地址") + private String hostAddress; + + @Schema(description = "操作文件") + private String[] paths; + + @Schema(description = "请求ip") + private String address; + + @Schema(description = "请求地址") + private String location; + + @Schema(description = "userAgent") + private String userAgent; + + @Schema(description = "操作类型") + private String type; + + @Schema(description = "参数") + private Map extra; + + @Schema(description = "操作结果 0失败 1成功") + private Integer result; + + @Schema(description = "开始时间") + private Date startTime; + +} diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/service/TerminalFileLogService.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/service/TerminalFileLogService.java new file mode 100644 index 00000000..78aa9cac --- /dev/null +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/service/TerminalFileLogService.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023 - present Dromara, All rights reserved. + * + * https://visor.dromara.org + * https://visor.dromara.org.cn + * https://visor.orionsec.cn + * + * Members: + * Jiahang Li - ljh1553488six@139.com - author + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.dromara.visor.module.terminal.service; + +import cn.orionsec.kit.lang.define.wrapper.DataGrid; +import org.dromara.visor.module.terminal.entity.request.terminal.TerminalFileLogQueryRequest; +import org.dromara.visor.module.terminal.entity.vo.TerminalFileLogVO; + +import java.util.List; + +/** + * 终端文件日志服务类 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2023-12-26 22:09 + */ +public interface TerminalFileLogService { + + /** + * 分页查询终端文件操作日志 + * + * @param request request + * @return rows + */ + DataGrid getTerminalFileLogPage(TerminalFileLogQueryRequest request); + + /** + * 获取终端文件操作日志数量 + * + * @param request request + * @return count + */ + Long getTerminalFileLogCount(TerminalFileLogQueryRequest request); + + /** + * 删除终端文件操作日志 + * + * @param idList idList + * @return effect + */ + Integer deleteTerminalFileLog(List idList); + +} diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/service/TerminalSftpService.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/service/TerminalSftpService.java index 7d1fe1fb..14f3fabc 100644 --- a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/service/TerminalSftpService.java +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/service/TerminalSftpService.java @@ -22,15 +22,11 @@ */ package org.dromara.visor.module.terminal.service; -import cn.orionsec.kit.lang.define.wrapper.DataGrid; -import org.dromara.visor.module.terminal.entity.request.terminal.TerminalSftpLogQueryRequest; -import org.dromara.visor.module.terminal.entity.vo.TerminalSftpLogVO; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; import javax.servlet.http.HttpServletResponse; import java.io.IOException; -import java.util.List; /** * SFTP 操作 服务类 @@ -41,30 +37,6 @@ import java.util.List; */ public interface TerminalSftpService { - /** - * 分页查询 SFTP 操作日志 - * - * @param request request - * @return rows - */ - DataGrid getTerminalSftpLogPage(TerminalSftpLogQueryRequest request); - - /** - * 获取 SFTP 操作日志数量 - * - * @param request request - * @return count - */ - Long getTerminalSftpLogCount(TerminalSftpLogQueryRequest request); - - /** - * 删除 SFTP 操作日志 - * - * @param idList idList - * @return effect - */ - Integer deleteTerminalSftpLog(List idList); - /** * 设置文件内容 * diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/service/impl/TerminalFileLogServiceImpl.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/service/impl/TerminalFileLogServiceImpl.java new file mode 100644 index 00000000..d8516115 --- /dev/null +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/service/impl/TerminalFileLogServiceImpl.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2023 - present Dromara, All rights reserved. + * + * https://visor.dromara.org + * https://visor.dromara.org.cn + * https://visor.orionsec.cn + * + * Members: + * Jiahang Li - ljh1553488six@139.com - author + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.dromara.visor.module.terminal.service.impl; + +import cn.orionsec.kit.lang.define.wrapper.DataGrid; +import cn.orionsec.kit.lang.utils.Arrays1; +import cn.orionsec.kit.lang.utils.Strings; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import lombok.extern.slf4j.Slf4j; +import org.dromara.visor.common.constant.ExtraFieldConst; +import org.dromara.visor.framework.biz.operator.log.core.utils.OperatorLogs; +import org.dromara.visor.module.infra.api.OperatorLogApi; +import org.dromara.visor.module.infra.entity.dto.operator.OperatorLogQueryDTO; +import org.dromara.visor.module.terminal.convert.TerminalFileLogConvert; +import org.dromara.visor.module.terminal.define.operator.TerminalFileLogOperatorType; +import org.dromara.visor.module.terminal.entity.request.terminal.TerminalFileLogQueryRequest; +import org.dromara.visor.module.terminal.entity.vo.TerminalFileLogVO; +import org.dromara.visor.module.terminal.service.TerminalFileLogService; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Optional; + +/** + * 终端文件日志服务实现类 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2024/3/4 23:35 + */ +@Slf4j +@Service +public class TerminalFileLogServiceImpl implements TerminalFileLogService { + + @Resource + private OperatorLogApi operatorLogApi; + + @Override + public DataGrid getTerminalFileLogPage(TerminalFileLogQueryRequest request) { + // 查询 + OperatorLogQueryDTO query = this.buildQueryInfo(request); + // 转换 + return operatorLogApi.getOperatorLogPage(query) + .map(s -> { + JSONObject extra = JSON.parseObject(s.getExtra()); + TerminalFileLogVO vo = TerminalFileLogConvert.MAPPER.to(s); + vo.setHostId(extra.getLong(ExtraFieldConst.HOST_ID)); + vo.setHostName(extra.getString(ExtraFieldConst.HOST_NAME)); + vo.setHostAddress(extra.getString(ExtraFieldConst.ADDRESS)); + String[] paths = Optional.ofNullable(extra.getString(ExtraFieldConst.PATH)) + .map(p -> p.split("\\|")) + .orElse(new String[0]); + vo.setPaths(paths); + vo.setExtra(extra); + return vo; + }); + } + + @Override + public Long getTerminalFileLogCount(TerminalFileLogQueryRequest request) { + // 查询 + OperatorLogQueryDTO query = this.buildQueryInfo(request); + // 转换 + return operatorLogApi.getOperatorLogCount(query); + } + + @Override + public Integer deleteTerminalFileLog(List idList) { + log.info("TerminalSftpService.deleteTerminalFileLog start {}", JSON.toJSONString(idList)); + Integer effect = operatorLogApi.deleteOperatorLog(idList); + log.info("TerminalSftpService.deleteTerminalFileLog finish {}", effect); + // 设置日志参数 + OperatorLogs.add(OperatorLogs.COUNT, effect); + return effect; + } + + /** + * 构建查询对象 + * + * @param request request + * @return query + */ + private OperatorLogQueryDTO buildQueryInfo(TerminalFileLogQueryRequest request) { + Long hostId = request.getHostId(); + String type = request.getType(); + // 构建参数 + OperatorLogQueryDTO query = OperatorLogQueryDTO.builder() + .userId(request.getUserId()) + .result(request.getResult()) + .startTimeStart(Arrays1.getIfPresent(request.getStartTimeRange(), 0)) + .startTimeEnd(Arrays1.getIfPresent(request.getStartTimeRange(), 1)) + .build(); + query.setPage(request.getPage()); + query.setLimit(request.getLimit()); + query.setOrder(request.getOrder()); + if (Strings.isBlank(type)) { + // 查询全部文件操作类型 + query.setTypeList(TerminalFileLogOperatorType.TYPES); + } else { + query.setType(type); + } + // 模糊查询 + if (hostId != null) { + query.setExtra("\"hostId\": " + hostId + ","); + } + return query; + } + +} diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/service/impl/TerminalSftpServiceImpl.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/service/impl/TerminalSftpServiceImpl.java index 566d32c3..ed427cde 100644 --- a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/service/impl/TerminalSftpServiceImpl.java +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/service/impl/TerminalSftpServiceImpl.java @@ -23,9 +23,7 @@ package org.dromara.visor.module.terminal.service.impl; import cn.orionsec.kit.lang.constant.StandardContentType; -import cn.orionsec.kit.lang.define.wrapper.DataGrid; import cn.orionsec.kit.lang.define.wrapper.HttpWrapper; -import cn.orionsec.kit.lang.utils.Arrays1; import cn.orionsec.kit.lang.utils.Exceptions; import cn.orionsec.kit.lang.utils.Strings; import cn.orionsec.kit.lang.utils.Valid; @@ -34,27 +32,17 @@ import cn.orionsec.kit.lang.utils.io.Streams; import cn.orionsec.kit.net.host.SessionStore; import cn.orionsec.kit.net.host.sftp.SftpExecutor; import cn.orionsec.kit.web.servlet.web.Servlets; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; import org.dromara.visor.common.constant.Const; import org.dromara.visor.common.constant.ErrorMessage; -import org.dromara.visor.common.constant.ExtraFieldConst; import org.dromara.visor.common.session.config.SshConnectConfig; import org.dromara.visor.common.session.ssh.SessionStores; -import org.dromara.visor.framework.biz.operator.log.core.utils.OperatorLogs; import org.dromara.visor.framework.redis.core.utils.RedisStrings; import org.dromara.visor.framework.security.core.utils.SecurityUtils; import org.dromara.visor.module.asset.api.HostConnectApi; -import org.dromara.visor.module.infra.api.OperatorLogApi; -import org.dromara.visor.module.infra.entity.dto.operator.OperatorLogQueryDTO; -import org.dromara.visor.module.terminal.convert.TerminalSftpLogConvert; import org.dromara.visor.module.terminal.define.cache.TerminalCacheKeyDefine; -import org.dromara.visor.module.terminal.define.operator.TerminalOperatorType; import org.dromara.visor.module.terminal.entity.dto.SftpGetContentCacheDTO; import org.dromara.visor.module.terminal.entity.dto.SftpSetContentCacheDTO; -import org.dromara.visor.module.terminal.entity.request.terminal.TerminalSftpLogQueryRequest; -import org.dromara.visor.module.terminal.entity.vo.TerminalSftpLogVO; import org.dromara.visor.module.terminal.handler.transfer.manager.TerminalTransferManager; import org.dromara.visor.module.terminal.handler.transfer.session.DownloadSession; import org.dromara.visor.module.terminal.service.TerminalSftpService; @@ -67,7 +55,6 @@ import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.List; import java.util.Optional; /** @@ -81,54 +68,12 @@ import java.util.Optional; @Service public class TerminalSftpServiceImpl implements TerminalSftpService { - @Resource - private OperatorLogApi operatorLogApi; - @Resource private HostConnectApi hostConnectApi; @Resource private TerminalTransferManager terminalTransferManager; - @Override - public DataGrid getTerminalSftpLogPage(TerminalSftpLogQueryRequest request) { - // 查询 - OperatorLogQueryDTO query = this.buildQueryInfo(request); - // 转换 - return operatorLogApi.getOperatorLogPage(query) - .map(s -> { - JSONObject extra = JSON.parseObject(s.getExtra()); - TerminalSftpLogVO vo = TerminalSftpLogConvert.MAPPER.to(s); - vo.setHostId(extra.getLong(ExtraFieldConst.HOST_ID)); - vo.setHostName(extra.getString(ExtraFieldConst.HOST_NAME)); - vo.setHostAddress(extra.getString(ExtraFieldConst.ADDRESS)); - String[] paths = Optional.ofNullable(extra.getString(ExtraFieldConst.PATH)) - .map(p -> p.split("\\|")) - .orElse(new String[0]); - vo.setPaths(paths); - vo.setExtra(extra); - return vo; - }); - } - - @Override - public Long getTerminalSftpLogCount(TerminalSftpLogQueryRequest request) { - // 查询 - OperatorLogQueryDTO query = this.buildQueryInfo(request); - // 转换 - return operatorLogApi.getOperatorLogCount(query); - } - - @Override - public Integer deleteTerminalSftpLog(List idList) { - log.info("TerminalSftpService.deleteSftpLog start {}", JSON.toJSONString(idList)); - Integer effect = operatorLogApi.deleteOperatorLog(idList); - log.info("TerminalSftpService.deleteSftpLog finish {}", effect); - // 设置日志参数 - OperatorLogs.add(OperatorLogs.COUNT, effect); - return effect; - } - @Override public void getFileContentByToken(String token, HttpServletResponse response) throws IOException { // 解析 token @@ -215,36 +160,4 @@ public class TerminalSftpServiceImpl implements TerminalSftpService { return session; } - /** - * 构建查询对象 - * - * @param request request - * @return query - */ - private OperatorLogQueryDTO buildQueryInfo(TerminalSftpLogQueryRequest request) { - Long hostId = request.getHostId(); - String type = request.getType(); - // 构建参数 - OperatorLogQueryDTO query = OperatorLogQueryDTO.builder() - .userId(request.getUserId()) - .result(request.getResult()) - .startTimeStart(Arrays1.getIfPresent(request.getStartTimeRange(), 0)) - .startTimeEnd(Arrays1.getIfPresent(request.getStartTimeRange(), 1)) - .build(); - query.setPage(request.getPage()); - query.setLimit(request.getLimit()); - query.setOrder(request.getOrder()); - if (Strings.isBlank(type)) { - // 查询全部 SFTP 类型 - query.setTypeList(TerminalOperatorType.SFTP_TYPES); - } else { - query.setType(type); - } - // 模糊查询 - if (hostId != null) { - query.setExtra("\"hostId\": " + hostId + ","); - } - return query; - } - } From 700b6d221db7b418b7882b8908528d94e389e00a Mon Sep 17 00:00:00 2001 From: lijiahangmax Date: Mon, 30 Jun 2025 17:12:36 +0800 Subject: [PATCH 13/19] =?UTF-8?q?:hammer:=20=E4=BF=AE=E6=94=B9=E4=B8=BB?= =?UTF-8?q?=E6=9C=BA=E8=87=AA=E5=8A=A8=E5=88=9B=E5=BB=BA=E9=85=8D=E7=BD=AE?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../asset/service/HostConfigService.java | 8 ++++++ .../service/impl/HostConfigServiceImpl.java | 28 +++++++++++++++++++ .../asset/service/impl/HostServiceImpl.java | 6 +++- .../model/TransferOperatorRequest.java | 7 ----- .../host-list/components/host-form-drawer.vue | 1 + .../host-list/components/host-form-info.vue | 2 ++ 6 files changed, 44 insertions(+), 8 deletions(-) diff --git a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/HostConfigService.java b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/HostConfigService.java index 2595ba88..0742ac7a 100644 --- a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/HostConfigService.java +++ b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/HostConfigService.java @@ -37,6 +37,14 @@ import java.util.List; */ public interface HostConfigService { + /** + * 初始化主机配置 + * + * @param hostId hostId + * @param types types + */ + void initHostConfig(Long hostId, List types); + /** * 更新主机配置 * diff --git a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/impl/HostConfigServiceImpl.java b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/impl/HostConfigServiceImpl.java index fba78e3c..96414948 100644 --- a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/impl/HostConfigServiceImpl.java +++ b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/impl/HostConfigServiceImpl.java @@ -67,6 +67,34 @@ public class HostConfigServiceImpl implements HostConfigService { @Resource private HostConfigDAO hostConfigDAO; + @Override + public void initHostConfig(Long hostId, List types) { + // 查询主机配置类型 + List hostConfigTypes = hostConfigDAO.selectByHostId(hostId) + .stream() + .map(HostConfigDO::getType) + .collect(Collectors.toList()); + List configs = new ArrayList<>(); + for (String type : types) { + // 配置存在则跳过 + if (hostConfigTypes.contains(type)) { + continue; + } + // 配置不存在则初始化 + HostConfigDO config = HostConfigDO.builder() + .hostId(hostId) + .type(type) + .status(EnableStatus.ENABLED.name()) + .config(HostConfigStrategyEnum.of(type).getDefault().serial()) + .build(); + configs.add(config); + } + // 插入主机配置 + if (!configs.isEmpty()) { + hostConfigDAO.insertBatch(configs); + } + } + @Override public Integer updateHostConfig(HostConfigUpdateRequest request) { log.info("HostConfigService-updateHostConfig request: {}", request); diff --git a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/impl/HostServiceImpl.java b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/impl/HostServiceImpl.java index 94e33f98..b5a2c178 100644 --- a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/impl/HostServiceImpl.java +++ b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/impl/HostServiceImpl.java @@ -128,8 +128,10 @@ public class HostServiceImpl implements HostService { this.checkHostCodePresent(record); // 插入主机 int effect = hostDAO.insert(record); - log.info("HostService-createHost effect: {}", effect); Long id = record.getId(); + log.info("HostService-createHost id: {}, effect: {}", id, effect); + // 初始化主机配置 + hostConfigService.initHostConfig(id, request.getTypes()); // 插入 tag tagRelApi.addTagRel(TagTypeEnum.HOST, id, request.getTags()); // 引用分组 @@ -183,6 +185,8 @@ public class HostServiceImpl implements HostService { // 修改 config 状态 hostConfigDAO.updateConfigStatus(id, types, EnableStatus.ENABLED.name()); hostConfigDAO.updateConfigInvertStatus(id, types, EnableStatus.DISABLED.name()); + // 初始化主机配置 + hostConfigService.initHostConfig(id, types); // 删除缓存 this.clearCache(); return effect; diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/transfer/model/TransferOperatorRequest.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/transfer/model/TransferOperatorRequest.java index dd772baa..ff48cb1f 100644 --- a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/transfer/model/TransferOperatorRequest.java +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/transfer/model/TransferOperatorRequest.java @@ -27,8 +27,6 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import java.util.List; - /** * 文件操作请求 实体对象 * @@ -62,11 +60,6 @@ public class TransferOperatorRequest { */ private Long hostId; - /** - * 被压缩文件路径 - */ - private List paths; - /** * 错误信息 后端赋值 */ diff --git a/orion-visor-ui/src/views/asset/host-list/components/host-form-drawer.vue b/orion-visor-ui/src/views/asset/host-list/components/host-form-drawer.vue index 213ec1b9..85dc3ed0 100644 --- a/orion-visor-ui/src/views/asset/host-list/components/host-form-drawer.vue +++ b/orion-visor-ui/src/views/asset/host-list/components/host-form-drawer.vue @@ -133,6 +133,7 @@ // 更新主机信息 const onUpdateHostInfo = (id: number) => { + title.value = '修改主机'; hostId.value = id; hostViewUpdated.value = true; }; diff --git a/orion-visor-ui/src/views/asset/host-list/components/host-form-info.vue b/orion-visor-ui/src/views/asset/host-list/components/host-form-info.vue index 0a45dae8..cc8c76a7 100644 --- a/orion-visor-ui/src/views/asset/host-list/components/host-form-info.vue +++ b/orion-visor-ui/src/views/asset/host-list/components/host-form-info.vue @@ -194,11 +194,13 @@ // 复制 const { data } = await copyHost(formModel.value); Message.success('复制成功'); + formModel.value.id = data; emits('updated', data); } else if (!formModel.value.id) { // 新增 const { data } = await createHost(formModel.value); Message.success('创建成功'); + formModel.value.id = data; emits('updated', data); } else { // 修改 From 5dcd8cbad269e727431fdc925b44fce0141789b6 Mon Sep 17 00:00:00 2001 From: lijiahangmax Date: Mon, 30 Jun 2025 21:59:24 +0800 Subject: [PATCH 14/19] =?UTF-8?q?:hammer:=20=E4=BC=98=E5=8C=96=E4=BC=A0?= =?UTF-8?q?=E8=BE=93=E6=A8=A1=E5=9D=97=E6=97=A5=E5=BF=97=E9=80=BB=E8=BE=91?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../visor/module/common/utils/SftpUtils.java | 19 ------------ .../define/operator/TerminalOperatorType.java | 6 ++-- .../SftpDownloadFlatDirectoryHandler.java | 3 +- .../terminal/handler/SftpRemoveHandler.java | 7 +++-- .../handler/TerminalConnectHandler.java | 1 + .../model/transport/TerminalSetInfo.java | 5 +++ .../handler/terminal/session/SftpSession.java | 3 +- .../model/TransferOperatorRequest.java | 5 +++ .../transfer/session/DownloadSession.java | 3 +- .../transfer/session/TransferSession.java | 29 +++++++---------- .../transfer/session/UploadSession.java | 3 +- .../handler/transfer/utils/TransferUtils.java | 31 +++++++++++++++++++ .../module/terminal/utils/SftpFileUtils.java | 28 +++++++++++++++++ 13 files changed, 96 insertions(+), 47 deletions(-) diff --git a/orion-visor-modules/orion-visor-module-common/src/main/java/org/dromara/visor/module/common/utils/SftpUtils.java b/orion-visor-modules/orion-visor-module-common/src/main/java/org/dromara/visor/module/common/utils/SftpUtils.java index 2c662e1f..6dda7b46 100644 --- a/orion-visor-modules/orion-visor-module-common/src/main/java/org/dromara/visor/module/common/utils/SftpUtils.java +++ b/orion-visor-modules/orion-visor-module-common/src/main/java/org/dromara/visor/module/common/utils/SftpUtils.java @@ -22,10 +22,8 @@ */ package org.dromara.visor.module.common.utils; -import cn.orionsec.kit.lang.constant.Letters; import cn.orionsec.kit.lang.utils.Booleans; import cn.orionsec.kit.lang.utils.Strings; -import cn.orionsec.kit.lang.utils.io.Files1; import cn.orionsec.kit.net.host.sftp.SftpExecutor; import cn.orionsec.kit.net.host.sftp.SftpFile; import cn.orionsec.kit.spring.SpringHolder; @@ -69,21 +67,4 @@ public class SftpUtils { } } - /** - * 获取移动目标路径 - * - * @param source source - * @param target target - * @return absolute target - */ - public static String getAbsoluteTargetPath(String source, String target) { - if (target.charAt(0) == Letters.SLASH) { - // 绝对路径 - return Files1.getPath(Files1.normalize(target)); - } else { - // 相对路径 - return Files1.getPath(Files1.normalize(Files1.getPath(source + "/../" + target))); - } - } - } diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/define/operator/TerminalOperatorType.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/define/operator/TerminalOperatorType.java index 162c6e59..9752e2f5 100644 --- a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/define/operator/TerminalOperatorType.java +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/define/operator/TerminalOperatorType.java @@ -74,15 +74,15 @@ public class TerminalOperatorType extends InitializingOperatorTypes { new OperatorType(OperatorRiskLevel.L, SFTP_MKDIR, "创建文件夹 ${hostName} ${path}"), new OperatorType(OperatorRiskLevel.L, SFTP_TOUCH, "创建文件 ${hostName} ${path}"), new OperatorType(OperatorRiskLevel.M, SFTP_MOVE, "移动文件 ${hostName} ${path}${target}"), - new OperatorType(OperatorRiskLevel.H, SFTP_REMOVE, "删除文件 ${hostName} ${path}"), + new OperatorType(OperatorRiskLevel.H, SFTP_REMOVE, "删除文件 ${hostName} ${count}个\n${path}"), new OperatorType(OperatorRiskLevel.H, SFTP_TRUNCATE, "截断文件 ${hostName} ${path}"), new OperatorType(OperatorRiskLevel.M, SFTP_CHMOD, "文件提权 ${hostName} ${path} ${mod}"), new OperatorType(OperatorRiskLevel.M, SFTP_CHOWN, "修改文件归属 ${hostName} ${path} ${id}"), new OperatorType(OperatorRiskLevel.M, SFTP_CHGRP, "修改文件分组 ${hostName} ${path} ${id}"), new OperatorType(OperatorRiskLevel.L, SFTP_GET_CONTENT, "获取文件内容 ${hostName} ${path}"), new OperatorType(OperatorRiskLevel.M, SFTP_SET_CONTENT, "修改文件内容 ${hostName} ${path}"), - new OperatorType(OperatorRiskLevel.M, SFTP_UPLOAD, "上传文件 ${hostName} ${path}"), - new OperatorType(OperatorRiskLevel.M, SFTP_DOWNLOAD, "下载文件 ${hostName} ${path}"), + new OperatorType(OperatorRiskLevel.M, SFTP_UPLOAD, "上传文件 ${hostName} (${count}个)\n${path}"), + new OperatorType(OperatorRiskLevel.M, SFTP_DOWNLOAD, "下载文件 ${hostName} (${count}个)\n${path}"), new OperatorType(OperatorRiskLevel.M, RDP_UPLOAD, "上传文件 ${hostName} ${path}"), new OperatorType(OperatorRiskLevel.M, RDP_DOWNLOAD, "下载文件 ${hostName} ${path}"), }; diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/handler/SftpDownloadFlatDirectoryHandler.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/handler/SftpDownloadFlatDirectoryHandler.java index eaa03ac8..6dc9b143 100644 --- a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/handler/SftpDownloadFlatDirectoryHandler.java +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/handler/SftpDownloadFlatDirectoryHandler.java @@ -29,6 +29,7 @@ import org.dromara.visor.module.terminal.handler.terminal.model.request.SftpDown import org.dromara.visor.module.terminal.handler.terminal.model.response.SftpFileVO; import org.dromara.visor.module.terminal.handler.terminal.sender.ISftpTerminalSender; import org.dromara.visor.module.terminal.handler.terminal.session.ISftpSession; +import org.dromara.visor.module.terminal.utils.SftpFileUtils; import org.springframework.stereotype.Component; import java.util.Arrays; @@ -50,7 +51,7 @@ public class SftpDownloadFlatDirectoryHandler extends AbstractTerminalHandler files = Lists.empty(); diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/handler/SftpRemoveHandler.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/handler/SftpRemoveHandler.java index 81693ce7..df595b10 100644 --- a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/handler/SftpRemoveHandler.java +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/handler/SftpRemoveHandler.java @@ -24,12 +24,14 @@ package org.dromara.visor.module.terminal.handler.terminal.handler; import cn.orionsec.kit.lang.utils.collect.Maps; import lombok.extern.slf4j.Slf4j; +import org.dromara.visor.common.constant.Const; import org.dromara.visor.framework.biz.operator.log.core.utils.OperatorLogs; import org.dromara.visor.module.terminal.define.operator.TerminalOperatorType; import org.dromara.visor.module.terminal.handler.terminal.model.TerminalChannelProps; import org.dromara.visor.module.terminal.handler.terminal.model.request.SftpBaseRequest; import org.dromara.visor.module.terminal.handler.terminal.sender.ISftpTerminalSender; import org.dromara.visor.module.terminal.handler.terminal.session.ISftpSession; +import org.dromara.visor.module.terminal.utils.SftpFileUtils; import org.springframework.stereotype.Component; import java.util.Arrays; @@ -49,11 +51,11 @@ public class SftpRemoveHandler extends AbstractTerminalHandler extra = Maps.newMap(); extra.put(OperatorLogs.PATH, path); + extra.put(OperatorLogs.COUNT, paths.length); this.saveOperatorLog(props, extra, TerminalOperatorType.SFTP_REMOVE, startTime, ex); diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/handler/TerminalConnectHandler.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/handler/TerminalConnectHandler.java index d2aab8b7..14555223 100644 --- a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/handler/TerminalConnectHandler.java +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/handler/TerminalConnectHandler.java @@ -129,6 +129,7 @@ public class TerminalConnectHandler extends AbstractTerminalHandler extra = Maps.newMap(); - extra.put(OperatorLogs.PATH, path); - extra.put(OperatorLogs.HOST_ID, connectConfig.getHostId()); - extra.put(OperatorLogs.HOST_NAME, connectConfig.getHostName()); - extra.put(OperatorLogs.ADDRESS, connectConfig.getHostAddress()); - // 获取日志 - TerminalChannelProps props = WebSockets.getAttr(channel, FieldConst.PROPS); - OperatorLogModel model = TerminalUtils.getOperatorLogModel(props, extra, type, System.currentTimeMillis(), null); - // 保存 + protected void saveOperatorLog(Long logId, String type, List paths) { + String path = String.join(Const.LF, paths); + int count = paths.size(); + // 获取操作日志 + OperatorLogModel model = TransferUtils.getOperatorLogModel(type, path, count, connectConfig, WebSockets.getAttr(channel, FieldConst.PROPS)); + // 保存操作日志 TerminalAsyncSaver.saveOperatorLog(model); } diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/transfer/session/UploadSession.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/transfer/session/UploadSession.java index 30955a2b..447aa69b 100644 --- a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/transfer/session/UploadSession.java +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/transfer/session/UploadSession.java @@ -22,6 +22,7 @@ */ package org.dromara.visor.module.terminal.handler.transfer.session; +import cn.orionsec.kit.lang.utils.collect.Lists; import cn.orionsec.kit.lang.utils.io.Streams; import cn.orionsec.kit.net.host.SessionStore; import lombok.extern.slf4j.Slf4j; @@ -58,7 +59,7 @@ public class UploadSession extends TransferSession { try { log.info("UploadSession.startUpload start channelId: {}, path: {}", channelId, path); // 保存操作日志 - this.saveOperatorLog(TerminalOperatorType.SFTP_UPLOAD, path); + this.saveOperatorLog(request.getLogId(), TerminalOperatorType.SFTP_UPLOAD, Lists.singleton(path)); // 检查连接 this.init(); // 检查文件是否存在 diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/transfer/utils/TransferUtils.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/transfer/utils/TransferUtils.java index d4b0cf2a..8dcf85d3 100644 --- a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/transfer/utils/TransferUtils.java +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/transfer/utils/TransferUtils.java @@ -23,14 +23,21 @@ package org.dromara.visor.module.terminal.handler.transfer.utils; import cn.orionsec.kit.lang.utils.Strings; +import cn.orionsec.kit.lang.utils.collect.Maps; import com.alibaba.fastjson.JSON; import org.apache.catalina.connector.ClientAbortException; import org.dromara.visor.common.constant.ErrorMessage; +import org.dromara.visor.common.session.config.BaseConnectConfig; +import org.dromara.visor.framework.biz.operator.log.core.model.OperatorLogModel; +import org.dromara.visor.framework.biz.operator.log.core.utils.OperatorLogs; import org.dromara.visor.framework.websocket.core.utils.WebSockets; +import org.dromara.visor.module.terminal.handler.terminal.model.TerminalChannelProps; +import org.dromara.visor.module.terminal.handler.terminal.utils.TerminalUtils; import org.dromara.visor.module.terminal.handler.transfer.enums.TransferReceiver; import org.dromara.visor.module.terminal.handler.transfer.model.TransferOperatorResponse; import org.springframework.web.socket.WebSocketSession; +import java.util.Map; import java.util.function.Consumer; /** @@ -45,6 +52,30 @@ public class TransferUtils { private TransferUtils() { } + /** + * 获取传输操作日志 + * + * @param type type + * @param path path + * @param count count + * @param config config + * @param props props + */ + public static OperatorLogModel getOperatorLogModel(String type, + String path, Integer count, + BaseConnectConfig config, + TerminalChannelProps props) { + // 设置参数 + Map extra = Maps.newMap(); + extra.put(OperatorLogs.PATH, path); + extra.put(OperatorLogs.COUNT, count); + extra.put(OperatorLogs.HOST_ID, config.getHostId()); + extra.put(OperatorLogs.HOST_NAME, config.getHostName()); + extra.put(OperatorLogs.ADDRESS, config.getHostAddress()); + // 获取操作日志 + return TerminalUtils.getOperatorLogModel(props, extra, type, System.currentTimeMillis(), null); + } + /** * 发送消息 * diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/utils/SftpFileUtils.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/utils/SftpFileUtils.java index 27eb64e5..8c97c447 100644 --- a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/utils/SftpFileUtils.java +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/utils/SftpFileUtils.java @@ -22,6 +22,7 @@ */ package org.dromara.visor.module.terminal.utils; +import cn.orionsec.kit.lang.constant.Letters; import cn.orionsec.kit.lang.utils.io.FileType; import cn.orionsec.kit.lang.utils.io.Files1; import cn.orionsec.kit.net.host.sftp.SftpFile; @@ -41,6 +42,33 @@ public class SftpFileUtils { private SftpFileUtils() { } + /** + * 获取移动目标路径 + * + * @param source source + * @param target target + * @return absolute target + */ + public static String getAbsoluteTargetPath(String source, String target) { + if (target.charAt(0) == Letters.SLASH) { + // 绝对路径 + return Files1.getPath(Files1.normalize(target)); + } else { + // 相对路径 + return Files1.getPath(Files1.normalize(Files1.getPath(source + "/../" + target))); + } + } + + /** + * 分割文件路径 + * + * @param path path + * @return paths + */ + public static String[] fromMultiPaths(String path) { + return path.split("\\|"); + } + /** * 转为文件 * From 4468a429dd215db6af356d1829f099a752172287 Mon Sep 17 00:00:00 2001 From: lijiahangmax Date: Mon, 30 Jun 2025 22:12:28 +0800 Subject: [PATCH 15/19] =?UTF-8?q?:hammer:=20=E4=BF=AE=E6=94=B9=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E4=BC=A0=E8=BE=93=E9=80=BB=E8=BE=91.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/view/sftp/sftp-chmod-modal.vue | 14 ++--- .../view/sftp/sftp-create-modal.vue | 19 +++--- .../components/view/sftp/sftp-move-modal.vue | 14 ++--- .../view/sftp/sftp-table-header.vue | 29 ++++----- .../components/view/sftp/sftp-table.vue | 13 ++-- .../view/sftp/sftp-upload-modal.vue | 12 ++-- .../components/view/sftp/sftp-view.vue | 59 ++++++++++--------- .../terminal/components/view/ssh/ssh-view.vue | 4 +- .../src/views/terminal/interfaces/session.ts | 1 + .../src/views/terminal/interfaces/transfer.ts | 6 +- .../service/channel/base-terminal-channel.ts | 10 ++-- .../service/handler/ssh-session-handler.ts | 2 +- .../terminal/service/session/base-session.ts | 2 +- .../transfer/sftp-base-transfer-task.ts | 14 +++-- .../transfer/sftp-file-download-task.ts | 6 +- .../service/transfer/sftp-file-upload-task.ts | 6 +- .../service/transfer/sftp-transfer-manager.ts | 9 +-- .../src/views/terminal/types/const.ts | 12 ---- 18 files changed, 110 insertions(+), 122 deletions(-) diff --git a/orion-visor-ui/src/views/terminal/components/view/sftp/sftp-chmod-modal.vue b/orion-visor-ui/src/views/terminal/components/view/sftp/sftp-chmod-modal.vue index 5269c3c3..584ba3f4 100644 --- a/orion-visor-ui/src/views/terminal/components/view/sftp/sftp-chmod-modal.vue +++ b/orion-visor-ui/src/views/terminal/components/view/sftp/sftp-chmod-modal.vue @@ -48,12 +48,13 @@ import type { ISftpSession } from '@/views/terminal/interfaces'; import useVisible from '@/hooks/visible'; import { nextTick, ref } from 'vue'; - import { useTerminalStore } from '@/store'; import { permission10toString } from '@/utils/file'; - import { TerminalSessionTypes } from '@/views/terminal/types/const'; + + const props = defineProps<{ + session?: ISftpSession; + }>(); const { visible, setVisible } = useVisible(); - const { sessionManager } = useTerminalStore(); const sessionKey = ref(); const modRef = ref(); @@ -92,10 +93,9 @@ if (error) { return false; } - // 获取会话 - const session = sessionManager.getSession(sessionKey.value); - if (session?.type === TerminalSessionTypes.SFTP.type) { - session.chmod(formModel.value.path, formModel.value.mod); + // 提权 + if (props.session) { + props.session.chmod(formModel.value.path, formModel.value.mod); } } catch (e) { return false; diff --git a/orion-visor-ui/src/views/terminal/components/view/sftp/sftp-create-modal.vue b/orion-visor-ui/src/views/terminal/components/view/sftp/sftp-create-modal.vue index 2552ab05..9f4ecc03 100644 --- a/orion-visor-ui/src/views/terminal/components/view/sftp/sftp-create-modal.vue +++ b/orion-visor-ui/src/views/terminal/components/view/sftp/sftp-create-modal.vue @@ -33,13 +33,13 @@ import type { ISftpSession } from '@/views/terminal/interfaces'; import useVisible from '@/hooks/visible'; import { nextTick, ref } from 'vue'; - import { useTerminalStore } from '@/store'; - import { TerminalSessionTypes } from '@/views/terminal/types/const'; + + const props = defineProps<{ + session?: ISftpSession; + }>(); const { visible, setVisible } = useVisible(); - const { sessionManager } = useTerminalStore(); - const sessionKey = ref(); const touch = ref(false); const pathRef = ref(); const formRef = ref(); @@ -48,8 +48,7 @@ }); // 打开新增 - const open = (key: string, path: string, isTouch: boolean) => { - sessionKey.value = key; + const open = (path: string, isTouch: boolean) => { if (path === '/') { formModel.value.path = path; } else { @@ -73,15 +72,13 @@ if (error) { return false; } - // 获取会话 - const session = sessionManager.getSession(sessionKey.value); - if (session?.type === TerminalSessionTypes.SFTP.type) { + if (props.session) { if (touch.value) { // 创建文件 - session.touch(formModel.value.path); + props.session.touch(formModel.value.path); } else { // 创建文件夹 - session.mkdir(formModel.value.path); + props.session.mkdir(formModel.value.path); } } } catch (e) { diff --git a/orion-visor-ui/src/views/terminal/components/view/sftp/sftp-move-modal.vue b/orion-visor-ui/src/views/terminal/components/view/sftp/sftp-move-modal.vue index 1a1fdc97..67784643 100644 --- a/orion-visor-ui/src/views/terminal/components/view/sftp/sftp-move-modal.vue +++ b/orion-visor-ui/src/views/terminal/components/view/sftp/sftp-move-modal.vue @@ -41,11 +41,12 @@ import type { ISftpSession } from '@/views/terminal/interfaces'; import useVisible from '@/hooks/visible'; import { nextTick, ref } from 'vue'; - import { useTerminalStore } from '@/store'; - import { TerminalSessionTypes } from '@/views/terminal/types/const'; + + const props = defineProps<{ + session?: ISftpSession; + }>(); const { visible, setVisible } = useVisible(); - const { sessionManager } = useTerminalStore(); const sessionKey = ref(); const targetRef = ref(); @@ -77,10 +78,9 @@ if (error) { return false; } - // 获取会话 - const session = sessionManager.getSession(sessionKey.value); - if (session?.type === TerminalSessionTypes.SFTP.type) { - session.move(formModel.value.path, formModel.value.target); + // 移动文件 + if (props.session) { + props.session.move(formModel.value.path, formModel.value.target); } } catch (e) { return false; diff --git a/orion-visor-ui/src/views/terminal/components/view/sftp/sftp-table-header.vue b/orion-visor-ui/src/views/terminal/components/view/sftp/sftp-table-header.vue index 98664f0b..6de1fd27 100644 --- a/orion-visor-ui/src/views/terminal/components/view/sftp/sftp-table-header.vue +++ b/orion-visor-ui/src/views/terminal/components/view/sftp/sftp-table-header.vue @@ -135,7 +135,7 @@ arrow-class="terminal-tooltip-content" content="创建文件"> + @click="createFile(true)"> @@ -148,7 +148,7 @@ arrow-class="terminal-tooltip-content" content="创建文件夹"> + @click="createFile(false)"> @@ -174,7 +174,7 @@ arrow-class="terminal-tooltip-content" content="上传"> + @click="openUpload"> @@ -204,9 +204,8 @@