Compare commits

...

15 Commits

Author SHA1 Message Date
李佳航
7015468e04 Merge pull request #60 from dromara/dev
Dev
2024-09-27 10:52:27 +08:00
lijiahang
ef57040e1d 📝 修改文档. 2024-09-27 10:41:06 +08:00
lijiahang
2a6537c604 🔖 升级版本. 2024-09-27 10:21:17 +08:00
lijiahang
541a9790ad 优化批量上传逻辑. 2024-09-26 17:38:39 +08:00
lijiahang
011e8ad089 📝 修改文档. 2024-09-14 17:18:51 +08:00
李佳航
3918d36c24 Merge pull request #59 from dromara/dev
Dev
2024-09-14 16:46:32 +08:00
lijiahang
37f452c7c3 Merge remote-tracking branch 'origin/dev' into dev
# Conflicts:
#	README.md
2024-09-14 16:44:31 +08:00
lijiahang
fc885bc0ad 📝 修改文档. 2024-09-14 16:43:53 +08:00
李佳航
44c226092d Merge pull request #58 from dromara/dev
Dev
2024-09-11 21:18:41 +08:00
lijiahangmax
14cf8c2492 Merge remote-tracking branch 'origin/dev' into dev 2024-09-11 21:16:08 +08:00
lijiahangmax
d6f5898e61 📝 修改 logo. 2024-09-11 21:14:32 +08:00
lijiahang
0ab8e405cc 📝 修改文档. 2024-09-11 10:18:35 +08:00
lijiahang
4e4231d5a5 修改清理限制. 2024-09-03 10:53:39 +08:00
李佳航
2df3f2fa1a Merge pull request #56 from dromara/dev
🔖 升级版本.
2024-09-02 15:28:10 +08:00
lijiahang
d6048f78f0 🔖 升级版本. 2024-09-02 14:57:47 +08:00
39 changed files with 277 additions and 195 deletions

View File

