🔨 上传任务使用用户连接配置.
This commit is contained in:
@@ -119,6 +119,8 @@ public interface ErrorMessage {
|
||||
|
||||
String AUTH_ERROR = "认证失败";
|
||||
|
||||
String FILE_UPLOAD_ERROR = "文件上传失败";
|
||||
|
||||
String SCRIPT_UPLOAD_ERROR = "脚本上传失败";
|
||||
|
||||
String EXEC_ERROR = "执行失败";
|
||||
|
||||
@@ -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> 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> T valid(T obj, Class<?>... groups) {
|
||||
notNull(obj, ErrorMessage.PARAM_MISSING);
|
||||
// 验证对象
|
||||
Set<ConstraintViolation<T>> set = VALIDATOR.validate(obj, groups);
|
||||
Set<ConstraintViolation<T>> set = validator.validate(obj, groups);
|
||||
if (!set.isEmpty()) {
|
||||
throw new ConstraintViolationException(set);
|
||||
}
|
||||
|
||||
@@ -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<UploadTaskFileDO> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 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<UploadTaskFileDO> 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<Long> idList, String status) {
|
||||
default int updateToCanceledByIdList(List<Long> 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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -67,6 +67,9 @@ public class UploadTaskFileVO implements Serializable {
|
||||
@Schema(description = "状态")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "错误信息")
|
||||
private String errorMessage;
|
||||
|
||||
@Schema(description = "开始时间")
|
||||
private Date startTime;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
// 响应结果
|
||||
|
||||
@@ -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<FileUploadFileItemDTO> files;
|
||||
|
||||
}
|
||||
@@ -44,9 +44,9 @@ public class FileUploadFileItemDTO {
|
||||
private String fileId;
|
||||
|
||||
/**
|
||||
* 远程路径
|
||||
* 文件路径
|
||||
*/
|
||||
private String remotePath;
|
||||
private String filePath;
|
||||
|
||||
/**
|
||||
* 当前大小
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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<FileUploadFileItemDTO> files;
|
||||
|
||||
@@ -77,10 +90,11 @@ public class FileUploader implements IFileUploader {
|
||||
|
||||
private volatile boolean closed;
|
||||
|
||||
public FileUploader(Long taskId, Long hostId, List<FileUploadFileItemDTO> 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<String, String> 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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<HostDO> hosts = this.getUploadTaskHosts(hostIdList);
|
||||
// 计算文件路径
|
||||
MultiHashMap<Long, String, String> 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<UploadTaskStatusVO> 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<Long, List<UploadTaskFileVO>> 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<Long, String, String> setFileRealRemotePath(UploadTaskCreateRequest request,
|
||||
List<HostDO> hosts) {
|
||||
MultiHashMap<Long, String, String> realRemoteFilePathMap = MultiHashMap.create();
|
||||
// 计算上传目录
|
||||
String remotePath = request.getRemotePath();
|
||||
List<UploadTaskFileRequest> files = request.getFiles();
|
||||
boolean containsEnv = remotePath.contains(Const.DOLLAR);
|
||||
if (containsEnv) {
|
||||
// 获取主机配置信息
|
||||
Map<Long, HostSshConfigModel> 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<String, String> 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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<result column="real_file_path" property="realFilePath"/>
|
||||
<result column="file_size" property="fileSize"/>
|
||||
<result column="status" property="status"/>
|
||||
<result column="error_message" property="errorMessage"/>
|
||||
<result column="start_time" property="startTime"/>
|
||||
<result column="end_time" property="endTime"/>
|
||||
<result column="create_time" property="createTime"/>
|
||||
@@ -23,7 +24,7 @@
|
||||
|
||||
<!-- 通用查询结果列 -->
|
||||
<sql id="Base_Column_List">
|
||||
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
|
||||
</sql>
|
||||
|
||||
</mapper>
|
||||
|
||||
@@ -89,6 +89,7 @@ export interface UploadTaskFile {
|
||||
filePath: string;
|
||||
fileSize: number;
|
||||
status: string;
|
||||
errorMessage: string;
|
||||
startTime: number;
|
||||
endTime: number;
|
||||
current: number;
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -31,7 +31,11 @@
|
||||
</div>
|
||||
<!-- 进度 -->
|
||||
<a-tooltip position="left"
|
||||
:content="file.fileSize ? ((file.current || 0) / file.fileSize * 100).toFixed(2) + '%' : '0%'"
|
||||
:content="(
|
||||
file.status === UploadTaskFileStatus.FAILED
|
||||
? (file.errorMessage || '')
|
||||
: (file.fileSize ? ((file.current || 0) / file.fileSize * 100).toFixed(2) + '%' : '0%')
|
||||
)"
|
||||
mini>
|
||||
<a-progress type="circle"
|
||||
size="mini"
|
||||
|
||||
@@ -240,8 +240,9 @@
|
||||
for (let file of host.files) {
|
||||
const fileStatus = taskStatusData.files.find(s => s.id === file.id);
|
||||
if (fileStatus) {
|
||||
file.status = fileStatus.status;
|
||||
file.current = fileStatus.current;
|
||||
file.status = fileStatus.status;
|
||||
file.errorMessage = fileStatus.errorMessage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,9 +46,9 @@
|
||||
<a-descriptions-item label="脚本执行">
|
||||
{{ record.scriptExec === EnabledStatus.ENABLED ? '是' : '否' }}
|
||||
</a-descriptions-item>
|
||||
<!-- 创建时间 -->
|
||||
<a-descriptions-item label="创建时间">
|
||||
{{ dateFormat(new Date(record.createTime)) }}
|
||||
<!-- 执行用户 -->
|
||||
<a-descriptions-item label="执行用户">
|
||||
{{ record.execUsername }}
|
||||
</a-descriptions-item>
|
||||
<!-- 修改时间 -->
|
||||
<a-descriptions-item label="修改时间">
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
:on-before-ok="handlerOk"
|
||||
@close="handleClose">
|
||||
<a-spin class="full" :loading="loading">
|
||||
<!-- 提示 -->
|
||||
<a-alert class="mb16">修改后将使用此用户的权限以及主机配置执行此任务</a-alert>
|
||||
<a-form :model="formModel"
|
||||
label-align="right"
|
||||
:auto-label-width="true">
|
||||
|
||||
Reference in New Issue
Block a user