🔨 上传任务使用用户连接配置.

This commit is contained in:
lijiahangmax
2024-12-14 12:41:06 +08:00
parent 885446dc58
commit 985091f42b
23 changed files with 266 additions and 147 deletions

View File

@@ -119,6 +119,8 @@ public interface ErrorMessage {
String AUTH_ERROR = "认证失败"; String AUTH_ERROR = "认证失败";
String FILE_UPLOAD_ERROR = "文件上传失败";
String SCRIPT_UPLOAD_ERROR = "脚本上传失败"; String SCRIPT_UPLOAD_ERROR = "脚本上传失败";
String EXEC_ERROR = "执行失败"; String EXEC_ERROR = "执行失败";

View File

@@ -37,7 +37,7 @@ import java.util.function.Function;
*/ */
public class Valid extends cn.orionsec.kit.lang.utils.Valid { 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) { public static <T> T notNull(T object) {
return notNull(object, ErrorMessage.PARAM_MISSING); 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) { public static <T> T valid(T obj, Class<?>... groups) {
notNull(obj, ErrorMessage.PARAM_MISSING); notNull(obj, ErrorMessage.PARAM_MISSING);
// 验证对象 // 验证对象
Set<ConstraintViolation<T>> set = VALIDATOR.validate(obj, groups); Set<ConstraintViolation<T>> set = validator.validate(obj, groups);
if (!set.isEmpty()) { if (!set.isEmpty()) {
throw new ConstraintViolationException(set); throw new ConstraintViolationException(set);
} }

View File

@@ -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.mapper.IMapper;
import org.dromara.visor.framework.mybatis.core.query.Conditions; import org.dromara.visor.framework.mybatis.core.query.Conditions;
import org.dromara.visor.module.asset.entity.domain.UploadTaskFileDO; 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; import java.util.List;
/** /**
@@ -58,35 +60,39 @@ public interface UploadTaskFileDAO extends IMapper<UploadTaskFileDO> {
} }
/** /**
* 通过 taskId hostId 更新状态 * 通过 taskId hostId 更新为失败
* *
* @param taskId taskId * @param taskId taskId
* @param hostId hostId * @param hostId hostId
* @param status status * @param errorMessage errorMessage
* @return effect * @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() LambdaQueryWrapper<UploadTaskFileDO> wrapper = this.wrapper()
.eq(UploadTaskFileDO::getTaskId, taskId) .eq(UploadTaskFileDO::getTaskId, taskId)
.eq(UploadTaskFileDO::getHostId, hostId); .eq(UploadTaskFileDO::getHostId, hostId);
// 修改值 // 修改值
UploadTaskFileDO update = new UploadTaskFileDO(); 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); return this.update(update, wrapper);
} }
/** /**
* 通过 id 更新状态 * 通过 id 更新为取消
* *
* @param idList idList * @param idList idList
* @param status status
* @return effect * @return effect
*/ */
default int updateStatusByIdList(List<Long> idList, String status) { default int updateToCanceledByIdList(List<Long> idList) {
UploadTaskFileDO update = new UploadTaskFileDO(); 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)); return this.update(update, Conditions.in(UploadTaskFileDO::getId, idList));
} }

View File

@@ -75,6 +75,10 @@ public class UploadTaskFileDO extends BaseDO {
@TableField("status") @TableField("status")
private String status; private String status;
@Schema(description = "错误信息")
@TableField("error_message")
private String errorMessage;
@Schema(description = "开始时间") @Schema(description = "开始时间")
@TableField("start_time") @TableField("start_time")
private Date startTime; private Date startTime;

View File

@@ -67,6 +67,9 @@ public class UploadTaskFileVO implements Serializable {
@Schema(description = "状态") @Schema(description = "状态")
private String status; private String status;
@Schema(description = "错误信息")
private String errorMessage;
@Schema(description = "开始时间") @Schema(description = "开始时间")
private Date startTime; private Date startTime;

View File

@@ -27,7 +27,7 @@ import lombok.Getter;
*/ */
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public enum HostSshOsTypeEnum { public enum HostOsTypeEnum {
/** /**
* linux * linux
@@ -43,11 +43,11 @@ public enum HostSshOsTypeEnum {
private final String scriptSuffix; private final String scriptSuffix;
public static HostSshOsTypeEnum of(String type) { public static HostOsTypeEnum of(String type) {
if (type == null) { if (type == null) {
return LINUX; return LINUX;
} }
for (HostSshOsTypeEnum value : values()) { for (HostOsTypeEnum value : values()) {
if (value.name().equals(type)) { if (value.name().equals(type)) {
return value; return value;
} }

View File

@@ -38,7 +38,7 @@ import org.springframework.web.socket.WebSocketSession;
@Slf4j @Slf4j
public class ExecLogTracker implements IExecLogTracker { 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; private final WebSocketSession session;
@@ -69,9 +69,9 @@ public class ExecLogTracker implements IExecLogTracker {
try { try {
this.tracker = new DelayTrackerListener(absolutePath, this); this.tracker = new DelayTrackerListener(absolutePath, this);
tracker.charset(config.getCharset()); tracker.charset(config.getCharset());
tracker.delayMillis(TRACKER_CONFIG.getDelay()); tracker.delayMillis(trackerConfig.getDelay());
tracker.offset(FileOffsetMode.LINE, TRACKER_CONFIG.getOffset()); tracker.offset(FileOffsetMode.LINE, trackerConfig.getOffset());
tracker.notFoundMode(FileNotFoundMode.WAIT_COUNT, TRACKER_CONFIG.getWaitTimes()); tracker.notFoundMode(FileNotFoundMode.WAIT_COUNT, trackerConfig.getWaitTimes());
// 开始监听文件 // 开始监听文件
tracker.run(); tracker.run();
} catch (Exception e) { } catch (Exception e) {

View File

@@ -47,7 +47,7 @@ import java.util.Map;
@Slf4j @Slf4j
public abstract class TransferSession implements ITransferSession { 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; protected final TerminalConnectDTO connectInfo;

View File

@@ -55,7 +55,7 @@ public class UploadSession extends TransferSession {
// 检查连接 // 检查连接
this.init(); this.init();
// 检查文件是否存在 // 检查文件是否存在
SftpUtils.checkUploadFilePresent(SFTP_CONFIG, executor, path); SftpUtils.checkUploadFilePresent(sftpConfig, executor, path);
// 打开输出流 // 打开输出流
this.outputStream = executor.openOutputStream(path); this.outputStream = executor.openOutputStream(path);
// 响应结果 // 响应结果

View File

@@ -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;
}

View File

@@ -44,9 +44,9 @@ public class FileUploadFileItemDTO {
private String fileId; private String fileId;
/** /**
* 远程路径 * 文件路径
*/ */
private String remotePath; private String filePath;
/** /**
* 当前大小 * 当前大小

View File

@@ -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.UploadTaskFileStatusEnum;
import org.dromara.visor.module.asset.enums.UploadTaskStatusEnum; 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.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.model.FileUploadFileItemDTO;
import org.dromara.visor.module.asset.handler.host.upload.uploader.FileUploader; import org.dromara.visor.module.asset.handler.host.upload.uploader.FileUploader;
import org.dromara.visor.module.asset.handler.host.upload.uploader.IFileUploader; import org.dromara.visor.module.asset.handler.host.upload.uploader.IFileUploader;
@@ -102,6 +103,8 @@ public class FileUploadTask implements IFileUploadTask {
} catch (Exception e) { } catch (Exception e) {
log.error("FileUploadTask.run error id: {}", id, e); log.error("FileUploadTask.run error id: {}", id, e);
} finally { } finally {
// 移除任务
fileUploadTaskManager.removeTask(id);
// 修改状态 // 修改状态
if (canceled) { if (canceled) {
this.updateStatus(UploadTaskStatusEnum.CANCELED); this.updateStatus(UploadTaskStatusEnum.CANCELED);
@@ -110,8 +113,6 @@ public class FileUploadTask implements IFileUploadTask {
} }
// 检查是否发送消息 // 检查是否发送消息
this.checkSendMessage(); this.checkSendMessage();
// 移除任务
fileUploadTaskManager.removeTask(id);
// 释放资源 // 释放资源
this.close(); this.close();
} }
@@ -156,7 +157,7 @@ public class FileUploadTask implements IFileUploadTask {
.map(s -> FileUploadFileItemDTO.builder() .map(s -> FileUploadFileItemDTO.builder()
.id(s.getId()) .id(s.getId())
.fileId(s.getFileId()) .fileId(s.getFileId())
.remotePath(s.getRealFilePath()) .filePath(s.getFilePath())
.status(UploadTaskFileStatusEnum.WAITING.name()) .status(UploadTaskFileStatusEnum.WAITING.name())
.current(0L) .current(0L)
.build()) .build())
@@ -165,7 +166,14 @@ public class FileUploadTask implements IFileUploadTask {
return; 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));
}); });
} }

View File

@@ -15,6 +15,9 @@
*/ */
package org.dromara.visor.module.asset.handler.host.upload.uploader; 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.lang.utils.io.Streams;
import cn.orionsec.kit.net.host.SessionStore; import cn.orionsec.kit.net.host.SessionStore;
import cn.orionsec.kit.net.host.sftp.SftpExecutor; import cn.orionsec.kit.net.host.sftp.SftpExecutor;
@@ -22,14 +25,19 @@ import cn.orionsec.kit.spring.SpringHolder;
import lombok.Getter; import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.visor.framework.common.constant.Const; 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.enums.EndpointDefine;
import org.dromara.visor.framework.common.file.FileClient; 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.dao.UploadTaskFileDAO;
import org.dromara.visor.module.asset.define.config.AppSftpConfig; 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.domain.UploadTaskFileDO;
import org.dromara.visor.module.asset.entity.dto.TerminalConnectDTO; 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.enums.UploadTaskFileStatusEnum;
import org.dromara.visor.module.asset.handler.host.jsch.SessionStores; 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.handler.host.upload.model.FileUploadFileItemDTO;
import org.dromara.visor.module.asset.service.TerminalService; import org.dromara.visor.module.asset.service.TerminalService;
import org.dromara.visor.module.asset.utils.SftpUtils; import org.dromara.visor.module.asset.utils.SftpUtils;
@@ -38,6 +46,7 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; 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 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 static final FileClient localFileClient = SpringHolder.getBean("localFileClient");
private TerminalConnectDTO connectInfo;
private SessionStore sessionStore; private SessionStore sessionStore;
private SftpExecutor executor; private SftpExecutor executor;
@@ -66,6 +77,8 @@ public class FileUploader implements IFileUploader {
private final Long hostId; private final Long hostId;
private final FileUploadConfigDTO config;
@Getter @Getter
private final List<FileUploadFileItemDTO> files; private final List<FileUploadFileItemDTO> files;
@@ -77,10 +90,11 @@ public class FileUploader implements IFileUploader {
private volatile boolean closed; private volatile boolean closed;
public FileUploader(Long taskId, Long hostId, List<FileUploadFileItemDTO> files) { public FileUploader(FileUploadConfigDTO config) {
this.taskId = taskId; this.taskId = config.getTaskId();
this.hostId = hostId; this.hostId = config.getHostId();
this.files = files; this.files = config.getFiles();
this.config = config;
} }
@Override @Override
@@ -91,6 +105,8 @@ public class FileUploader implements IFileUploader {
if (!run) { if (!run) {
return; return;
} }
// 计算实际上传路径
this.calcRemotePath();
// 上传文件 // 上传文件
for (FileUploadFileItemDTO file : files) { for (FileUploadFileItemDTO file : files) {
if (closed) { if (closed) {
@@ -116,7 +132,7 @@ public class FileUploader implements IFileUploader {
log.info("HostFileUploader.initSession start taskId: {}, hostId: {}", taskId, hostId); log.info("HostFileUploader.initSession start taskId: {}, hostId: {}", taskId, hostId);
try { try {
// 打开会话 // 打开会话
TerminalConnectDTO connectInfo = terminalService.getTerminalConnectInfo(hostId); this.connectInfo = terminalService.getTerminalConnectInfo(hostId, config.getUserId());
this.sessionStore = SessionStores.openSessionStore(connectInfo); this.sessionStore = SessionStores.openSessionStore(connectInfo);
this.executor = sessionStore.getSftpExecutor(connectInfo.getFileNameCharset()); this.executor = sessionStore.getSftpExecutor(connectInfo.getFileNameCharset());
executor.connect(); executor.connect();
@@ -125,12 +141,32 @@ public class FileUploader implements IFileUploader {
} catch (Exception e) { } catch (Exception e) {
log.error("HostFileUploader.initSession error taskId: {}, hostId: {}", taskId, hostId, 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())); files.forEach(s -> s.setStatus(UploadTaskFileStatusEnum.FAILED.name()));
return false; 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) { private void uploadFile(FileUploadFileItemDTO file) {
log.info("HostFileUploader.uploadFile start taskId: {}, hostId: {}, id: {}", taskId, hostId, file.getId()); log.info("HostFileUploader.uploadFile start taskId: {}, hostId: {}, id: {}", taskId, hostId, file.getId());
// 修改状态 String path = Files1.getPath(config.getRemotePath(), file.getFilePath());
this.updateStatus(file, UploadTaskFileStatusEnum.UPLOADING);
try { try {
// 修改为上传中
this.updateToUploading(file, path);
// 获取本地文件路径 // 获取本地文件路径
String endpoint = EndpointDefine.UPLOAD_SWAP.format(taskId); String endpoint = EndpointDefine.UPLOAD_SWAP.format(taskId);
String localPath = localFileClient.getReturnPath(endpoint + Const.SLASH + file.getFileId()); String localPath = localFileClient.getReturnPath(endpoint + Const.SLASH + file.getFileId());
// 检查文件是否存在 // 检查文件是否存在
String remotePath = file.getRemotePath(); SftpUtils.checkUploadFilePresent(sftpConfig, executor, path);
SftpUtils.checkUploadFilePresent(SFTP_CONFIG, executor, remotePath);
// 打开输出流 // 打开输出流
this.inputStream = localFileClient.getContentInputStream(localPath); this.inputStream = localFileClient.getContentInputStream(localPath);
this.outputStream = executor.openOutputStream(remotePath); this.outputStream = executor.openOutputStream(path);
// 传输 // 传输
byte[] buffer = new byte[executor.getBufferSize()]; byte[] buffer = new byte[executor.getBufferSize()];
int read; int read;
@@ -159,26 +195,26 @@ public class FileUploader implements IFileUploader {
} }
outputStream.flush(); outputStream.flush();
// 修改状态 // 修改状态
this.updateStatus(file, UploadTaskFileStatusEnum.FINISHED); this.updateToFinished(file);
log.info("HostFileUploader.uploadFile finish taskId: {}, hostId: {}, id: {}", taskId, hostId, file.getId()); log.info("HostFileUploader.uploadFile finish taskId: {}, hostId: {}, id: {}", taskId, hostId, file.getId());
} catch (Exception e) { } 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) { if (canceled) {
this.updateStatus(file, UploadTaskFileStatusEnum.CANCELED); this.updateToCanceled(file);
} else { } else {
this.updateStatus(file, UploadTaskFileStatusEnum.FAILED); this.updateToFailed(file, ErrorMessage.getErrorMessage(e, ErrorMessage.FILE_UPLOAD_ERROR));
} }
} finally { } finally {
// 释放文件 // 释放文件
this.resetFile(); this.releaseFileResource();
} }
} }
/** /**
* 释放文件 * 释放文件资源
*/ */
private void resetFile() { private void releaseFileResource() {
Streams.close(outputStream); Streams.close(outputStream);
Streams.close(inputStream); Streams.close(inputStream);
} }
@@ -199,34 +235,70 @@ public class FileUploader implements IFileUploader {
return; 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 file file
* @param status status * @param status status
* @return record
*/ */
private void updateStatus(FileUploadFileItemDTO file, UploadTaskFileStatusEnum status) { private UploadTaskFileDO getUpdateRecord(FileUploadFileItemDTO file, UploadTaskFileStatusEnum status) {
file.setStatus(status.name()); file.setStatus(status.name());
UploadTaskFileDO update = new UploadTaskFileDO(); UploadTaskFileDO update = new UploadTaskFileDO();
update.setId(file.getId()); update.setId(file.getId());
update.setStatus(status.name()); update.setStatus(status.name());
if (UploadTaskFileStatusEnum.UPLOADING.equals(status)) { return update;
// 上传中
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);
} }
@Override @Override
@@ -247,9 +319,9 @@ public class FileUploader implements IFileUploader {
return; return;
} }
this.closed = true; this.closed = true;
// 释放资源 // 释放文件资源
Streams.close(outputStream); this.releaseFileResource();
Streams.close(inputStream); // 释放连接资源
Streams.close(executor); Streams.close(executor);
Streams.close(sessionStore); Streams.close(sessionStore);
} }

View File

@@ -382,12 +382,12 @@ public class ExecCommandServiceImpl implements ExecCommandService {
* @return scriptPath * @return scriptPath
*/ */
private String buildScriptPath(String username, String osType, Long logId, Long hostId) { 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 String name = FileConst.EXEC
+ "/" + logId + "/" + logId
+ "/" + hostId + "/" + hostId
+ os.getScriptSuffix(); + os.getScriptSuffix();
return PathUtils.buildAppPath(HostSshOsTypeEnum.WINDOWS.equals(os), username, FileConst.SCRIPT, name); return PathUtils.buildAppPath(HostOsTypeEnum.WINDOWS.equals(os), username, FileConst.SCRIPT, name);
} }
} }

View File

@@ -16,7 +16,6 @@
package org.dromara.visor.module.asset.service.impl; package org.dromara.visor.module.asset.service.impl;
import cn.orionsec.kit.lang.annotation.Keep; 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.define.wrapper.DataGrid;
import cn.orionsec.kit.lang.utils.Arrays1; import cn.orionsec.kit.lang.utils.Arrays1;
import cn.orionsec.kit.lang.utils.Booleans; 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.biz.operator.log.core.utils.OperatorLogs;
import org.dromara.visor.framework.common.constant.Const; import org.dromara.visor.framework.common.constant.Const;
import org.dromara.visor.framework.common.constant.ErrorMessage; 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.enums.EndpointDefine;
import org.dromara.visor.framework.common.file.FileClient; import org.dromara.visor.framework.common.file.FileClient;
import org.dromara.visor.framework.common.security.LoginUser; 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.SqlUtils;
import org.dromara.visor.framework.common.utils.Valid; import org.dromara.visor.framework.common.utils.Valid;
import org.dromara.visor.framework.mybatis.core.query.Conditions; 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.dto.UploadTaskExtraDTO;
import org.dromara.visor.module.asset.entity.request.upload.*; import org.dromara.visor.module.asset.entity.request.upload.*;
import org.dromara.visor.module.asset.entity.vo.*; import org.dromara.visor.module.asset.entity.vo.*;
import org.dromara.visor.module.asset.enums.*; import org.dromara.visor.module.asset.enums.HostStatusEnum;
import org.dromara.visor.module.asset.handler.host.config.model.HostSshConfigModel; 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.FileUploadTasks;
import org.dromara.visor.module.asset.handler.host.upload.manager.FileUploadTaskManager; 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.model.FileUploadFileItemDTO;
import org.dromara.visor.module.asset.handler.host.upload.task.IFileUploadTask; 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.handler.host.upload.uploader.IFileUploader;
import org.dromara.visor.module.asset.service.AssetAuthorizedDataService; 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.UploadTaskFileService;
import org.dromara.visor.module.asset.service.UploadTaskService; import org.dromara.visor.module.asset.service.UploadTaskService;
import org.dromara.visor.module.infra.api.FileUploadApi; import org.dromara.visor.module.infra.api.FileUploadApi;
@@ -102,9 +100,6 @@ public class UploadTaskServiceImpl implements UploadTaskService {
@Resource @Resource
private AssetAuthorizedDataService assetAuthorizedDataService; private AssetAuthorizedDataService assetAuthorizedDataService;
@Resource
private HostConfigService hostConfigService;
@Resource @Resource
private FileUploadTaskManager fileUploadTaskManager; private FileUploadTaskManager fileUploadTaskManager;
@@ -126,8 +121,6 @@ public class UploadTaskServiceImpl implements UploadTaskService {
this.checkHostPermission(hostIdList); this.checkHostPermission(hostIdList);
// 查询主机信息 // 查询主机信息
List<HostDO> hosts = this.getUploadTaskHosts(hostIdList); List<HostDO> hosts = this.getUploadTaskHosts(hostIdList);
// 计算文件路径
MultiHashMap<Long, String, String> realRemoteFilePathMap = this.setFileRealRemotePath(request, hosts);
// 转换 // 转换
UploadTaskDO record = UploadTaskConvert.MAPPER.to(request); UploadTaskDO record = UploadTaskConvert.MAPPER.to(request);
record.setUserId(user.getId()); record.setUserId(user.getId());
@@ -153,7 +146,6 @@ public class UploadTaskServiceImpl implements UploadTaskService {
.hostId(hostId) .hostId(hostId)
.fileId(s.getFileId()) .fileId(s.getFileId())
.filePath(s.getFilePath()) .filePath(s.getFilePath())
.realFilePath(realRemoteFilePathMap.get(hostId, s.getFileId()))
.fileSize(s.getFileSize()) .fileSize(s.getFileSize())
.status(UploadTaskFileStatusEnum.WAITING.name()) .status(UploadTaskFileStatusEnum.WAITING.name())
.build()) .build())
@@ -204,8 +196,10 @@ public class UploadTaskServiceImpl implements UploadTaskService {
// 查询任务 // 查询任务
List<UploadTaskStatusVO> tasks = uploadTaskDAO.of() List<UploadTaskStatusVO> tasks = uploadTaskDAO.of()
.createWrapper() .createWrapper()
.select(UploadTaskDO::getId, UploadTaskDO::getStatus, .select(UploadTaskDO::getId,
UploadTaskDO::getStartTime, UploadTaskDO::getEndTime) UploadTaskDO::getStatus,
UploadTaskDO::getStartTime,
UploadTaskDO::getEndTime)
.in(UploadTaskDO::getId, idList) .in(UploadTaskDO::getId, idList)
.then() .then()
.list(UploadTaskConvert.MAPPER::toStatus); .list(UploadTaskConvert.MAPPER::toStatus);
@@ -215,9 +209,14 @@ public class UploadTaskServiceImpl implements UploadTaskService {
// 查询任务文件 // 查询任务文件
Map<Long, List<UploadTaskFileVO>> taskFilesMap = uploadTaskFileDAO.of() Map<Long, List<UploadTaskFileVO>> taskFilesMap = uploadTaskFileDAO.of()
.createWrapper() .createWrapper()
.select(UploadTaskFileDO::getId, UploadTaskFileDO::getTaskId, UploadTaskFileDO::getHostId, .select(UploadTaskFileDO::getId,
UploadTaskFileDO::getStatus, UploadTaskFileDO::getFileSize, UploadTaskFileDO::getTaskId,
UploadTaskFileDO::getStartTime, UploadTaskFileDO::getEndTime) UploadTaskFileDO::getHostId,
UploadTaskFileDO::getStatus,
UploadTaskFileDO::getFileSize,
UploadTaskFileDO::getErrorMessage,
UploadTaskFileDO::getStartTime,
UploadTaskFileDO::getEndTime)
.in(UploadTaskFileDO::getTaskId, idList) .in(UploadTaskFileDO::getTaskId, idList)
.then() .then()
.stream() .stream()
@@ -377,53 +376,6 @@ public class UploadTaskServiceImpl implements UploadTaskService {
return hosts; 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; return;
} }
// 修改任务状态 // 修改任务状态
uploadTaskFileDAO.updateStatusByIdList(cancelIdList, UploadTaskFileStatusEnum.CANCELED.name()); uploadTaskFileDAO.updateToCanceledByIdList(cancelIdList);
} }
/** /**

View File

@@ -12,6 +12,7 @@
<result column="real_file_path" property="realFilePath"/> <result column="real_file_path" property="realFilePath"/>
<result column="file_size" property="fileSize"/> <result column="file_size" property="fileSize"/>
<result column="status" property="status"/> <result column="status" property="status"/>
<result column="error_message" property="errorMessage"/>
<result column="start_time" property="startTime"/> <result column="start_time" property="startTime"/>
<result column="end_time" property="endTime"/> <result column="end_time" property="endTime"/>
<result column="create_time" property="createTime"/> <result column="create_time" property="createTime"/>
@@ -23,7 +24,7 @@
<!-- 通用查询结果列 --> <!-- 通用查询结果列 -->
<sql id="Base_Column_List"> <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> </sql>
</mapper> </mapper>

View File

@@ -89,6 +89,7 @@ export interface UploadTaskFile {
filePath: string; filePath: string;
fileSize: number; fileSize: number;
status: string; status: string;
errorMessage: string;
startTime: number; startTime: number;
endTime: number; endTime: number;
current: number; current: number;

View File

@@ -10,15 +10,15 @@ const fieldConfig = {
label: 'id', label: 'id',
dataIndex: 'id', dataIndex: 'id',
slotName: 'id', slotName: 'id',
}, {
label: '类型',
dataIndex: 'type',
slotName: 'type',
}, { }, {
label: '用户名', label: '用户名',
dataIndex: 'username', dataIndex: 'username',
slotName: 'username', slotName: 'username',
ellipsis: true, ellipsis: true,
}, {
label: '类型',
dataIndex: 'type',
slotName: 'type',
}, { }, {
label: '主机密钥', label: '主机密钥',
dataIndex: 'keyId', dataIndex: 'keyId',

View File

@@ -15,15 +15,15 @@ const columns = [
slotName: 'name', slotName: 'name',
ellipsis: true, ellipsis: true,
tooltip: true tooltip: true
}, {
title: '用户名',
dataIndex: 'username',
slotName: 'username',
}, { }, {
title: '类型', title: '类型',
dataIndex: 'type', dataIndex: 'type',
slotName: 'type', slotName: 'type',
width: 138, width: 138,
}, {
title: '用户名',
dataIndex: 'username',
slotName: 'username',
}, { }, {
title: '主机密钥', title: '主机密钥',
dataIndex: 'keyId', dataIndex: 'keyId',

View File

@@ -31,7 +31,11 @@
</div> </div>
<!-- 进度 --> <!-- 进度 -->
<a-tooltip position="left" <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> mini>
<a-progress type="circle" <a-progress type="circle"
size="mini" size="mini"

View File

@@ -240,8 +240,9 @@
for (let file of host.files) { for (let file of host.files) {
const fileStatus = taskStatusData.files.find(s => s.id === file.id); const fileStatus = taskStatusData.files.find(s => s.id === file.id);
if (fileStatus) { if (fileStatus) {
file.status = fileStatus.status;
file.current = fileStatus.current; file.current = fileStatus.current;
file.status = fileStatus.status;
file.errorMessage = fileStatus.errorMessage;
} }
} }
} }

View File

@@ -46,9 +46,9 @@
<a-descriptions-item label="脚本执行"> <a-descriptions-item label="脚本执行">
{{ record.scriptExec === EnabledStatus.ENABLED ? '是' : '否' }} {{ record.scriptExec === EnabledStatus.ENABLED ? '是' : '否' }}
</a-descriptions-item> </a-descriptions-item>
<!-- 创建时间 --> <!-- 执行用户 -->
<a-descriptions-item label="创建时间"> <a-descriptions-item label="执行用户">
{{ dateFormat(new Date(record.createTime)) }} {{ record.execUsername }}
</a-descriptions-item> </a-descriptions-item>
<!-- 修改时间 --> <!-- 修改时间 -->
<a-descriptions-item label="修改时间"> <a-descriptions-item label="修改时间">

View File

@@ -12,6 +12,8 @@
:on-before-ok="handlerOk" :on-before-ok="handlerOk"
@close="handleClose"> @close="handleClose">
<a-spin class="full" :loading="loading"> <a-spin class="full" :loading="loading">
<!-- 提示 -->
<a-alert class="mb16">修改后将使用此用户的权限以及主机配置执行此任务</a-alert>
<a-form :model="formModel" <a-form :model="formModel"
label-align="right" label-align="right"
:auto-label-width="true"> :auto-label-width="true">