From 985091f42b564250359762f6c4f74f526a363a40 Mon Sep 17 00:00:00 2001 From: lijiahangmax Date: Sat, 14 Dec 2024 12:41:06 +0800 Subject: [PATCH] =?UTF-8?q?:hammer:=20=E4=B8=8A=E4=BC=A0=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E7=94=A8=E6=88=B7=E8=BF=9E=E6=8E=A5=E9=85=8D?= =?UTF-8?q?=E7=BD=AE.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/constant/ErrorMessage.java | 2 + .../visor/framework/common/utils/Valid.java | 4 +- .../module/asset/dao/UploadTaskFileDAO.java | 26 +-- .../asset/entity/domain/UploadTaskFileDO.java | 4 + .../asset/entity/vo/UploadTaskFileVO.java | 3 + ...SshOsTypeEnum.java => HostOsTypeEnum.java} | 6 +- .../host/exec/log/tracker/ExecLogTracker.java | 8 +- .../transfer/session/TransferSession.java | 2 +- .../host/transfer/session/UploadSession.java | 2 +- .../upload/model/FileUploadConfigDTO.java | 63 ++++++++ .../upload/model/FileUploadFileItemDTO.java | 4 +- .../host/upload/task/FileUploadTask.java | 16 +- .../host/upload/uploader/FileUploader.java | 150 +++++++++++++----- .../service/impl/ExecCommandServiceImpl.java | 4 +- .../service/impl/UploadTaskServiceImpl.java | 82 ++-------- .../resources/mapper/UploadTaskFileMapper.xml | 3 +- orion-visor-ui/src/api/exec/upload-task.ts | 1 + .../asset/host-identity/types/card.fields.ts | 8 +- .../host-identity/types/table.columns.ts | 8 +- .../components/batch-upload-progress.vue | 6 +- .../batch-upload/components/upload-panel.vue | 3 +- .../components/exec-job-detail-drawer.vue | 6 +- .../components/exec-user-update-modal.vue | 2 + 23 files changed, 266 insertions(+), 147 deletions(-) rename orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/enums/{HostSshOsTypeEnum.java => HostOsTypeEnum.java} (92%) create mode 100644 orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/upload/model/FileUploadConfigDTO.java diff --git a/orion-visor-framework/orion-visor-framework-common/src/main/java/org/dromara/visor/framework/common/constant/ErrorMessage.java b/orion-visor-framework/orion-visor-framework-common/src/main/java/org/dromara/visor/framework/common/constant/ErrorMessage.java index 49b5e723..735006d0 100644 --- a/orion-visor-framework/orion-visor-framework-common/src/main/java/org/dromara/visor/framework/common/constant/ErrorMessage.java +++ b/orion-visor-framework/orion-visor-framework-common/src/main/java/org/dromara/visor/framework/common/constant/ErrorMessage.java @@ -119,6 +119,8 @@ public interface ErrorMessage { String AUTH_ERROR = "认证失败"; + String FILE_UPLOAD_ERROR = "文件上传失败"; + String SCRIPT_UPLOAD_ERROR = "脚本上传失败"; String EXEC_ERROR = "执行失败"; diff --git a/orion-visor-framework/orion-visor-framework-common/src/main/java/org/dromara/visor/framework/common/utils/Valid.java b/orion-visor-framework/orion-visor-framework-common/src/main/java/org/dromara/visor/framework/common/utils/Valid.java index 4a730011..b413b490 100644 --- a/orion-visor-framework/orion-visor-framework-common/src/main/java/org/dromara/visor/framework/common/utils/Valid.java +++ b/orion-visor-framework/orion-visor-framework-common/src/main/java/org/dromara/visor/framework/common/utils/Valid.java @@ -37,7 +37,7 @@ import java.util.function.Function; */ public class Valid extends cn.orionsec.kit.lang.utils.Valid { - private static final Validator VALIDATOR = SpringHolder.getBean(Validator.class); + private static final Validator validator = SpringHolder.getBean(Validator.class); public static T notNull(T object) { return notNull(object, ErrorMessage.PARAM_MISSING); @@ -123,7 +123,7 @@ public class Valid extends cn.orionsec.kit.lang.utils.Valid { public static T valid(T obj, Class... groups) { notNull(obj, ErrorMessage.PARAM_MISSING); // 验证对象 - Set> set = VALIDATOR.validate(obj, groups); + Set> set = validator.validate(obj, groups); if (!set.isEmpty()) { throw new ConstraintViolationException(set); } diff --git a/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/dao/UploadTaskFileDAO.java b/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/dao/UploadTaskFileDAO.java index 42df9b45..610cf746 100644 --- a/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/dao/UploadTaskFileDAO.java +++ b/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/dao/UploadTaskFileDAO.java @@ -20,7 +20,9 @@ import org.apache.ibatis.annotations.Mapper; import org.dromara.visor.framework.mybatis.core.mapper.IMapper; import org.dromara.visor.framework.mybatis.core.query.Conditions; import org.dromara.visor.module.asset.entity.domain.UploadTaskFileDO; +import org.dromara.visor.module.asset.enums.UploadTaskFileStatusEnum; +import java.util.Date; import java.util.List; /** @@ -58,35 +60,39 @@ public interface UploadTaskFileDAO extends IMapper { } /** - * 通过 taskId hostId 更新状态 + * 通过 taskId hostId 更新为失败 * - * @param taskId taskId - * @param hostId hostId - * @param status status + * @param taskId taskId + * @param hostId hostId + * @param errorMessage errorMessage * @return effect */ - default int updateStatusByTaskHostId(Long taskId, Long hostId, String status) { + default int updateToFailedByTaskHostId(Long taskId, Long hostId, String errorMessage) { // 条件 LambdaQueryWrapper wrapper = this.wrapper() .eq(UploadTaskFileDO::getTaskId, taskId) .eq(UploadTaskFileDO::getHostId, hostId); // 修改值 UploadTaskFileDO update = new UploadTaskFileDO(); - update.setStatus(status); + update.setStatus(UploadTaskFileStatusEnum.FAILED.name()); + update.setErrorMessage(errorMessage); + update.setStartTime(new Date()); + update.setEndTime(new Date()); // 更新 return this.update(update, wrapper); } /** - * 通过 id 更新状态 + * 通过 id 更新为取消 * * @param idList idList - * @param status status * @return effect */ - default int updateStatusByIdList(List idList, String status) { + default int updateToCanceledByIdList(List idList) { UploadTaskFileDO update = new UploadTaskFileDO(); - update.setStatus(status); + update.setStatus(UploadTaskFileStatusEnum.CANCELED.name()); + update.setStartTime(new Date()); + update.setEndTime(new Date()); // 更新 return this.update(update, Conditions.in(UploadTaskFileDO::getId, idList)); } diff --git a/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/entity/domain/UploadTaskFileDO.java b/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/entity/domain/UploadTaskFileDO.java index d075d6bd..5d5726c9 100644 --- a/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/entity/domain/UploadTaskFileDO.java +++ b/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/entity/domain/UploadTaskFileDO.java @@ -75,6 +75,10 @@ public class UploadTaskFileDO extends BaseDO { @TableField("status") private String status; + @Schema(description = "错误信息") + @TableField("error_message") + private String errorMessage; + @Schema(description = "开始时间") @TableField("start_time") private Date startTime; diff --git a/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/entity/vo/UploadTaskFileVO.java b/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/entity/vo/UploadTaskFileVO.java index a019073b..603dbeef 100644 --- a/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/entity/vo/UploadTaskFileVO.java +++ b/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/entity/vo/UploadTaskFileVO.java @@ -67,6 +67,9 @@ public class UploadTaskFileVO implements Serializable { @Schema(description = "状态") private String status; + @Schema(description = "错误信息") + private String errorMessage; + @Schema(description = "开始时间") private Date startTime; diff --git a/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/enums/HostSshOsTypeEnum.java b/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/enums/HostOsTypeEnum.java similarity index 92% rename from orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/enums/HostSshOsTypeEnum.java rename to orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/enums/HostOsTypeEnum.java index 21b8ce51..ec4dbde5 100644 --- a/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/enums/HostSshOsTypeEnum.java +++ b/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/enums/HostOsTypeEnum.java @@ -27,7 +27,7 @@ import lombok.Getter; */ @Getter @AllArgsConstructor -public enum HostSshOsTypeEnum { +public enum HostOsTypeEnum { /** * linux @@ -43,11 +43,11 @@ public enum HostSshOsTypeEnum { private final String scriptSuffix; - public static HostSshOsTypeEnum of(String type) { + public static HostOsTypeEnum of(String type) { if (type == null) { return LINUX; } - for (HostSshOsTypeEnum value : values()) { + for (HostOsTypeEnum value : values()) { if (value.name().equals(type)) { return value; } diff --git a/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/exec/log/tracker/ExecLogTracker.java b/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/exec/log/tracker/ExecLogTracker.java index 6c6ae814..a84dce53 100644 --- a/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/exec/log/tracker/ExecLogTracker.java +++ b/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/exec/log/tracker/ExecLogTracker.java @@ -38,7 +38,7 @@ import org.springframework.web.socket.WebSocketSession; @Slf4j public class ExecLogTracker implements IExecLogTracker { - private static final AppTrackerConfig TRACKER_CONFIG = SpringHolder.getBean(AppTrackerConfig.class); + private static final AppTrackerConfig trackerConfig = SpringHolder.getBean(AppTrackerConfig.class); private final WebSocketSession session; @@ -69,9 +69,9 @@ public class ExecLogTracker implements IExecLogTracker { try { this.tracker = new DelayTrackerListener(absolutePath, this); tracker.charset(config.getCharset()); - tracker.delayMillis(TRACKER_CONFIG.getDelay()); - tracker.offset(FileOffsetMode.LINE, TRACKER_CONFIG.getOffset()); - tracker.notFoundMode(FileNotFoundMode.WAIT_COUNT, TRACKER_CONFIG.getWaitTimes()); + tracker.delayMillis(trackerConfig.getDelay()); + tracker.offset(FileOffsetMode.LINE, trackerConfig.getOffset()); + tracker.notFoundMode(FileNotFoundMode.WAIT_COUNT, trackerConfig.getWaitTimes()); // 开始监听文件 tracker.run(); } catch (Exception e) { diff --git a/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/transfer/session/TransferSession.java b/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/transfer/session/TransferSession.java index 00271dab..cdb0e084 100644 --- a/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/transfer/session/TransferSession.java +++ b/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/transfer/session/TransferSession.java @@ -47,7 +47,7 @@ import java.util.Map; @Slf4j public abstract class TransferSession implements ITransferSession { - protected static final AppSftpConfig SFTP_CONFIG = SpringHolder.getBean(AppSftpConfig.class); + protected static final AppSftpConfig sftpConfig = SpringHolder.getBean(AppSftpConfig.class); protected final TerminalConnectDTO connectInfo; diff --git a/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/transfer/session/UploadSession.java b/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/transfer/session/UploadSession.java index f12f0b69..d67dce79 100644 --- a/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/transfer/session/UploadSession.java +++ b/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/transfer/session/UploadSession.java @@ -55,7 +55,7 @@ public class UploadSession extends TransferSession { // 检查连接 this.init(); // 检查文件是否存在 - SftpUtils.checkUploadFilePresent(SFTP_CONFIG, executor, path); + SftpUtils.checkUploadFilePresent(sftpConfig, executor, path); // 打开输出流 this.outputStream = executor.openOutputStream(path); // 响应结果 diff --git a/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/upload/model/FileUploadConfigDTO.java b/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/upload/model/FileUploadConfigDTO.java new file mode 100644 index 00000000..5caa7682 --- /dev/null +++ b/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/upload/model/FileUploadConfigDTO.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023 - present Jiahang Li (visor.orionsec.cn ljh1553488six@139.com). + * + * 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.asset.handler.host.upload.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * 文件上传配置 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2024/5/8 14:35 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class FileUploadConfigDTO { + + /** + * taskId + */ + private Long taskId; + + /** + * hostId + */ + private Long hostId; + + /** + * userId + */ + private Long userId; + + /** + * 上传路径 + */ + private String remotePath; + + /** + * 文件 + */ + private List files; + +} diff --git a/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/upload/model/FileUploadFileItemDTO.java b/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/upload/model/FileUploadFileItemDTO.java index ab8c24ca..730878ed 100644 --- a/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/upload/model/FileUploadFileItemDTO.java +++ b/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/upload/model/FileUploadFileItemDTO.java @@ -44,9 +44,9 @@ public class FileUploadFileItemDTO { private String fileId; /** - * 远程路径 + * 文件路径 */ - private String remotePath; + private String filePath; /** * 当前大小 diff --git a/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/upload/task/FileUploadTask.java b/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/upload/task/FileUploadTask.java index dd2a75ab..7da4bab2 100644 --- a/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/upload/task/FileUploadTask.java +++ b/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/upload/task/FileUploadTask.java @@ -31,6 +31,7 @@ import org.dromara.visor.module.asset.entity.domain.UploadTaskFileDO; import org.dromara.visor.module.asset.enums.UploadTaskFileStatusEnum; import org.dromara.visor.module.asset.enums.UploadTaskStatusEnum; import org.dromara.visor.module.asset.handler.host.upload.manager.FileUploadTaskManager; +import org.dromara.visor.module.asset.handler.host.upload.model.FileUploadConfigDTO; import org.dromara.visor.module.asset.handler.host.upload.model.FileUploadFileItemDTO; import org.dromara.visor.module.asset.handler.host.upload.uploader.FileUploader; import org.dromara.visor.module.asset.handler.host.upload.uploader.IFileUploader; @@ -102,6 +103,8 @@ public class FileUploadTask implements IFileUploadTask { } catch (Exception e) { log.error("FileUploadTask.run error id: {}", id, e); } finally { + // 移除任务 + fileUploadTaskManager.removeTask(id); // 修改状态 if (canceled) { this.updateStatus(UploadTaskStatusEnum.CANCELED); @@ -110,8 +113,6 @@ public class FileUploadTask implements IFileUploadTask { } // 检查是否发送消息 this.checkSendMessage(); - // 移除任务 - fileUploadTaskManager.removeTask(id); // 释放资源 this.close(); } @@ -156,7 +157,7 @@ public class FileUploadTask implements IFileUploadTask { .map(s -> FileUploadFileItemDTO.builder() .id(s.getId()) .fileId(s.getFileId()) - .remotePath(s.getRealFilePath()) + .filePath(s.getFilePath()) .status(UploadTaskFileStatusEnum.WAITING.name()) .current(0L) .build()) @@ -165,7 +166,14 @@ public class FileUploadTask implements IFileUploadTask { return; } // 添加到上传器 - uploaderList.add(new FileUploader(id, k, files)); + FileUploadConfigDTO config = FileUploadConfigDTO.builder() + .taskId(id) + .hostId(k) + .userId(record.getUserId()) + .remotePath(record.getRemotePath()) + .files(files) + .build(); + uploaderList.add(new FileUploader(config)); }); } diff --git a/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/upload/uploader/FileUploader.java b/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/upload/uploader/FileUploader.java index a04e3395..ddddf070 100644 --- a/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/upload/uploader/FileUploader.java +++ b/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/upload/uploader/FileUploader.java @@ -15,6 +15,9 @@ */ package org.dromara.visor.module.asset.handler.host.upload.uploader; +import cn.orionsec.kit.lang.utils.Strings; +import cn.orionsec.kit.lang.utils.collect.Maps; +import cn.orionsec.kit.lang.utils.io.Files1; import cn.orionsec.kit.lang.utils.io.Streams; import cn.orionsec.kit.net.host.SessionStore; import cn.orionsec.kit.net.host.sftp.SftpExecutor; @@ -22,14 +25,19 @@ import cn.orionsec.kit.spring.SpringHolder; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.dromara.visor.framework.common.constant.Const; +import org.dromara.visor.framework.common.constant.ErrorMessage; +import org.dromara.visor.framework.common.constant.ExtraFieldConst; import org.dromara.visor.framework.common.enums.EndpointDefine; import org.dromara.visor.framework.common.file.FileClient; +import org.dromara.visor.framework.common.utils.PathUtils; import org.dromara.visor.module.asset.dao.UploadTaskFileDAO; import org.dromara.visor.module.asset.define.config.AppSftpConfig; import org.dromara.visor.module.asset.entity.domain.UploadTaskFileDO; import org.dromara.visor.module.asset.entity.dto.TerminalConnectDTO; +import org.dromara.visor.module.asset.enums.HostOsTypeEnum; import org.dromara.visor.module.asset.enums.UploadTaskFileStatusEnum; import org.dromara.visor.module.asset.handler.host.jsch.SessionStores; +import org.dromara.visor.module.asset.handler.host.upload.model.FileUploadConfigDTO; import org.dromara.visor.module.asset.handler.host.upload.model.FileUploadFileItemDTO; import org.dromara.visor.module.asset.service.TerminalService; import org.dromara.visor.module.asset.utils.SftpUtils; @@ -38,6 +46,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.Date; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; /** @@ -54,10 +63,12 @@ public class FileUploader implements IFileUploader { private static final UploadTaskFileDAO uploadTaskFileDAO = SpringHolder.getBean(UploadTaskFileDAO.class); - private static final AppSftpConfig SFTP_CONFIG = SpringHolder.getBean(AppSftpConfig.class); + private static final AppSftpConfig sftpConfig = SpringHolder.getBean(AppSftpConfig.class); private static final FileClient localFileClient = SpringHolder.getBean("localFileClient"); + private TerminalConnectDTO connectInfo; + private SessionStore sessionStore; private SftpExecutor executor; @@ -66,6 +77,8 @@ public class FileUploader implements IFileUploader { private final Long hostId; + private final FileUploadConfigDTO config; + @Getter private final List files; @@ -77,10 +90,11 @@ public class FileUploader implements IFileUploader { private volatile boolean closed; - public FileUploader(Long taskId, Long hostId, List files) { - this.taskId = taskId; - this.hostId = hostId; - this.files = files; + public FileUploader(FileUploadConfigDTO config) { + this.taskId = config.getTaskId(); + this.hostId = config.getHostId(); + this.files = config.getFiles(); + this.config = config; } @Override @@ -91,6 +105,8 @@ public class FileUploader implements IFileUploader { if (!run) { return; } + // 计算实际上传路径 + this.calcRemotePath(); // 上传文件 for (FileUploadFileItemDTO file : files) { if (closed) { @@ -116,7 +132,7 @@ public class FileUploader implements IFileUploader { log.info("HostFileUploader.initSession start taskId: {}, hostId: {}", taskId, hostId); try { // 打开会话 - TerminalConnectDTO connectInfo = terminalService.getTerminalConnectInfo(hostId); + this.connectInfo = terminalService.getTerminalConnectInfo(hostId, config.getUserId()); this.sessionStore = SessionStores.openSessionStore(connectInfo); this.executor = sessionStore.getSftpExecutor(connectInfo.getFileNameCharset()); executor.connect(); @@ -125,12 +141,32 @@ public class FileUploader implements IFileUploader { } catch (Exception e) { log.error("HostFileUploader.initSession error taskId: {}, hostId: {}", taskId, hostId, e); // 修改状态 - uploadTaskFileDAO.updateStatusByTaskHostId(taskId, hostId, UploadTaskFileStatusEnum.FAILED.name()); + uploadTaskFileDAO.updateToFailedByTaskHostId(taskId, hostId, ErrorMessage.getErrorMessage(e, ErrorMessage.CONNECT_ERROR)); files.forEach(s -> s.setStatus(UploadTaskFileStatusEnum.FAILED.name())); return false; } } + /** + * 计算实际上传路径 + */ + public void calcRemotePath() { + // 计算上传目录 + String remotePath = config.getRemotePath(); + boolean containsEnv = remotePath.contains(Const.DOLLAR); + if (containsEnv) { + // 替换占位符 + String username = connectInfo.getUsername(); + String home = PathUtils.getHomePath(HostOsTypeEnum.isWindows(connectInfo.getOsType()), username); + // 替换环境变量路径 + Map env = Maps.newMap(4); + env.put(ExtraFieldConst.USERNAME, username); + env.put(ExtraFieldConst.HOME, home); + // 设置主机上传路径 + config.setRemotePath(Files1.getPath(Strings.format(remotePath, env))); + } + } + /** * 上传文件 * @@ -138,18 +174,18 @@ public class FileUploader implements IFileUploader { */ private void uploadFile(FileUploadFileItemDTO file) { log.info("HostFileUploader.uploadFile start taskId: {}, hostId: {}, id: {}", taskId, hostId, file.getId()); - // 修改状态 - this.updateStatus(file, UploadTaskFileStatusEnum.UPLOADING); + String path = Files1.getPath(config.getRemotePath(), file.getFilePath()); try { + // 修改为上传中 + this.updateToUploading(file, path); // 获取本地文件路径 String endpoint = EndpointDefine.UPLOAD_SWAP.format(taskId); String localPath = localFileClient.getReturnPath(endpoint + Const.SLASH + file.getFileId()); // 检查文件是否存在 - String remotePath = file.getRemotePath(); - SftpUtils.checkUploadFilePresent(SFTP_CONFIG, executor, remotePath); + SftpUtils.checkUploadFilePresent(sftpConfig, executor, path); // 打开输出流 this.inputStream = localFileClient.getContentInputStream(localPath); - this.outputStream = executor.openOutputStream(remotePath); + this.outputStream = executor.openOutputStream(path); // 传输 byte[] buffer = new byte[executor.getBufferSize()]; int read; @@ -159,26 +195,26 @@ public class FileUploader implements IFileUploader { } outputStream.flush(); // 修改状态 - this.updateStatus(file, UploadTaskFileStatusEnum.FINISHED); + this.updateToFinished(file); log.info("HostFileUploader.uploadFile finish taskId: {}, hostId: {}, id: {}", taskId, hostId, file.getId()); } catch (Exception e) { - log.info("HostFileUploader.uploadFile error taskId: {}, hostId: {}, id: {}, canceled: {}", taskId, hostId, file.getId(), canceled); + log.error("HostFileUploader.uploadFile error taskId: {}, hostId: {}, id: {}, canceled: {}", taskId, hostId, file.getId(), canceled, e); // 修改状态 if (canceled) { - this.updateStatus(file, UploadTaskFileStatusEnum.CANCELED); + this.updateToCanceled(file); } else { - this.updateStatus(file, UploadTaskFileStatusEnum.FAILED); + this.updateToFailed(file, ErrorMessage.getErrorMessage(e, ErrorMessage.FILE_UPLOAD_ERROR)); } } finally { // 释放文件 - this.resetFile(); + this.releaseFileResource(); } } /** - * 释放文件 + * 释放文件资源 */ - private void resetFile() { + private void releaseFileResource() { Streams.close(outputStream); Streams.close(inputStream); } @@ -199,34 +235,70 @@ public class FileUploader implements IFileUploader { return; } // 修改状态 - uploadTaskFileDAO.updateStatusByIdList(idList, UploadTaskFileStatusEnum.CANCELED.name()); + uploadTaskFileDAO.updateToCanceledByIdList(idList); } /** - * 更新状态 + * 修改为上传中 + * + * @param file file + * @param realFilePath realFilePath + */ + private void updateToUploading(FileUploadFileItemDTO file, String realFilePath) { + UploadTaskFileDO record = this.getUpdateRecord(file, UploadTaskFileStatusEnum.UPLOADING); + record.setRealFilePath(realFilePath); + record.setStartTime(new Date()); + uploadTaskFileDAO.updateById(record); + } + + /** + * 修改为完成 + * + * @param file file + */ + private void updateToFinished(FileUploadFileItemDTO file) { + UploadTaskFileDO record = this.getUpdateRecord(file, UploadTaskFileStatusEnum.FINISHED); + record.setEndTime(new Date()); + uploadTaskFileDAO.updateById(record); + } + + /** + * 修改为失败 + * + * @param file file + * @param errorMessage errorMessage + */ + private void updateToFailed(FileUploadFileItemDTO file, String errorMessage) { + UploadTaskFileDO record = this.getUpdateRecord(file, UploadTaskFileStatusEnum.FAILED); + record.setErrorMessage(errorMessage); + record.setEndTime(new Date()); + uploadTaskFileDAO.updateById(record); + } + + /** + * 修改为取消 + * + * @param file file + */ + private void updateToCanceled(FileUploadFileItemDTO file) { + UploadTaskFileDO record = this.getUpdateRecord(file, UploadTaskFileStatusEnum.CANCELED); + record.setEndTime(new Date()); + uploadTaskFileDAO.updateById(record); + } + + /** + * 获取修改记录 * * @param file file * @param status status + * @return record */ - private void updateStatus(FileUploadFileItemDTO file, UploadTaskFileStatusEnum status) { + private UploadTaskFileDO getUpdateRecord(FileUploadFileItemDTO file, UploadTaskFileStatusEnum status) { file.setStatus(status.name()); UploadTaskFileDO update = new UploadTaskFileDO(); update.setId(file.getId()); update.setStatus(status.name()); - if (UploadTaskFileStatusEnum.UPLOADING.equals(status)) { - // 上传中 - update.setStartTime(new Date()); - } else if (UploadTaskFileStatusEnum.FINISHED.equals(status)) { - // 已完成 - update.setEndTime(new Date()); - } else if (UploadTaskFileStatusEnum.FAILED.equals(status)) { - // 已失败 - update.setEndTime(new Date()); - } else if (UploadTaskFileStatusEnum.CANCELED.equals(status)) { - // 已失败 - update.setEndTime(new Date()); - } - uploadTaskFileDAO.updateById(update); + return update; } @Override @@ -247,9 +319,9 @@ public class FileUploader implements IFileUploader { return; } this.closed = true; - // 释放资源 - Streams.close(outputStream); - Streams.close(inputStream); + // 释放文件资源 + this.releaseFileResource(); + // 释放连接资源 Streams.close(executor); Streams.close(sessionStore); } diff --git a/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/impl/ExecCommandServiceImpl.java b/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/impl/ExecCommandServiceImpl.java index f8f080e4..aa2e8956 100644 --- a/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/impl/ExecCommandServiceImpl.java +++ b/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/impl/ExecCommandServiceImpl.java @@ -382,12 +382,12 @@ public class ExecCommandServiceImpl implements ExecCommandService { * @return scriptPath */ private String buildScriptPath(String username, String osType, Long logId, Long hostId) { - HostSshOsTypeEnum os = HostSshOsTypeEnum.of(osType); + HostOsTypeEnum os = HostOsTypeEnum.of(osType); String name = FileConst.EXEC + "/" + logId + "/" + hostId + os.getScriptSuffix(); - return PathUtils.buildAppPath(HostSshOsTypeEnum.WINDOWS.equals(os), username, FileConst.SCRIPT, name); + return PathUtils.buildAppPath(HostOsTypeEnum.WINDOWS.equals(os), username, FileConst.SCRIPT, name); } } diff --git a/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/impl/UploadTaskServiceImpl.java b/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/impl/UploadTaskServiceImpl.java index b58604d6..f11da061 100644 --- a/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/impl/UploadTaskServiceImpl.java +++ b/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/impl/UploadTaskServiceImpl.java @@ -16,7 +16,6 @@ package org.dromara.visor.module.asset.service.impl; import cn.orionsec.kit.lang.annotation.Keep; -import cn.orionsec.kit.lang.define.collect.MultiHashMap; import cn.orionsec.kit.lang.define.wrapper.DataGrid; import cn.orionsec.kit.lang.utils.Arrays1; import cn.orionsec.kit.lang.utils.Booleans; @@ -32,11 +31,9 @@ import lombok.extern.slf4j.Slf4j; import org.dromara.visor.framework.biz.operator.log.core.utils.OperatorLogs; import org.dromara.visor.framework.common.constant.Const; import org.dromara.visor.framework.common.constant.ErrorMessage; -import org.dromara.visor.framework.common.constant.ExtraFieldConst; import org.dromara.visor.framework.common.enums.EndpointDefine; import org.dromara.visor.framework.common.file.FileClient; import org.dromara.visor.framework.common.security.LoginUser; -import org.dromara.visor.framework.common.utils.PathUtils; import org.dromara.visor.framework.common.utils.SqlUtils; import org.dromara.visor.framework.common.utils.Valid; import org.dromara.visor.framework.mybatis.core.query.Conditions; @@ -53,15 +50,16 @@ import org.dromara.visor.module.asset.entity.domain.UploadTaskFileDO; import org.dromara.visor.module.asset.entity.dto.UploadTaskExtraDTO; import org.dromara.visor.module.asset.entity.request.upload.*; import org.dromara.visor.module.asset.entity.vo.*; -import org.dromara.visor.module.asset.enums.*; -import org.dromara.visor.module.asset.handler.host.config.model.HostSshConfigModel; +import org.dromara.visor.module.asset.enums.HostStatusEnum; +import org.dromara.visor.module.asset.enums.HostTypeEnum; +import org.dromara.visor.module.asset.enums.UploadTaskFileStatusEnum; +import org.dromara.visor.module.asset.enums.UploadTaskStatusEnum; import org.dromara.visor.module.asset.handler.host.upload.FileUploadTasks; import org.dromara.visor.module.asset.handler.host.upload.manager.FileUploadTaskManager; import org.dromara.visor.module.asset.handler.host.upload.model.FileUploadFileItemDTO; import org.dromara.visor.module.asset.handler.host.upload.task.IFileUploadTask; import org.dromara.visor.module.asset.handler.host.upload.uploader.IFileUploader; import org.dromara.visor.module.asset.service.AssetAuthorizedDataService; -import org.dromara.visor.module.asset.service.HostConfigService; import org.dromara.visor.module.asset.service.UploadTaskFileService; import org.dromara.visor.module.asset.service.UploadTaskService; import org.dromara.visor.module.infra.api.FileUploadApi; @@ -102,9 +100,6 @@ public class UploadTaskServiceImpl implements UploadTaskService { @Resource private AssetAuthorizedDataService assetAuthorizedDataService; - @Resource - private HostConfigService hostConfigService; - @Resource private FileUploadTaskManager fileUploadTaskManager; @@ -126,8 +121,6 @@ public class UploadTaskServiceImpl implements UploadTaskService { this.checkHostPermission(hostIdList); // 查询主机信息 List hosts = this.getUploadTaskHosts(hostIdList); - // 计算文件路径 - MultiHashMap realRemoteFilePathMap = this.setFileRealRemotePath(request, hosts); // 转换 UploadTaskDO record = UploadTaskConvert.MAPPER.to(request); record.setUserId(user.getId()); @@ -153,7 +146,6 @@ public class UploadTaskServiceImpl implements UploadTaskService { .hostId(hostId) .fileId(s.getFileId()) .filePath(s.getFilePath()) - .realFilePath(realRemoteFilePathMap.get(hostId, s.getFileId())) .fileSize(s.getFileSize()) .status(UploadTaskFileStatusEnum.WAITING.name()) .build()) @@ -204,8 +196,10 @@ public class UploadTaskServiceImpl implements UploadTaskService { // 查询任务 List tasks = uploadTaskDAO.of() .createWrapper() - .select(UploadTaskDO::getId, UploadTaskDO::getStatus, - UploadTaskDO::getStartTime, UploadTaskDO::getEndTime) + .select(UploadTaskDO::getId, + UploadTaskDO::getStatus, + UploadTaskDO::getStartTime, + UploadTaskDO::getEndTime) .in(UploadTaskDO::getId, idList) .then() .list(UploadTaskConvert.MAPPER::toStatus); @@ -215,9 +209,14 @@ public class UploadTaskServiceImpl implements UploadTaskService { // 查询任务文件 Map> taskFilesMap = uploadTaskFileDAO.of() .createWrapper() - .select(UploadTaskFileDO::getId, UploadTaskFileDO::getTaskId, UploadTaskFileDO::getHostId, - UploadTaskFileDO::getStatus, UploadTaskFileDO::getFileSize, - UploadTaskFileDO::getStartTime, UploadTaskFileDO::getEndTime) + .select(UploadTaskFileDO::getId, + UploadTaskFileDO::getTaskId, + UploadTaskFileDO::getHostId, + UploadTaskFileDO::getStatus, + UploadTaskFileDO::getFileSize, + UploadTaskFileDO::getErrorMessage, + UploadTaskFileDO::getStartTime, + UploadTaskFileDO::getEndTime) .in(UploadTaskFileDO::getTaskId, idList) .then() .stream() @@ -377,53 +376,6 @@ public class UploadTaskServiceImpl implements UploadTaskService { return hosts; } - /** - * 设置文件实际路径 - * - * @param request request - * @param hosts hosts - * @return realRemoteFilePathMap - */ - public MultiHashMap setFileRealRemotePath(UploadTaskCreateRequest request, - List hosts) { - MultiHashMap realRemoteFilePathMap = MultiHashMap.create(); - // 计算上传目录 - String remotePath = request.getRemotePath(); - List files = request.getFiles(); - boolean containsEnv = remotePath.contains(Const.DOLLAR); - if (containsEnv) { - // 获取主机配置信息 - Map hostConfigMap = hostConfigService.buildHostConfigMap(hosts, HostTypeEnum.SSH); - for (HostDO host : hosts) { - Long id = host.getId(); - // 替换占位符 - String username = Optional.ofNullable(id) - .map(hostConfigMap::get) - .map(HostSshConfigModel::getUsername) - .orElse(Const.EMPTY); - String home = PathUtils.getHomePath(HostSshOsTypeEnum.isWindows(host.getOsType()), username); - // 替换环境变量路径 - Map env = Maps.newMap(4); - env.put(ExtraFieldConst.USERNAME, username); - env.put(ExtraFieldConst.HOME, home); - // 设置主机上传路径 - String realRemotePath = Files1.getPath(Strings.format(remotePath, env)); - for (UploadTaskFileRequest file : files) { - realRemoteFilePathMap.put(id, file.getFileId(), Files1.getPath(realRemotePath, file.getFilePath())); - } - } - } else { - // 无占位符 - for (UploadTaskFileRequest file : files) { - String path = Files1.getPath(remotePath, file.getFilePath()); - for (HostDO host : hosts) { - realRemoteFilePathMap.put(host.getId(), file.getFileId(), path); - } - } - } - return realRemoteFilePathMap; - } - /** * 检查文件完整性 * @@ -448,7 +400,7 @@ public class UploadTaskServiceImpl implements UploadTaskService { return; } // 修改任务状态 - uploadTaskFileDAO.updateStatusByIdList(cancelIdList, UploadTaskFileStatusEnum.CANCELED.name()); + uploadTaskFileDAO.updateToCanceledByIdList(cancelIdList); } /** diff --git a/orion-visor-module-asset/orion-visor-module-asset-service/src/main/resources/mapper/UploadTaskFileMapper.xml b/orion-visor-module-asset/orion-visor-module-asset-service/src/main/resources/mapper/UploadTaskFileMapper.xml index 0ea35696..37ac2c1f 100644 --- a/orion-visor-module-asset/orion-visor-module-asset-service/src/main/resources/mapper/UploadTaskFileMapper.xml +++ b/orion-visor-module-asset/orion-visor-module-asset-service/src/main/resources/mapper/UploadTaskFileMapper.xml @@ -12,6 +12,7 @@ + @@ -23,7 +24,7 @@ - id, task_id, host_id, file_id, file_path, real_file_path, file_size, status, start_time, end_time, create_time, update_time, creator, updater, deleted + id, task_id, host_id, file_id, file_path, real_file_path, file_size, status, error_message, start_time, end_time, create_time, update_time, creator, updater, deleted diff --git a/orion-visor-ui/src/api/exec/upload-task.ts b/orion-visor-ui/src/api/exec/upload-task.ts index f94dc953..ea901071 100644 --- a/orion-visor-ui/src/api/exec/upload-task.ts +++ b/orion-visor-ui/src/api/exec/upload-task.ts @@ -89,6 +89,7 @@ export interface UploadTaskFile { filePath: string; fileSize: number; status: string; + errorMessage: string; startTime: number; endTime: number; current: number; diff --git a/orion-visor-ui/src/views/asset/host-identity/types/card.fields.ts b/orion-visor-ui/src/views/asset/host-identity/types/card.fields.ts index 3696c2e1..e5f1e3fc 100644 --- a/orion-visor-ui/src/views/asset/host-identity/types/card.fields.ts +++ b/orion-visor-ui/src/views/asset/host-identity/types/card.fields.ts @@ -10,15 +10,15 @@ const fieldConfig = { label: 'id', dataIndex: 'id', slotName: 'id', - }, { - label: '类型', - dataIndex: 'type', - slotName: 'type', }, { label: '用户名', dataIndex: 'username', slotName: 'username', ellipsis: true, + }, { + label: '类型', + dataIndex: 'type', + slotName: 'type', }, { label: '主机密钥', dataIndex: 'keyId', diff --git a/orion-visor-ui/src/views/asset/host-identity/types/table.columns.ts b/orion-visor-ui/src/views/asset/host-identity/types/table.columns.ts index 3bd375ec..8cf7852a 100644 --- a/orion-visor-ui/src/views/asset/host-identity/types/table.columns.ts +++ b/orion-visor-ui/src/views/asset/host-identity/types/table.columns.ts @@ -15,15 +15,15 @@ const columns = [ slotName: 'name', ellipsis: true, tooltip: true + }, { + title: '用户名', + dataIndex: 'username', + slotName: 'username', }, { title: '类型', dataIndex: 'type', slotName: 'type', width: 138, - }, { - title: '用户名', - dataIndex: 'username', - slotName: 'username', }, { title: '主机密钥', dataIndex: 'keyId', diff --git a/orion-visor-ui/src/views/exec/batch-upload/components/batch-upload-progress.vue b/orion-visor-ui/src/views/exec/batch-upload/components/batch-upload-progress.vue index d302688b..4a863086 100644 --- a/orion-visor-ui/src/views/exec/batch-upload/components/batch-upload-progress.vue +++ b/orion-visor-ui/src/views/exec/batch-upload/components/batch-upload-progress.vue @@ -31,7 +31,11 @@ s.id === file.id); if (fileStatus) { - file.status = fileStatus.status; file.current = fileStatus.current; + file.status = fileStatus.status; + file.errorMessage = fileStatus.errorMessage; } } } diff --git a/orion-visor-ui/src/views/exec/exec-job/components/exec-job-detail-drawer.vue b/orion-visor-ui/src/views/exec/exec-job/components/exec-job-detail-drawer.vue index ec30e336..88029055 100644 --- a/orion-visor-ui/src/views/exec/exec-job/components/exec-job-detail-drawer.vue +++ b/orion-visor-ui/src/views/exec/exec-job/components/exec-job-detail-drawer.vue @@ -46,9 +46,9 @@ {{ record.scriptExec === EnabledStatus.ENABLED ? '是' : '否' }} - - - {{ dateFormat(new Date(record.createTime)) }} + + + {{ record.execUsername }} diff --git a/orion-visor-ui/src/views/exec/exec-job/components/exec-user-update-modal.vue b/orion-visor-ui/src/views/exec/exec-job/components/exec-user-update-modal.vue index 84cbe3df..bc5893d3 100644 --- a/orion-visor-ui/src/views/exec/exec-job/components/exec-user-update-modal.vue +++ b/orion-visor-ui/src/views/exec/exec-job/components/exec-user-update-modal.vue @@ -12,6 +12,8 @@ :on-before-ok="handlerOk" @close="handleClose"> + + 修改后将使用此用户的权限以及主机配置执行此任务