@@ -1,5 +1,5 @@
<div align="center"><img src="https://bjuimg.obs.cn-north-4.myhuaweicloud.com/images/2024/5/29/cec03bbd-0eab-464d-9caf-d0b5a7ffc5a6.png" alt="logo" width="32" /></div>
<p style="margin-top: 12px" align="center"><b>一款高颜值、现代化的智能运维&轻量堡垒机平台。</b></p>
<div align="center"><img src="https://bjuimg.obs.cn-north-4.myhuaweicloud.com/images/2024/9/11/11e7e78e-2af0-4c68-9811-db8a4c4400f4.png" alt="logo" width="520" /></div>
<p style="margin-top: 12px" align="center"><b>一款高颜值、现代化的自动化运维&轻量堡垒机平台。</b></p>
<p align="center">
<a target="_blank"
style="text-decoration: none !important;"
@@ -40,7 +40,7 @@
------------------------------
**`orion-visor`** 提供一站式服务器运维解决方案。
**`orion-visor`** 提供一站式自动化运维解决方案。
* **资产管理**:支持对资产进行分组,实现对主机、密钥和身份的统一管理和授权。
* **在线终端**:提供在线终端 SSH 服务,支持快捷命令、自定义快捷键和主题风格。
@@ -51,7 +51,7 @@
## 演示环境
* 🔗 演示地址: https://dv.orionsec.cn/
* 🔗 演示地址: [https://dv.orionsec.cn/](https://dv.orionsec.cn/)
* 🔏 演示账号: admin/admin
* ⭐ 体验后可以点一下 `star` 这对我很重要! [github](https://github.com/dromara/orion-visor) [gitee](https://gitee.com/dromara/orion-visor) [gitcode](https://gitcode.com/dromara/orion-visor/overview)
* 🌈 如果本项目对你有帮助请帮忙推广一下 让更多的人知道此项目!
@@ -111,14 +111,18 @@ docker compose up -d
[![Star History Chart](https://api.star-history.com/svg?repos=lijiahangmax/orion-visor&type=Date)](https://star-history.com/#lijiahangmax/orion-visor&Date)
## 关于我
本人专注于使用 Java 和 Vue 进行全栈开发, 并在系统自动化运维方面拥有丰富开发的经验。如果您在这些领域有需求或遇到痛点, 请随时联系我, 并备注“合作”。
## 联系我
<div style="display: flex;">
<img src="https://bjuimg.obs.cn-north-4.myhuaweicloud.com/images/2024/5/17/d42d91f3-f0ee-4c63-adab-a35809e0804c.jpg" alt="wx" width="298px" height="398px"/>
</div>
![个人微信: ljh1553488](https://img.shields.io/badge/ljh1553488-blue?style=social&label=WX%3A)
![QQ群1: 755242157](https://img.shields.io/badge/755242157-blue?style=social&label=QQ%E7%BE%A41%3A%20)
微信: ljh1553488
QQ群: 755242157
📧 咨询问题微信备注: vis
📧 合作/功能定制备注: 合作

View File

@@ -1,7 +1,7 @@
version: '3.3'
services:
service:
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-service:2.1.5
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-service:2.1.7
privileged: true
ports:
- 1081:80
@@ -32,7 +32,7 @@ services:
- mysql
- redis
mysql:
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-mysql:2.1.5
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-mysql:2.1.7
privileged: true
ports:
- 3307:3306
@@ -52,7 +52,7 @@ services:
retries: 10
start_period: 3s
redis:
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-redis:2.1.5
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-redis:2.1.7
privileged: true
ports:
- 6380:6379

View File

@@ -1,7 +1,7 @@
version: '3.3'
services:
service:
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-service:2.1.5
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-service:2.1.7
privileged: true
ports:
- ${SERVICE_PORT:-1081}:80
@@ -32,7 +32,7 @@ services:
- mysql
- redis
mysql:
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-mysql:2.1.5
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-mysql:2.1.7
privileged: true
ports:
- 3307:3306
@@ -52,7 +52,7 @@ services:
retries: 15
start_period: 3s
redis:
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-redis:2.1.5
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-redis:2.1.7
privileged: true
ports:
- 6380:6379
@@ -68,7 +68,7 @@ services:
retries: 15
start_period: 3s
adminer:
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-adminer:2.1.5
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-adminer:2.1.7
ports:
- 8081:8080
depends_on:

View File

@@ -1,4 +1,4 @@
#/bin/bash
version=2.1.5
version=2.1.7
docker build -t orion-visor-adminer:${version} .
docker tag orion-visor-adminer:${version} registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-adminer:${version}

View File

@@ -1,5 +1,5 @@
#/bin/bash
version=2.1.5
version=2.1.7
cp -r ../../sql ./sql
docker build -t orion-visor-mysql:${version} .
rm -rf ./sql

View File

@@ -1,5 +1,5 @@
#/bin/bash
version=2.1.5
version=2.1.7
docker push registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-adminer:${version}
docker push registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-mysql:${version}
docker push registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-redis:${version}

View File

@@ -1,4 +1,4 @@
#/bin/bash
version=2.1.5
version=2.1.7
docker build -t orion-visor-redis:${version} .
docker tag orion-visor-redis:${version} registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-redis:${version}

View File

@@ -1,5 +1,5 @@
#/bin/bash
version=2.1.5
version=2.1.7
mv ../../orion-visor-launch/target/orion-visor-launch.jar ./orion-visor-launch.jar
mv ../../orion-visor-ui/dist ./dist
docker build -t orion-visor-service:${version} .

View File

@@ -14,7 +14,7 @@
<url>https://github.com/dromara/orion-visor</url>
<properties>
<revision>2.1.5</revision>
<revision>2.1.7</revision>
<spring.boot.version>2.7.17</spring.boot.version>
<spring.boot.admin.version>2.7.15</spring.boot.admin.version>
<flatten.maven.plugin.version>1.5.0</flatten.maven.plugin.version>

View File

@@ -14,7 +14,7 @@ public interface AppConst extends OrionConst {
/**
* 同 ${orion.version} 迭代时候需要手动更改
*/
String VERSION = "2.1.5";
String VERSION = "2.1.7";
/**
* 同 ${spring.application.name}

View File

@@ -21,6 +21,8 @@ public interface ExtraFieldConst extends FieldConst {
String USERNAME = "username";
String HOME = "home";
String STATUS_NAME = "statusName";
String KEY_NAME = "keyName";

View File

@@ -1,38 +0,0 @@
package com.orion.visor.framework.common.utils;
import com.orion.lang.utils.Arrays1;
import com.orion.lang.utils.crypto.Caesars;
/**
* 混淆工具类
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/6/17 18:27
*/
public class Mixes {
private Mixes() {
}
/**
* 混淆
* <p>
* 此方法不可修改
*
* @param str str
* @return str
*/
public static String obfuscate(String str) {
char[] chars = str.toCharArray();
Arrays1.reverse(chars);
for (int i = 0; i < chars.length; i += 2) {
char temp = chars[i];
chars[i] = chars[i + 1];
chars[i + 1] = temp;
}
String res = new String(chars);
return new Caesars().encrypt(res);
}
}

View File

@@ -1,6 +1,8 @@
package com.orion.visor.framework.common.utils;
import com.orion.lang.utils.Objects1;
import com.orion.lang.utils.Systems;
import com.orion.lang.utils.io.Files1;
import com.orion.visor.framework.common.constant.AppConst;
import com.orion.visor.framework.common.constant.Const;
@@ -24,9 +26,25 @@ public class PathUtils {
* @return 用户目录
*/
public static String getHomePath(boolean isWindows, String username) {
return getHomePath(isWindows, username, false);
}
/**
* 获取用户根目录
*
* @param isWindows isWindows
* @param username 用户名
* @param prependSeparator 是否在头部添加分隔符
* @return 用户目录
*/
public static String getHomePath(boolean isWindows, String username, boolean prependSeparator) {
if (isWindows) {
// windows
return "C:/Users/" + username;
if (prependSeparator) {
return "/C:/Users/" + username;
} else {
return "C:/Users/" + username;
}
} else {
// linux
if (Const.ROOT.equals(username)) {
@@ -66,4 +84,34 @@ public class PathUtils {
return path.toString();
}
/**
* 头部添加分隔符
*
* @param path path
* @return path
*/
public static String prependSeparator(String path) {
if (path.startsWith("/")) {
return path;
}
return "/" + path;
}
/**
* 获取 orion path
*
* @param path path
* @return path
*/
public static String getOrionPath(String path) {
path = Systems.HOME_DIR
+ Files1.SEPARATOR
+ AppConst.ORION
+ Files1.SEPARATOR
+ AppConst.APP_NAME
+ Files1.SEPARATOR
+ path;
return Files1.getPath(path);
}
}

View File

@@ -40,4 +40,6 @@ public interface HostConvert {
List<HostVO> toList(List<HostDO> domain);
List<HostBaseVO> toBaseList(List<HostDO> domain);
}

View File

@@ -28,7 +28,7 @@ public class UploadTaskOperatorType extends InitializingOperatorTypes {
@Override
public OperatorType[] types() {
return new OperatorType[]{
new OperatorType(M, UPLOAD, "批量上传文件 <sb>${count}</sb>个 (${name})"),
new OperatorType(M, UPLOAD, "批量上传文件 <sb>${count}</sb> 个 (${name})"),
new OperatorType(M, CANCEL, "取消上传文件 <sb>${name}</sb>"),
new OperatorType(H, DELETE, "删除上传记录 <sb>${count}</sb>条"),
new OperatorType(H, CLEAR, "清理上传记录 <sb>${count}</sb>条"),

View File

@@ -48,6 +48,10 @@ public class UploadTaskFileDO extends BaseDO {
@TableField("file_path")
private String filePath;
@Schema(description = "实际文件路径")
@TableField("real_file_path")
private String realFilePath;
@Schema(description = "文件大小")
@TableField("file_size")
private Long fileSize;

View File

@@ -40,6 +40,9 @@ public class UploadTaskFileVO implements Serializable {
@Schema(description = "文件路径")
private String filePath;
@Schema(description = "实际文件路径")
private String realFilePath;
@Schema(description = "文件大小")
private Long fileSize;

View File

@@ -40,4 +40,24 @@ public enum HostSshOsTypeEnum {
return LINUX;
}
/**
* 是否为 linux 系统
*
* @param type type
* @return isLinux
*/
public static boolean isLinux(String type) {
return LINUX.name().equals(type);
}
/**
* 是否为 windows 系统
*
* @param type type
* @return isWindows
*/
public static boolean isWindows(String type) {
return WINDOWS.name().equals(type);
}
}

View File

@@ -17,6 +17,7 @@ import com.orion.net.host.ssh.command.CommandExecutor;
import com.orion.spring.SpringHolder;
import com.orion.visor.framework.common.constant.ErrorMessage;
import com.orion.visor.framework.common.file.FileClient;
import com.orion.visor.framework.common.utils.PathUtils;
import com.orion.visor.module.asset.dao.ExecHostLogDAO;
import com.orion.visor.module.asset.entity.domain.ExecHostLogDO;
import com.orion.visor.module.asset.entity.dto.HostTerminalConnectDTO;
@@ -159,11 +160,8 @@ public abstract class BaseExecCommandHandler implements IExecCommandHandler {
// 打开 sftp
sftpExecutor = sessionStore.getSftpExecutor(execHostCommand.getFileNameCharset());
sftpExecutor.connect();
// 必须要以 / 开头
String scriptPath = execHostCommand.getScriptPath();
if (!scriptPath.startsWith("/")) {
scriptPath = "/" + scriptPath;
}
// 文件上传必须要以 / 开头
String scriptPath = PathUtils.prependSeparator(execHostCommand.getScriptPath());
// 创建文件
sftpExecutor.touch(scriptPath);
// 写入命令
@@ -225,29 +223,33 @@ public abstract class BaseExecCommandHandler implements IExecCommandHandler {
Long id = execHostCommand.getHostLogId();
String statusName = status.name();
log.info("BaseExecCommandHandler.updateStatus start id: {}, status: {}", id, statusName);
updateRecord.setId(id);
updateRecord.setStatus(statusName);
if (ExecHostStatusEnum.RUNNING.equals(status)) {
// 运行中
updateRecord.setStartTime(new Date());
} else if (ExecHostStatusEnum.COMPLETED.equals(status)) {
// 完成
updateRecord.setFinishTime(new Date());
updateRecord.setExitCode(executor.getExitCode());
this.exitCode = executor.getExitCode();
} else if (ExecHostStatusEnum.FAILED.equals(status)) {
// 失败
updateRecord.setFinishTime(new Date());
updateRecord.setErrorMessage(this.getErrorMessage(ex));
} else if (ExecHostStatusEnum.TIMEOUT.equals(status)) {
// 超时
updateRecord.setFinishTime(new Date());
} else if (ExecHostStatusEnum.INTERRUPTED.equals(status)) {
// 中断
updateRecord.setFinishTime(new Date());
try {
updateRecord.setId(id);
updateRecord.setStatus(statusName);
if (ExecHostStatusEnum.RUNNING.equals(status)) {
// 运行中
updateRecord.setStartTime(new Date());
} else if (ExecHostStatusEnum.COMPLETED.equals(status)) {
// 完成
updateRecord.setFinishTime(new Date());
updateRecord.setExitCode(executor.getExitCode());
this.exitCode = executor.getExitCode();
} else if (ExecHostStatusEnum.FAILED.equals(status)) {
// 失败
updateRecord.setFinishTime(new Date());
updateRecord.setErrorMessage(this.getErrorMessage(ex));
} else if (ExecHostStatusEnum.TIMEOUT.equals(status)) {
// 超时
updateRecord.setFinishTime(new Date());
} else if (ExecHostStatusEnum.INTERRUPTED.equals(status)) {
// 中断
updateRecord.setFinishTime(new Date());
}
int effect = execHostLogDAO.updateById(updateRecord);
log.info("BaseExecCommandHandler.updateStatus finish id: {}, effect: {}", id, effect);
} catch (Exception e) {
log.error("BaseExecCommandHandler.updateStatus error id: {}", id, e);
}
int effect = execHostLogDAO.updateById(updateRecord);
log.info("BaseExecCommandHandler.updateStatus finish id: {}, effect: {}", id, effect);
}
@Override

View File

@@ -154,24 +154,28 @@ public class ExecTaskHandler implements IExecTaskHandler {
*/
private void updateStatus(ExecStatusEnum status) {
Long id = execCommand.getLogId();
String statusName = status.name();
log.info("ExecTaskHandler-updateStatus start id: {}, status: {}", id, statusName);
ExecLogDO update = new ExecLogDO();
update.setId(id);
update.setStatus(statusName);
if (ExecStatusEnum.RUNNING.equals(status)) {
// 执行中
this.startTime = new Date();
update.setStartTime(new Date());
} else if (ExecStatusEnum.COMPLETED.equals(status)) {
// 执行完成
update.setFinishTime(new Date());
} else if (ExecStatusEnum.FAILED.equals(status)) {
// 执行失败
update.setFinishTime(new Date());
try {
String statusName = status.name();
log.info("ExecTaskHandler-updateStatus start id: {}, status: {}", id, statusName);
ExecLogDO update = new ExecLogDO();
update.setId(id);
update.setStatus(statusName);
if (ExecStatusEnum.RUNNING.equals(status)) {
// 执行中
this.startTime = new Date();
update.setStartTime(new Date());
} else if (ExecStatusEnum.COMPLETED.equals(status)) {
// 执行完成
update.setFinishTime(new Date());
} else if (ExecStatusEnum.FAILED.equals(status)) {
// 执行失败
update.setFinishTime(new Date());
}
int effect = execLogDAO.updateById(update);
log.info("ExecTaskHandler-updateStatus finish id: {}, effect: {}", id, effect);
} catch (Exception e) {
log.error("ExecTaskHandler-updateStatus error id: {}", id, e);
}
int effect = execLogDAO.updateById(update);
log.info("ExecTaskHandler-updateStatus finish id: {}, effect: {}", id, effect);
}
/**

View File

@@ -1,11 +1,9 @@
package com.orion.visor.module.asset.handler.host.upload.task;
import com.orion.lang.utils.Threads;
import com.orion.lang.utils.io.Files1;
import com.orion.lang.utils.io.Streams;
import com.orion.lang.utils.time.Dates;
import com.orion.spring.SpringHolder;
import com.orion.visor.framework.common.constant.Const;
import com.orion.visor.framework.common.constant.ExtraFieldConst;
import com.orion.visor.module.asset.dao.UploadTaskDAO;
import com.orion.visor.module.asset.dao.UploadTaskFileDAO;
@@ -143,7 +141,7 @@ public class FileUploadTask implements IFileUploadTask {
.map(s -> FileUploadFileItemDTO.builder()
.id(s.getId())
.fileId(s.getFileId())
.remotePath(Files1.getPath(Const.SLASH + record.getRemotePath() + Const.SLASH + s.getFilePath()))
.remotePath(s.getRealFilePath())
.status(UploadTaskFileStatusEnum.WAITING.name())
.current(0L)
.build())

View File

@@ -1,8 +1,5 @@
package com.orion.visor.module.asset.handler.host.upload.uploader;
import com.orion.lang.utils.Strings;
import com.orion.lang.utils.collect.Maps;
import com.orion.lang.utils.io.Files1;
import com.orion.lang.utils.io.Streams;
import com.orion.net.host.SessionStore;
import com.orion.net.host.sftp.SftpExecutor;
@@ -10,12 +7,10 @@ import com.orion.spring.SpringHolder;
import com.orion.visor.framework.common.constant.Const;
import com.orion.visor.framework.common.enums.EndpointDefine;
import com.orion.visor.framework.common.file.FileClient;
import com.orion.visor.framework.common.utils.PathUtils;
import com.orion.visor.module.asset.dao.UploadTaskFileDAO;
import com.orion.visor.module.asset.define.config.AppSftpConfig;
import com.orion.visor.module.asset.entity.domain.UploadTaskFileDO;
import com.orion.visor.module.asset.entity.dto.HostTerminalConnectDTO;
import com.orion.visor.module.asset.enums.HostSshOsTypeEnum;
import com.orion.visor.module.asset.enums.UploadTaskFileStatusEnum;
import com.orion.visor.module.asset.handler.host.jsch.SessionStores;
import com.orion.visor.module.asset.handler.host.upload.model.FileUploadFileItemDTO;
@@ -28,7 +23,6 @@ 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;
/**
@@ -106,10 +100,8 @@ public class FileUploader implements IFileUploader {
private boolean initSession() {
log.info("HostFileUploader.initSession start taskId: {}, hostId: {}", taskId, hostId);
try {
// 替换用户路径
HostTerminalConnectDTO connectInfo = hostTerminalService.getTerminalConnectInfo(hostId);
this.replaceRemotePathVariable(connectInfo.getOsType(), connectInfo.getUsername());
// 打开会话
HostTerminalConnectDTO connectInfo = hostTerminalService.getTerminalConnectInfo(hostId);
this.sessionStore = SessionStores.openSessionStore(connectInfo);
this.executor = sessionStore.getSftpExecutor(connectInfo.getFileNameCharset());
executor.connect();
@@ -222,27 +214,6 @@ public class FileUploader implements IFileUploader {
uploadTaskFileDAO.updateById(update);
}
/**
* 替换文件路径变量
*
* @param osType osType
* @param username username
*/
private void replaceRemotePathVariable(String osType, String username) {
// 包含变量
if (!files.get(0).getRemotePath().contains(Const.DOLLAR)) {
return;
}
String home = PathUtils.getHomePath(HostSshOsTypeEnum.WINDOWS.name().equals(osType), username);
// 替换变量
Map<String, String> env = Maps.newMap(4);
env.put("username", username);
env.put("home", home);
for (FileUploadFileItemDTO file : files) {
file.setRemotePath(Files1.getPath(Strings.format(file.getRemotePath(), env)));
}
}
@Override
public void cancel() {
log.info("HostFileUploader.cancel taskId: {}, hostId: {}, canceled: {}, closed: {}", taskId, hostId, canceled, closed);

View File

@@ -259,9 +259,10 @@ public class HostServiceImpl implements HostService {
if (wrapper == null) {
return DataGrid.of(Lists.empty());
}
// 数量条件
LambdaQueryWrapper<HostDO> countWrapper = wrapper.clone();
// 基础条件
LambdaQueryWrapper<HostDO> countWrapper = wrapper.clone()
.select(HostDAO.BASE_COLUMN)
wrapper.select(HostDAO.BASE_COLUMN)
.orderByAsc(HostDO::getId);
// 查询
DataGrid<HostVO> hosts = hostDAO.of(wrapper)

View File

@@ -3,6 +3,7 @@ package com.orion.visor.module.asset.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.orion.lang.annotation.Keep;
import com.orion.lang.define.collect.MultiHashMap;
import com.orion.lang.define.wrapper.DataGrid;
import com.orion.lang.utils.Arrays1;
import com.orion.lang.utils.Booleans;
@@ -15,9 +16,11 @@ import com.orion.lang.utils.time.Dates;
import com.orion.visor.framework.biz.operator.log.core.utils.OperatorLogs;
import com.orion.visor.framework.common.constant.Const;
import com.orion.visor.framework.common.constant.ErrorMessage;
import com.orion.visor.framework.common.constant.ExtraFieldConst;
import com.orion.visor.framework.common.enums.EndpointDefine;
import com.orion.visor.framework.common.file.FileClient;
import com.orion.visor.framework.common.security.LoginUser;
import com.orion.visor.framework.common.utils.PathUtils;
import com.orion.visor.framework.common.utils.SqlUtils;
import com.orion.visor.framework.common.utils.Valid;
import com.orion.visor.framework.mybatis.core.query.Conditions;
@@ -28,20 +31,21 @@ import com.orion.visor.module.asset.convert.UploadTaskFileConvert;
import com.orion.visor.module.asset.dao.HostDAO;
import com.orion.visor.module.asset.dao.UploadTaskDAO;
import com.orion.visor.module.asset.dao.UploadTaskFileDAO;
import com.orion.visor.module.asset.entity.domain.HostDO;
import com.orion.visor.module.asset.entity.domain.UploadTaskDO;
import com.orion.visor.module.asset.entity.domain.UploadTaskFileDO;
import com.orion.visor.module.asset.entity.dto.UploadTaskExtraDTO;
import com.orion.visor.module.asset.entity.request.upload.*;
import com.orion.visor.module.asset.entity.vo.*;
import com.orion.visor.module.asset.enums.HostTypeEnum;
import com.orion.visor.module.asset.enums.UploadTaskFileStatusEnum;
import com.orion.visor.module.asset.enums.UploadTaskStatusEnum;
import com.orion.visor.module.asset.enums.*;
import com.orion.visor.module.asset.handler.host.config.model.HostSshConfigModel;
import com.orion.visor.module.asset.handler.host.upload.FileUploadTasks;
import com.orion.visor.module.asset.handler.host.upload.manager.FileUploadTaskManager;
import com.orion.visor.module.asset.handler.host.upload.model.FileUploadFileItemDTO;
import com.orion.visor.module.asset.handler.host.upload.task.IFileUploadTask;
import com.orion.visor.module.asset.handler.host.upload.uploader.IFileUploader;
import com.orion.visor.module.asset.service.AssetAuthorizedDataService;
import com.orion.visor.module.asset.service.HostConfigService;
import com.orion.visor.module.asset.service.UploadTaskFileService;
import com.orion.visor.module.asset.service.UploadTaskService;
import com.orion.visor.module.infra.api.FileUploadApi;
@@ -83,6 +87,9 @@ public class UploadTaskServiceImpl implements UploadTaskService {
@Resource
private AssetAuthorizedDataService assetAuthorizedDataService;
@Resource
private HostConfigService hostConfigService;
@Resource
private FileUploadTaskManager fileUploadTaskManager;
@@ -103,10 +110,9 @@ public class UploadTaskServiceImpl implements UploadTaskService {
// 检查主机是否有权限
this.checkHostPermission(hostIdList);
// 查询主机信息
List<HostBaseVO> hosts = hostDAO.selectBaseByIdList(hostIdList)
.stream()
.map(HostConvert.MAPPER::toBase)
.collect(Collectors.toList());
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());
@@ -117,7 +123,7 @@ public class UploadTaskServiceImpl implements UploadTaskService {
record.setHostCount(hostIdList.size());
UploadTaskExtraDTO extra = UploadTaskExtraDTO.builder()
.hostIdList(hostIdList)
.hosts(hosts)
.hosts(HostConvert.MAPPER.toBaseList(hosts))
.build();
record.setExtraInfo(JSON.toJSONString(extra));
// 插入任务表
@@ -132,6 +138,7 @@ 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())
@@ -336,6 +343,68 @@ public class UploadTaskServiceImpl implements UploadTaskService {
}
}
/**
* 查询上传任务主机信息
*
* @param hostIdList hostIdList
* @return hosts
*/
public List<HostDO> getUploadTaskHosts(List<Long> hostIdList) {
// 查询主机信息
List<HostDO> hosts = hostDAO.selectBatchIds(hostIdList);
// 检查主机数量
Valid.eq(hosts.size(), hostIdList.size(), ErrorMessage.HOST_ABSENT);
// 检查主机状态
boolean allEnabled = hosts.stream()
.map(HostDO::getStatus)
.allMatch(s -> HostStatusEnum.ENABLED.name().equals(s));
Valid.isTrue(allEnabled, ErrorMessage.HOST_NOT_ENABLED);
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);
hostConfigMap.forEach((k, v) -> {
// 替换占位符
String username = v.getUsername();
String home = PathUtils.getHomePath(HostSshOsTypeEnum.isWindows(v.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(k, 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;
}
/**
* 检查文件完整性
*

View File

@@ -9,6 +9,7 @@
<result column="host_id" property="hostId"/>
<result column="file_id" property="fileId"/>
<result column="file_path" property="filePath"/>
<result column="real_file_path" property="realFilePath"/>
<result column="file_size" property="fileSize"/>
<result column="status" property="status"/>
<result column="start_time" property="startTime"/>
@@ -22,7 +23,7 @@
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, task_id, host_id, file_id, 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, start_time, end_time, create_time, update_time, creator, updater, deleted
</sql>
</mapper>

View File

@@ -4,10 +4,10 @@ import com.orion.ext.process.ProcessAwaitExecutor;
import com.orion.lang.support.Attempt;
import com.orion.lang.utils.Arrays1;
import com.orion.lang.utils.Strings;
import com.orion.lang.utils.crypto.Signatures;
import com.orion.lang.utils.io.Streams;
import com.orion.visor.framework.common.constant.AppConst;
import com.orion.visor.framework.common.constant.Const;
import com.orion.visor.framework.common.utils.Mixes;
import com.orion.visor.module.infra.entity.vo.AppInfoVO;
import com.orion.visor.module.infra.service.SystemSettingService;
import org.springframework.stereotype.Service;
@@ -70,7 +70,7 @@ public class SystemSettingServiceImpl implements SystemSettingService {
if (!Strings.isBlank(extraUuid)) {
uuid = extraUuid.trim();
}
return this.uuid = Mixes.obfuscate(uuid);
return this.uuid = Signatures.md5(uuid);
} catch (Exception e) {
// IGNORED
}

View File

@@ -1,6 +1,6 @@
VITE_API_BASE_URL= 'http://127.0.0.1:9200/orion-visor/api'
VITE_WS_BASE_URL= 'ws://127.0.0.1:9200/orion-visor/keep-alive'
VITE_APP_VERSION= '2.1.5'
VITE_APP_VERSION= '2.1.7'
VITE_APP_RELEASE= 'community'
VITE_SFTP_PREVIEW_MB= 2
VITE_DEMO_MODE= false

View File

@@ -1,6 +1,6 @@
VITE_API_BASE_URL= '/orion-visor/api'
VITE_WS_BASE_URL= '/orion-visor/keep-alive'
VITE_APP_VERSION= '2.1.5'
VITE_APP_VERSION= '2.1.7'
VITE_APP_RELEASE= 'community'
VITE_SFTP_PREVIEW_MB= 2
VITE_DEMO_MODE= false

View File

@@ -34,7 +34,7 @@ const enabled = (): Partial<VitePWAOptions> => {
manifest: {
name: 'Orion Visor Community',
short_name: 'Orion Visor',
description: '一款高颜值、现代化的智能运维&轻量堡垒机平台。',
description: '一款高颜值、现代化的自动化运维&轻量堡垒机平台。',
theme_color: '#212529',
icons: [{
src: 'manifest_logo_267.png',

View File

@@ -1,7 +1,7 @@
{
"name": "orion-visor-ui",
"description": "Orion Visor UI",
"version": "2.1.5",
"version": "2.1.7",
"private": true,
"author": "Jiahang Li",
"license": "Apache 2.0",

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

@@ -59,8 +59,8 @@
<a-form-item field="limit" label="数量限制">
<a-input-number v-model="formModel.limit"
:min="1"
:max="maxLimit"
:placeholder="`请输入数量限制 最大: ${maxLimit}`"
:max="maxClearLimit"
:placeholder="`请输入数量限制 最大: ${maxClearLimit}`"
hide-button
allow-clear />
</a-form-item>
@@ -101,16 +101,14 @@
type: undefined,
status: undefined,
startTimeRange: undefined,
limit: maxLimit.value,
limit: maxClearLimit,
};
};
const maxLimit = ref<number>(0);
const formModel = ref<HostConnectLogQueryRequest>({});
// 打开
const open = (record: any) => {
maxLimit.value = maxClearLimit;
renderForm({ ...defaultForm(), ...record });
setVisible(true);
};

View File

@@ -9,7 +9,7 @@ export default {
'login.form.userName.placeholder': '用户名',
'login.form.password.placeholder': '密码',
'login.form.login': '登录',
'login.banner.slogan1': '现代化的智能运维平台',
'login.banner.slogan1': '现代化的自动化运维平台',
'login.banner.subSlogan1': '一站式操作 让运维变得更简单',
'login.banner.slogan2': '高颜值的轻量堡垒机平台',
'login.banner.subSlogan2': '内置批量处理模块 让工作更高效',

View File

@@ -51,8 +51,8 @@
<a-form-item field="limit" label="数量限制">
<a-input-number v-model="formModel.limit"
:min="1"
:max="maxLimit"
:placeholder="`请输入数量限制 最大: ${maxLimit}`"
:max="maxClearLimit"
:placeholder="`请输入数量限制 最大: ${maxClearLimit}`"
hide-button
allow-clear />
</a-form-item>
@@ -85,7 +85,6 @@
const { loading, setLoading } = useLoading();
const { toOptions } = useDictStore();
const maxLimit = ref<number>(0);
const formModel = ref<ExecLogQueryRequest>({});
const defaultForm = (): ExecLogQueryRequest => {
@@ -96,13 +95,12 @@
command: undefined,
status: undefined,
startTimeRange: undefined,
limit: maxLimit.value,
limit: maxClearLimit,
};
};
// 打开
const open = (record: any) => {
maxLimit.value = maxClearLimit;
renderForm({ ...defaultForm(), ...record });
setVisible(true);
};

View File

@@ -52,8 +52,8 @@
<a-form-item field="limit" label="数量限制">
<a-input-number v-model="formModel.limit"
:min="1"
:max="maxLimit"
:placeholder="`请输入数量限制 最大: ${maxLimit}`"
:max="maxClearLimit"
:placeholder="`请输入数量限制 最大: ${maxClearLimit}`"
hide-button
allow-clear />
</a-form-item>
@@ -85,7 +85,6 @@
const { loading, setLoading } = useLoading();
const { toOptions } = useDictStore();
const maxLimit = ref<number>(0);
const formModel = ref<UploadTaskQueryRequest>({});
const defaultForm = (): UploadTaskQueryRequest => {
@@ -95,13 +94,12 @@
description: undefined,
status: undefined,
createTimeRange: undefined,
limit: maxLimit.value,
limit: maxClearLimit,
};
};
// 打开
const open = (record: any) => {
maxLimit.value = maxClearLimit;
renderForm({ ...defaultForm(), ...record });
setVisible(true);
};

View File

@@ -46,8 +46,8 @@
<a-form-item field="limit" label="数量限制">
<a-input-number v-model="formModel.limit"
:min="1"
:max="maxLimit"
:placeholder="`请输入数量限制 最大: ${maxLimit}`"
:max="maxClearLimit"
:placeholder="`请输入数量限制 最大: ${maxClearLimit}`"
hide-button
allow-clear />
</a-form-item>
@@ -80,7 +80,6 @@
const { loading, setLoading } = useLoading();
const { toOptions } = useDictStore();
const maxLimit = ref<number>(0);
const formModel = ref<ExecLogQueryRequest>({});
const defaultForm = (): ExecLogQueryRequest => {
@@ -91,13 +90,12 @@
command: undefined,
status: undefined,
startTimeRange: undefined,
limit: maxLimit.value,
limit: maxClearLimit,
};
};
// 打开
const open = (record: any) => {
maxLimit.value = maxClearLimit;
renderForm({ ...defaultForm(), ...record });
setVisible(true);
};

View File

@@ -65,8 +65,8 @@
<a-form-item field="limit" label="数量限制">
<a-input-number v-model="formModel.limit"
:min="1"
:max="maxLimit"
:placeholder="`请输入数量限制 最大: ${maxLimit}`"
:max="maxClearLimit"
:placeholder="`请输入数量限制 最大: ${maxClearLimit}`"
hide-button
allow-clear />
</a-form-item>
@@ -105,11 +105,10 @@
riskLevel: undefined,
result: undefined,
startTimeRange: undefined,
limit: maxLimit.value,
limit: maxClearLimit,
};
};
const maxLimit = ref<number>(0);
const typeOptions = ref<SelectOptionData[]>(toOptions(operatorLogTypeKey));
const formModel = ref<OperatorLogQueryRequest>({});
@@ -117,7 +116,6 @@
// 打开
const open = (record: OperatorLogQueryRequest) => {
maxLimit.value = maxClearLimit;
renderForm({ ...defaultForm(), ...record });
setVisible(true);
};

View File

@@ -22,7 +22,7 @@
</modules>
<properties>
<revision>2.1.5</revision>
<revision>2.1.7</revision>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<maven.surefire.plugin.version>3.0.0-M5</maven.surefire.plugin.version>

View File

@@ -824,20 +824,21 @@ CREATE TABLE `upload_task`
DROP TABLE IF EXISTS `upload_task_file`;
CREATE TABLE `upload_task_file`
(
`id` bigint(0) NOT NULL AUTO_INCREMENT COMMENT 'id',
`task_id` bigint(0) NULL DEFAULT NULL COMMENT '用户id',
`host_id` bigint(0) NULL DEFAULT NULL COMMENT '主机id',
`file_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '文件id',
`file_path` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '文件路径',
`file_size` bigint(0) NULL DEFAULT NULL COMMENT '文件大小',
`status` char(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '状态',
`start_time` datetime(3) NULL DEFAULT NULL COMMENT '开始时间',
`end_time` datetime(3) NULL DEFAULT NULL COMMENT '结束时间',
`create_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '修改时间',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建人',
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新',
`deleted` tinyint(1) NULL DEFAULT 0 COMMENT '是否删除 0未删除 1已删除',
`id` bigint(0) NOT NULL AUTO_INCREMENT COMMENT 'id',
`task_id` bigint(0) NULL DEFAULT NULL COMMENT '用户id',
`host_id` bigint(0) NULL DEFAULT NULL COMMENT '主机id',
`file_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '文件id',
`file_path` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '文件路径',
`real_file_path` varchar(2048) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '实际文件路径',
`file_size` bigint(0) NULL DEFAULT NULL COMMENT '文件大小',
`status` char(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '状态',
`start_time` datetime(3) NULL DEFAULT NULL COMMENT '开始时间',
`end_time` datetime(3) NULL DEFAULT NULL COMMENT '结束时间',
`create_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '修改时间',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建',
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新人',
`deleted` tinyint(1) NULL DEFAULT 0 COMMENT '是否删除 0未删除 1已删除',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_task_id` (`task_id`) USING BTREE
) ENGINE = InnoDB