Compare commits
8 Commits
v1.0.0-bet
...
v1.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
770f54df96 | ||
|
|
4799936a5f | ||
|
|
55373bb7dc | ||
|
|
7e91ef8386 | ||
|
|
1b459beb84 | ||
|
|
c48e48ab72 | ||
|
|
1c6a38d5d9 | ||
|
|
44dd5a9079 |
10
README.md
10
README.md
@@ -2,7 +2,7 @@
|
||||
<img style="margin-right: 8px;" src="https://bjuimg.obs.cn-north-4.myhuaweicloud.com/images/2024/2/27/8c687ef1-5711-4a93-9db0-79c010af7902.png" width="32px" height="32px"/> orion-ops-pro 是什么
|
||||
</h1>
|
||||
|
||||
`orion-ops-pro` 一款开箱即用的运维平台, 提供了资产管理、资产授权、Web终端、WebSftp、角色管理、系统管理等功能。为运维团队提供轻量化的运维治理平台。它是根据 `orion-ops`
|
||||
`orion-ops-pro` 一款开箱即用的一站式智能运维平台, 提供了资产管理、资产授权、Web终端、WebSftp、角色管理、系统管理等功能。致力于简化运维团队的治理工作。它是根据 `orion-ops`
|
||||
的产品思路完全重构的一套系统, 重新设计了架构并优化交互逻辑, 操作更快捷友好。
|
||||
|
||||
<p style="text-align: left">
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
<br/>
|
||||
|
||||
当前版本: **1.0.0-beta.1**
|
||||
当前版本: **1.0.0**
|
||||
github: https://github.com/lijiahangmax/orion-ops-pro
|
||||
gitee: https://gitee.com/lijiahangmax/orion-ops-pro
|
||||
文档: https://lijiahangmax.gitee.io/orion-ops-pro/#/
|
||||
@@ -40,12 +40,12 @@ demo: http://101.43.254.243:1081/#/
|
||||
|
||||
## 特性
|
||||
|
||||
* 易用便捷: 极简配置, 开箱即用, 并兼容 Docker 部署方式。
|
||||
* 易用便捷: 极简配置, 开箱即用, 支持 Docker 部署方式。
|
||||
* 资产管理: 支持灵活配置主机分组, 统一管理主机、秘钥和身份。
|
||||
* 资产授权: 可将资产数据授权给指定角色和用户。
|
||||
* 权限控制: 全面管理用户角色, 支持动态菜单配置和强制下线等功能。
|
||||
* 在线终端: 提供便捷的在线 Web 终端服务, 支持自定义快捷键和主题风格。
|
||||
* 文件管理: 实现远程主机文件的批量上传、下载和在线编辑等操作。
|
||||
* 在线终端: 提供便捷的在线 Web 终端服务, 支持快捷命令、自定义快捷键和主题风格。
|
||||
* 文件管理: 实现远程主机大文件的批量上传、下载和在线编辑等操作。
|
||||
* 可扩展性: 前后端代码规范统一, 代码质量高、健壮且易于阅读和扩展。
|
||||
|
||||
[comment]: <> ( FIXME * 批量操作: 支持远程主机批量执行命令 以及 批量执行上传文件)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
version: '3.3'
|
||||
services:
|
||||
orion-ops-pro:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-ops-pro:1.0.0-beta.1
|
||||
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-ops-pro:1.0.0
|
||||
ports:
|
||||
- 1081:80
|
||||
environment:
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
mv ../../orion-ops-launch/target/orion-ops-launch.jar ./
|
||||
mv ../../orion-ops-ui/dist ./dist
|
||||
docker build -t orion-ops-pro:1.0.0-beta.1 .
|
||||
docker build -t orion-ops-pro:1.0.0 .
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<img style="margin-right: 8px;" src="./assert/logo.svg" width="32px" height="32px"/> orion-ops-pro 是什么
|
||||
</h1>
|
||||
|
||||
`orion-ops-pro` 一款开箱即用的运维平台, 提供了资产管理、资产授权、Web终端、WebSftp、角色管理、系统管理等功能。为运维团队提供轻量化的运维治理平台。它是根据 `orion-ops`
|
||||
`orion-ops-pro` 一款开箱即用的一站式智能运维平台, 提供了资产管理、资产授权、Web终端、WebSftp、角色管理、系统管理等功能。致力于简化运维团队的治理工作。它是根据 `orion-ops`
|
||||
的产品思路完全重构的一套系统, 重新设计了架构并优化交互逻辑, 操作更快捷友好。
|
||||
|
||||
<p style="text-align: left">
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
<br/>
|
||||
|
||||
当前版本: **1.0.0-beta.1**
|
||||
当前版本: **1.0.0**
|
||||
github: https://github.com/lijiahangmax/orion-ops-pro
|
||||
gitee: https://gitee.com/lijiahangmax/orion-ops-pro
|
||||
文档: https://lijiahangmax.gitee.io/orion-ops-pro/#/
|
||||
@@ -40,12 +40,12 @@ demo: http://101.43.254.243:1081/#/
|
||||
|
||||
## 特性
|
||||
|
||||
* 易用便捷: 极简配置, 开箱即用, 并兼容 Docker 部署方式。
|
||||
* 易用便捷: 极简配置, 开箱即用, 支持 Docker 部署方式。
|
||||
* 资产管理: 支持灵活配置主机分组, 统一管理主机、秘钥和身份。
|
||||
* 资产授权: 可将资产数据授权给指定角色和用户。
|
||||
* 权限控制: 全面管理用户角色, 支持动态菜单配置和强制下线等功能。
|
||||
* 在线终端: 提供便捷的在线 Web 终端服务, 支持自定义快捷键和主题风格。
|
||||
* 文件管理: 实现远程主机文件的批量上传、下载和在线编辑等操作。
|
||||
* 在线终端: 提供便捷的在线 Web 终端服务, 支持快捷命令、自定义快捷键和主题风格。
|
||||
* 文件管理: 实现远程主机大文件的批量上传、下载和在线编辑等操作。
|
||||
* 可扩展性: 前后端代码规范统一, 代码质量高、健壮且易于阅读和扩展。
|
||||
|
||||
[comment]: <> ( FIXME * 批量操作: 支持远程主机批量执行命令 以及 批量执行上传文件)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# orion-ops-pro <small>1.0.0-beta.1</small>
|
||||
# orion-ops-pro <small>1.0.0</small>
|
||||
|
||||
> 一款开箱即用的运维平台。
|
||||
|
||||
- 易用 便捷
|
||||
- 友好 易用
|
||||
- 安全 稳定
|
||||
- 智能 高效
|
||||
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
> 版本号严格遵循 Semver 规范。
|
||||
|
||||
[//]: # (🌈添加 🔨优化 🐞修复 [如何升级](/about/update.md?id=_100))
|
||||
[//]: # (🐞修复)
|
||||
|
||||
## 1.0.0
|
||||
|
||||
`2024-03-01` `release`
|
||||
|
||||
🌈 用户自定义终端标签颜色
|
||||
🔨 拓展数据模块添加缓存
|
||||
|
||||
[如何升级](/about/update.md?id=_100)
|
||||
|
||||
## 1.0.0-beta.1
|
||||
|
||||
|
||||
@@ -9,4 +9,4 @@
|
||||
* 站内消息
|
||||
* 后端配置动态配置
|
||||
* 终端背景图片
|
||||
* 按资产数据 UI 改版
|
||||
* 资产授权 UI 改版
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
⚡ 注意: 应用不支持跨版本升级, 可以进行多次升级
|
||||
|
||||
## 1.0.0-beta.1
|
||||
## 1.0.0
|
||||
|
||||
> sql 脚本
|
||||
|
||||
```sql
|
||||
|
||||
INSERT INTO `dict_key` VALUES (32, 'terminalTabColor', 'COLOR', '[]', '终端标签页颜色', '2024-03-01 15:01:44', '2024-03-01 15:01:44', '1', '1', 0);
|
||||
INSERT INTO `dict_value` VALUES (203, 32, 'terminalTabColor', 'rgb(var(--red-6))', '红色', '{}', 10, '2024-03-01 15:07:41', '2024-03-01 15:07:41', '1', '1', 0);
|
||||
INSERT INTO `dict_value` VALUES (204, 32, 'terminalTabColor', 'rgb(var(--orange-6))', '橙色', '{}', 20, '2024-03-01 15:07:55', '2024-03-01 15:07:55', '1', '1', 0);
|
||||
INSERT INTO `dict_value` VALUES (205, 32, 'terminalTabColor', 'rgb(var(--yellow-6))', '黄色', '{}', 30, '2024-03-01 15:08:13', '2024-03-01 15:08:13', '1', '1', 0);
|
||||
INSERT INTO `dict_value` VALUES (206, 32, 'terminalTabColor', 'rgb(var(--green-6))', '绿色', '{}', 40, '2024-03-01 15:08:23', '2024-03-01 15:08:23', '1', '1', 0);
|
||||
INSERT INTO `dict_value` VALUES (207, 32, 'terminalTabColor', 'rgb(var(--cyan-6))', '青色', '{}', 50, '2024-03-01 15:08:46', '2024-03-01 15:08:46', '1', '1', 0);
|
||||
INSERT INTO `dict_value` VALUES (208, 32, 'terminalTabColor', 'rgb(var(--blue-6))', '浅蓝', '{}', 60, '2024-03-01 15:11:01', '2024-03-01 15:11:01', '1', '1', 0);
|
||||
INSERT INTO `dict_value` VALUES (209, 32, 'terminalTabColor', 'rgb(var(--arcoblue-6))', '蓝色', '{}', 70, '2024-03-01 15:11:11', '2024-03-01 15:11:11', '1', '1', 0);
|
||||
INSERT INTO `dict_value` VALUES (210, 32, 'terminalTabColor', 'rgb(var(--purple-6))', '紫色', '{}', 80, '2024-03-01 15:11:20', '2024-03-01 15:11:20', '1', '1', 0);
|
||||
INSERT INTO `dict_value` VALUES (211, 32, 'terminalTabColor', 'rgb(var(--pinkpurple-6))', '粉紫', '{}', 90, '2024-03-01 15:11:41', '2024-03-01 15:11:41', '1', '1', 0);
|
||||
INSERT INTO `dict_value` VALUES (213, 32, 'terminalTabColor', 'rgb(var(--gray-6))', '灰色', '{}', 100, '2024-03-01 15:12:01', '2024-03-01 15:39:34', '1', '1', 0);
|
||||
```
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
|
||||
主机终端页面 支持 SSH, SFTP。
|
||||
打开后默认会进入新建连接页面, 页面的主机数据是用户授权的资产数据。
|
||||
鼠标移入列表内的主机上时, 右侧会出现 `打开 SSH` `打开 SFTP` `连接设置` `收藏` 的按钮。
|
||||
点击 `连接设置` 后, 会弹出模态框, 可以自定义配置连接主机的 密码/秘钥/身份, 仅对自己生效, 不会修改全局配置, `秘钥` `身份` 数据是用户授权的资产数据。
|
||||
鼠标移入列表内的主机上时, 右侧会出现 `打开 SSH` `打开 SFTP` `主机设置` `收藏` 的按钮。
|
||||
|
||||
> 主机设置
|
||||
|
||||
* SSH 配置: 可以自定义配置连接主机的 密码/秘钥/身份, 仅对自己生效, 不会修改全局配置, `秘钥` `身份` 数据是用户授权的资产数据
|
||||
* 标签颜色: 自定义配置标签的颜色, 可以用来区分环境等
|
||||
|
||||
> 顶部状态栏
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<url>https://github.com/lijiahangmax/orion-ops-pro</url>
|
||||
|
||||
<properties>
|
||||
<revision>1.0.0-beta.1</revision>
|
||||
<revision>1.0.0</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>
|
||||
|
||||
@@ -12,7 +12,7 @@ public interface OrionOpsProConst {
|
||||
/**
|
||||
* 同 ${orion.version} 迭代时候需要手动更改
|
||||
*/
|
||||
String VERSION = "1.0.0-beta.1";
|
||||
String VERSION = "1.0.0";
|
||||
|
||||
String GITHUB = "https://github.com/lijiahangmax/orion-ops-pro";
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@ public class WebSockets {
|
||||
try {
|
||||
Threads.sleep(delay);
|
||||
session.sendMessage(new TextMessage(message));
|
||||
log.info("消息重发成功");
|
||||
Threads.sleep(delay);
|
||||
} catch (Exception ex) {
|
||||
throw Exceptions.ioRuntime(ex);
|
||||
|
||||
@@ -64,4 +64,7 @@ public class HostVO implements Serializable {
|
||||
@Schema(description = "别名")
|
||||
private String alias;
|
||||
|
||||
@Schema(description = "颜色")
|
||||
private String color;
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@ package com.orion.ops.module.asset.enums;
|
||||
import com.orion.ops.framework.common.handler.data.GenericsDataDefinition;
|
||||
import com.orion.ops.framework.common.handler.data.model.GenericsDataModel;
|
||||
import com.orion.ops.framework.common.handler.data.strategy.MapDataStrategy;
|
||||
import com.orion.ops.module.asset.handler.host.extra.model.HostColorExtraModel;
|
||||
import com.orion.ops.module.asset.handler.host.extra.model.HostSshExtraModel;
|
||||
import com.orion.ops.module.asset.handler.host.extra.strategy.HostColorExtraStrategy;
|
||||
import com.orion.ops.module.asset.handler.host.extra.strategy.HostSshExtraStrategy;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
@@ -24,6 +26,11 @@ public enum HostExtraItemEnum implements GenericsDataDefinition {
|
||||
*/
|
||||
SSH("ssh", HostSshExtraModel.class, HostSshExtraStrategy.class),
|
||||
|
||||
/**
|
||||
* 颜色额外配置
|
||||
*/
|
||||
COLOR("color", HostColorExtraModel.class, HostColorExtraStrategy.class),
|
||||
|
||||
;
|
||||
|
||||
private final String item;
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.orion.ops.module.asset.handler.host.extra.model;
|
||||
|
||||
import com.orion.ops.framework.common.handler.data.model.GenericsDataModel;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 主机拓展信息 - color 模型
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/2/29 23:16
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(name = "HostExtraSshModel", description = "主机拓展信息 - color 模型")
|
||||
public class HostColorExtraModel implements GenericsDataModel {
|
||||
|
||||
@Schema(description = "颜色")
|
||||
private String color;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.orion.ops.module.asset.handler.host.extra.strategy;
|
||||
|
||||
import com.orion.ops.framework.common.handler.data.strategy.MapDataStrategy;
|
||||
import com.orion.ops.module.asset.handler.host.extra.model.HostColorExtraModel;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 主机拓展信息 - 颜色 模型处理策略
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/2/29 23:16
|
||||
*/
|
||||
@Component
|
||||
public class HostColorExtraStrategy implements MapDataStrategy<HostColorExtraModel> {
|
||||
|
||||
@Override
|
||||
public HostColorExtraModel getDefault() {
|
||||
return HostColorExtraModel.builder()
|
||||
// 默认透明
|
||||
.color("")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFill(HostColorExtraModel beforeModel, HostColorExtraModel afterModel) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preValid(HostColorExtraModel model) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void valid(HostColorExtraModel model) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.orion.ops.module.asset.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.orion.lang.function.Functions;
|
||||
import com.orion.lang.utils.Refs;
|
||||
import com.orion.lang.utils.collect.Lists;
|
||||
import com.orion.lang.utils.collect.Maps;
|
||||
import com.orion.ops.framework.common.constant.Const;
|
||||
@@ -10,8 +12,10 @@ import com.orion.ops.module.asset.convert.HostGroupConvert;
|
||||
import com.orion.ops.module.asset.entity.request.asset.AssetAuthorizedDataQueryRequest;
|
||||
import com.orion.ops.module.asset.entity.vo.*;
|
||||
import com.orion.ops.module.asset.enums.HostConnectTypeEnum;
|
||||
import com.orion.ops.module.asset.handler.host.extra.model.HostColorExtraModel;
|
||||
import com.orion.ops.module.asset.service.*;
|
||||
import com.orion.ops.module.infra.api.*;
|
||||
import com.orion.ops.module.infra.constant.DataExtraItems;
|
||||
import com.orion.ops.module.infra.entity.dto.data.DataGroupDTO;
|
||||
import com.orion.ops.module.infra.entity.dto.tag.TagDTO;
|
||||
import com.orion.ops.module.infra.enums.*;
|
||||
@@ -67,7 +71,7 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic
|
||||
private TagRelApi tagRelApi;
|
||||
|
||||
@Resource
|
||||
private DataAliasApi dataAliasApi;
|
||||
private DataExtraApi dataExtraApi;
|
||||
|
||||
@Override
|
||||
public List<Long> getAuthorizedDataRelId(DataPermissionTypeEnum type, AssetAuthorizedDataQueryRequest request) {
|
||||
@@ -179,8 +183,10 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic
|
||||
Future<List<Long>> favoriteResult = favoriteApi.getFavoriteRelIdListAsync(FavoriteTypeEnum.HOST, userId);
|
||||
// 查询最近连接的主机
|
||||
Future<List<Long>> latestConnectHostIdList = hostConnectLogService.getLatestConnectHostIdAsync(HostConnectTypeEnum.SSH, userId);
|
||||
// 查询数据别名
|
||||
Future<Map<Long, String>> dataAliasResult = dataAliasApi.getDataAliasAsync(userId, DataExtraTypeEnum.HOST);
|
||||
// 查询别名
|
||||
Future<Map<Long, String>> dataAliasResult = dataExtraApi.getExtraItemValuesByCacheAsync(userId, DataExtraTypeEnum.HOST, DataExtraItems.ALIAS);
|
||||
// 查询颜色
|
||||
Future<Map<Long, String>> dataColorResult = dataExtraApi.getExtraItemValuesByCacheAsync(userId, DataExtraTypeEnum.HOST, DataExtraItems.COLOR);
|
||||
// 查询分组
|
||||
List<DataGroupDTO> dataGroup = dataGroupApi.getDataGroupList(DataGroupTypeEnum.HOST);
|
||||
// 查询分组引用
|
||||
@@ -207,7 +213,8 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic
|
||||
// 设置主机拓展信息
|
||||
this.getAuthorizedHostExtra(wrapper.getHostList(),
|
||||
favoriteResult.get(),
|
||||
dataAliasResult.get());
|
||||
dataAliasResult.get(),
|
||||
dataColorResult.get());
|
||||
// 设置最近连接的主机
|
||||
wrapper.setLatestHosts(new LinkedHashSet<>(latestConnectHostIdList.get()));
|
||||
return wrapper;
|
||||
@@ -292,10 +299,12 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic
|
||||
* @param hosts hosts
|
||||
* @param favorite favorite
|
||||
* @param aliasMap aliasMap
|
||||
* @param colorMap colorMap
|
||||
*/
|
||||
private void getAuthorizedHostExtra(List<HostVO> hosts,
|
||||
List<Long> favorite,
|
||||
Map<Long, String> aliasMap) {
|
||||
Map<Long, String> aliasMap,
|
||||
Map<Long, String> colorMap) {
|
||||
if (Lists.isEmpty(hosts)) {
|
||||
return;
|
||||
}
|
||||
@@ -313,7 +322,21 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic
|
||||
}
|
||||
// 设置主机别名
|
||||
if (!Maps.isEmpty(aliasMap)) {
|
||||
hosts.forEach(s -> s.setAlias(aliasMap.get(s.getId())));
|
||||
hosts.forEach(s -> {
|
||||
String alias = aliasMap.get(s.getId());
|
||||
if (alias != null) {
|
||||
s.setAlias(Refs.unrefToString(alias));
|
||||
}
|
||||
});
|
||||
}
|
||||
// 设置主机颜色
|
||||
if (!Maps.isEmpty(colorMap)) {
|
||||
hosts.forEach(s -> {
|
||||
HostColorExtraModel color = JSON.parseObject(colorMap.get(s.getId()), HostColorExtraModel.class);
|
||||
if (color != null) {
|
||||
s.setColor(color.getColor());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.orion.ops.module.asset.service.impl;
|
||||
|
||||
import com.orion.lang.function.Functions;
|
||||
import com.orion.lang.utils.Refs;
|
||||
import com.orion.lang.utils.collect.Maps;
|
||||
import com.orion.ops.framework.common.constant.ErrorMessage;
|
||||
import com.orion.ops.framework.common.handler.data.model.GenericsDataModel;
|
||||
@@ -12,9 +13,8 @@ import com.orion.ops.module.asset.entity.request.host.HostExtraQueryRequest;
|
||||
import com.orion.ops.module.asset.entity.request.host.HostExtraUpdateRequest;
|
||||
import com.orion.ops.module.asset.enums.HostExtraItemEnum;
|
||||
import com.orion.ops.module.asset.service.HostExtraService;
|
||||
import com.orion.ops.module.infra.api.DataAliasApi;
|
||||
import com.orion.ops.module.infra.api.DataExtraApi;
|
||||
import com.orion.ops.module.infra.entity.dto.data.DataAliasUpdateDTO;
|
||||
import com.orion.ops.module.infra.constant.DataExtraItems;
|
||||
import com.orion.ops.module.infra.entity.dto.data.DataExtraDTO;
|
||||
import com.orion.ops.module.infra.entity.dto.data.DataExtraQueryDTO;
|
||||
import com.orion.ops.module.infra.entity.dto.data.DataExtraSetDTO;
|
||||
@@ -36,20 +36,18 @@ import java.util.stream.Collectors;
|
||||
@Service
|
||||
public class HostExtraServiceImpl implements HostExtraService {
|
||||
|
||||
@Resource
|
||||
private DataAliasApi dataAliasApi;
|
||||
|
||||
@Resource
|
||||
private DataExtraApi dataExtraApi;
|
||||
|
||||
@Override
|
||||
public Integer updateHostAlias(HostAliasUpdateRequest request) {
|
||||
DataAliasUpdateDTO update = DataAliasUpdateDTO.builder()
|
||||
DataExtraSetDTO update = DataExtraSetDTO.builder()
|
||||
.userId(SecurityUtils.getLoginUserId())
|
||||
.item(DataExtraItems.ALIAS)
|
||||
.relId(request.getId())
|
||||
.alias(request.getName())
|
||||
.value(Refs.json(request.getName()))
|
||||
.build();
|
||||
return dataAliasApi.updateDataAlias(update, DataExtraTypeEnum.HOST);
|
||||
return dataExtraApi.setExtraItem(update, DataExtraTypeEnum.HOST);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
package com.orion.ops.module.infra.api;
|
||||
|
||||
import com.orion.ops.module.infra.entity.dto.data.DataAliasUpdateDTO;
|
||||
import com.orion.ops.module.infra.enums.DataExtraTypeEnum;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* 数据别名 对外服务类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023-12-18 17:37
|
||||
*/
|
||||
public interface DataAliasApi {
|
||||
|
||||
/**
|
||||
* 更新数据别名
|
||||
*
|
||||
* @param dto dto
|
||||
* @param type type
|
||||
* @return effect
|
||||
*/
|
||||
Integer updateDataAlias(DataAliasUpdateDTO dto, DataExtraTypeEnum type);
|
||||
|
||||
/**
|
||||
* 查询数据别名
|
||||
*
|
||||
* @param userId userId
|
||||
* @param type type
|
||||
* @param relId relId
|
||||
* @return aliasName
|
||||
*/
|
||||
String getDataAlias(Long userId, DataExtraTypeEnum type, Long relId);
|
||||
|
||||
/**
|
||||
* 查询数据别名
|
||||
*
|
||||
* @param userId userId
|
||||
* @param type type
|
||||
* @return relId:aliasName
|
||||
*/
|
||||
Map<Long, String> getDataAlias(Long userId, DataExtraTypeEnum type);
|
||||
|
||||
/**
|
||||
* 异步查询数据别名
|
||||
*
|
||||
* @param userId userId
|
||||
* @param type type
|
||||
* @return relId:aliasName
|
||||
*/
|
||||
Future<Map<Long, String>> getDataAliasAsync(Long userId, DataExtraTypeEnum type);
|
||||
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import com.orion.ops.module.infra.enums.DataExtraTypeEnum;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* 数据拓展信息 对外服务类
|
||||
@@ -69,6 +70,37 @@ public interface DataExtraApi {
|
||||
*/
|
||||
Map<Long, String> getExtraItemValues(DataExtraQueryDTO dto, DataExtraTypeEnum type);
|
||||
|
||||
/**
|
||||
* 查询额外配置项 (查询缓存)
|
||||
*
|
||||
* @param userId userId
|
||||
* @param type type
|
||||
* @param item item
|
||||
* @param relId relId
|
||||
* @return value
|
||||
*/
|
||||
String getExtraItemValueByCache(Long userId, DataExtraTypeEnum type, String item, Long relId);
|
||||
|
||||
/**
|
||||
* 查询额外配置项 (查询缓存)
|
||||
*
|
||||
* @param userId userId
|
||||
* @param type type
|
||||
* @param item item
|
||||
* @return relId:value
|
||||
*/
|
||||
Map<Long, String> getExtraItemValuesByCache(Long userId, DataExtraTypeEnum type, String item);
|
||||
|
||||
/**
|
||||
* 异步查询额外配置项 (查询缓存)
|
||||
*
|
||||
* @param userId userId
|
||||
* @param type type
|
||||
* @param item item
|
||||
* @return value
|
||||
*/
|
||||
Future<Map<Long, String>> getExtraItemValuesByCacheAsync(Long userId, DataExtraTypeEnum type, String item);
|
||||
|
||||
/**
|
||||
* 查询额外配置
|
||||
*
|
||||
|
||||
@@ -11,4 +11,6 @@ public interface DataExtraItems {
|
||||
|
||||
String ALIAS = "alias";
|
||||
|
||||
String COLOR = "color";
|
||||
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
package com.orion.ops.module.infra.entity.dto.data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 数据别名 更新请求业务对象
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023-12-18 17:37
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(name = "DataAliasUpdateDTO", description = "数据别名 创建请求业务对象")
|
||||
public class DataAliasUpdateDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@NotNull
|
||||
@Schema(description = "用户id")
|
||||
private Long userId;
|
||||
|
||||
@NotNull
|
||||
@Schema(description = "数据id")
|
||||
private Long relId;
|
||||
|
||||
@NotNull
|
||||
@Size(max = 32)
|
||||
@Schema(description = "别名")
|
||||
private String alias;
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
package com.orion.ops.module.infra.api.impl;
|
||||
|
||||
import com.orion.ops.framework.common.utils.Valid;
|
||||
import com.orion.ops.module.infra.api.DataAliasApi;
|
||||
import com.orion.ops.module.infra.entity.dto.data.DataAliasUpdateDTO;
|
||||
import com.orion.ops.module.infra.entity.request.data.DataAliasUpdateRequest;
|
||||
import com.orion.ops.module.infra.enums.DataExtraTypeEnum;
|
||||
import com.orion.ops.module.infra.service.DataAliasService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* 数据别名 对外服务实现类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023-12-18 17:37
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class DataAliasApiImpl implements DataAliasApi {
|
||||
|
||||
@Resource
|
||||
private DataAliasService dataAliasService;
|
||||
|
||||
@Override
|
||||
public Integer updateDataAlias(DataAliasUpdateDTO dto, DataExtraTypeEnum type) {
|
||||
Valid.valid(dto);
|
||||
DataAliasUpdateRequest update = DataAliasUpdateRequest.builder()
|
||||
.userId(dto.getUserId())
|
||||
.type(type.name())
|
||||
.relId(dto.getRelId())
|
||||
.alias(dto.getAlias())
|
||||
.build();
|
||||
return dataAliasService.updateDataAlias(update);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDataAlias(Long userId, DataExtraTypeEnum type, Long relId) {
|
||||
Valid.allNotNull(userId, relId);
|
||||
return dataAliasService.getDataAlias(userId, type.name(), relId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Long, String> getDataAlias(Long userId, DataExtraTypeEnum type) {
|
||||
Valid.notNull(userId);
|
||||
return dataAliasService.getDataAlias(userId, type.name());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Async("asyncExecutor")
|
||||
public Future<Map<Long, String>> getDataAliasAsync(Long userId, DataExtraTypeEnum type) {
|
||||
Valid.notNull(userId);
|
||||
return CompletableFuture.completedFuture(dataAliasService.getDataAlias(userId, type.name()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,6 +18,8 @@ import org.springframework.stereotype.Service;
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -83,6 +85,23 @@ public class DataExtraApiImpl implements DataExtraApi {
|
||||
return dataExtraService.getExtraItemValues(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExtraItemValueByCache(Long userId, DataExtraTypeEnum type, String item, Long relId) {
|
||||
Valid.allNotNull(userId, type, item, relId);
|
||||
return dataExtraService.getExtraItemValueByCache(userId, type.name(), item, relId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Long, String> getExtraItemValuesByCache(Long userId, DataExtraTypeEnum type, String item) {
|
||||
Valid.allNotNull(userId, type, item);
|
||||
return dataExtraService.getExtraItemValuesByCache(userId, type.name(), item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Map<Long, String>> getExtraItemValuesByCacheAsync(Long userId, DataExtraTypeEnum type, String item) {
|
||||
return CompletableFuture.completedFuture(this.getExtraItemValuesByCache(userId, type, item));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataExtraDTO getExtraItem(DataExtraQueryDTO dto, DataExtraTypeEnum type) {
|
||||
Valid.allNotNull(dto.getUserId(), dto.getRelId(), dto.getItem());
|
||||
|
||||
@@ -15,9 +15,9 @@ import java.util.concurrent.TimeUnit;
|
||||
*/
|
||||
public interface DataExtraCacheKeyDefine {
|
||||
|
||||
CacheKeyDefine DATA_ALIAS = new CacheKeyBuilder()
|
||||
.key("data:alias:{}:{}")
|
||||
.desc("数据别名 ${userId} ${type}")
|
||||
CacheKeyDefine DATA_EXTRA = new CacheKeyBuilder()
|
||||
.key("data:extra:{}:{}:{}")
|
||||
.desc("数据拓展信息 ${userId} ${type} ${item}")
|
||||
.type(String.class)
|
||||
.struct(RedisCacheStruct.HASH)
|
||||
.timeout(1, TimeUnit.DAYS)
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
package com.orion.ops.module.infra.entity.request.data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 数据别名 更新请求对象
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023-12-18 17:37
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(name = "DataAliasUpdateRequest", description = "数据别名 更新请求对象")
|
||||
public class DataAliasUpdateRequest implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@NotNull
|
||||
@Schema(description = "用户id")
|
||||
private Long userId;
|
||||
|
||||
@NotNull
|
||||
@Schema(description = "数据id")
|
||||
private Long relId;
|
||||
|
||||
@NotBlank
|
||||
@Size(max = 32)
|
||||
@Schema(description = "数据类型")
|
||||
private String type;
|
||||
|
||||
@Size(max = 32)
|
||||
@Schema(description = "别名")
|
||||
private String alias;
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package com.orion.ops.module.infra.service;
|
||||
|
||||
import com.orion.ops.module.infra.entity.request.data.DataAliasUpdateRequest;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 数据别名 服务类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023-12-18 17:37
|
||||
*/
|
||||
public interface DataAliasService {
|
||||
|
||||
/**
|
||||
* 更新数据别名
|
||||
*
|
||||
* @param request request
|
||||
* @return effect
|
||||
*/
|
||||
Integer updateDataAlias(DataAliasUpdateRequest request);
|
||||
|
||||
/**
|
||||
* 查询数据别名
|
||||
*
|
||||
* @param userId userId
|
||||
* @param type type
|
||||
* @param relId relId
|
||||
* @return aliasName
|
||||
*/
|
||||
String getDataAlias(Long userId, String type, Long relId);
|
||||
|
||||
/**
|
||||
* 查询数据别名
|
||||
*
|
||||
* @param userId userId
|
||||
* @param type type
|
||||
* @return relId:aliasName
|
||||
*/
|
||||
Map<Long, String> getDataAlias(Long userId, String type);
|
||||
|
||||
}
|
||||
@@ -64,6 +64,27 @@ public interface DataExtraService {
|
||||
*/
|
||||
Map<Long, String> getExtraItemValues(DataExtraQueryRequest request);
|
||||
|
||||
/**
|
||||
* 查询额外配置项 (查询缓存)
|
||||
*
|
||||
* @param userId userId
|
||||
* @param type type
|
||||
* @param item item
|
||||
* @param relId relId
|
||||
* @return value
|
||||
*/
|
||||
String getExtraItemValueByCache(Long userId, String type, String item, Long relId);
|
||||
|
||||
/**
|
||||
* 查询额外配置项 (查询缓存)
|
||||
*
|
||||
* @param userId userId
|
||||
* @param type type
|
||||
* @param item item
|
||||
* @return relId:value
|
||||
*/
|
||||
Map<Long, String> getExtraItemValuesByCache(Long userId, String type, String item);
|
||||
|
||||
/**
|
||||
* 查询额外配置
|
||||
*
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
package com.orion.ops.module.infra.service.impl;
|
||||
|
||||
import com.orion.lang.utils.Refs;
|
||||
import com.orion.lang.utils.collect.Maps;
|
||||
import com.orion.ops.framework.redis.core.utils.RedisMaps;
|
||||
import com.orion.ops.framework.redis.core.utils.barrier.CacheBarriers;
|
||||
import com.orion.ops.module.infra.constant.DataExtraItems;
|
||||
import com.orion.ops.module.infra.define.cache.DataExtraCacheKeyDefine;
|
||||
import com.orion.ops.module.infra.entity.request.data.DataAliasUpdateRequest;
|
||||
import com.orion.ops.module.infra.entity.request.data.DataExtraQueryRequest;
|
||||
import com.orion.ops.module.infra.entity.request.data.DataExtraSetRequest;
|
||||
import com.orion.ops.module.infra.service.DataAliasService;
|
||||
import com.orion.ops.module.infra.service.DataExtraService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* 数据别名 服务实现类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023-12-18 17:37
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class DataAliasServiceImpl implements DataAliasService {
|
||||
|
||||
@Resource
|
||||
private DataExtraService dataExtraService;
|
||||
|
||||
@Override
|
||||
public Integer updateDataAlias(DataAliasUpdateRequest request) {
|
||||
Long userId = request.getUserId();
|
||||
String type = request.getType();
|
||||
// 更新
|
||||
DataExtraSetRequest update = new DataExtraSetRequest();
|
||||
update.setUserId(userId);
|
||||
update.setRelId(request.getRelId());
|
||||
update.setType(type);
|
||||
update.setItem(DataExtraItems.ALIAS);
|
||||
update.setValue(Refs.json(request.getAlias()));
|
||||
Integer effect = dataExtraService.setExtraItem(update);
|
||||
// 删除缓存
|
||||
RedisMaps.delete(DataExtraCacheKeyDefine.DATA_ALIAS.format(userId, type));
|
||||
return effect;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDataAlias(Long userId, String type, Long relId) {
|
||||
return this.getDataAlias(userId, type).get(relId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Long, String> getDataAlias(Long userId, String type) {
|
||||
// 查询缓存
|
||||
String key = DataExtraCacheKeyDefine.DATA_ALIAS.format(userId, type);
|
||||
Map<String, String> entities = RedisMaps.entities(key);
|
||||
if (Maps.isEmpty(entities)) {
|
||||
// 查询数据库
|
||||
DataExtraQueryRequest request = DataExtraQueryRequest.builder()
|
||||
.userId(userId)
|
||||
.type(type)
|
||||
.item(DataExtraItems.ALIAS)
|
||||
.build();
|
||||
Map<Long, String> extras = dataExtraService.getExtraItemValues(request);
|
||||
entities = Maps.map(extras, String::valueOf, Refs::unrefToString);
|
||||
// 设置屏障 防止穿透
|
||||
CacheBarriers.MAP.check(entities);
|
||||
// 设置缓存
|
||||
RedisMaps.putAll(key, DataExtraCacheKeyDefine.DATA_ALIAS, entities);
|
||||
}
|
||||
// 删除屏障
|
||||
CacheBarriers.MAP.remove(entities);
|
||||
// 转换
|
||||
return Maps.map(entities, Long::valueOf, Function.identity());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,9 +2,14 @@ package com.orion.ops.module.infra.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.orion.lang.function.Functions;
|
||||
import com.orion.lang.utils.collect.Lists;
|
||||
import com.orion.lang.utils.collect.Maps;
|
||||
import com.orion.ops.framework.common.constant.Const;
|
||||
import com.orion.ops.framework.mybatis.core.query.ThenLambdaWrapper;
|
||||
import com.orion.ops.framework.redis.core.utils.RedisMaps;
|
||||
import com.orion.ops.framework.redis.core.utils.barrier.CacheBarriers;
|
||||
import com.orion.ops.module.infra.dao.DataExtraDAO;
|
||||
import com.orion.ops.module.infra.define.cache.DataExtraCacheKeyDefine;
|
||||
import com.orion.ops.module.infra.entity.domain.DataExtraDO;
|
||||
import com.orion.ops.module.infra.entity.request.data.DataExtraQueryRequest;
|
||||
import com.orion.ops.module.infra.entity.request.data.DataExtraSetRequest;
|
||||
@@ -15,6 +20,7 @@ import org.springframework.stereotype.Service;
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -62,15 +68,29 @@ public class DataExtraServiceImpl implements DataExtraService {
|
||||
insert.setItem(request.getItem());
|
||||
insert.setValue(request.getValue());
|
||||
dataExtraDAO.insert(insert);
|
||||
// 删除缓存
|
||||
RedisMaps.delete(DataExtraCacheKeyDefine.DATA_EXTRA.format(request.getUserId(), request.getType(), request.getItem()));
|
||||
return insert.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer updateExtraValue(Long id, String value) {
|
||||
// 查询数据
|
||||
DataExtraDO data = this.getCacheSelectWrapper()
|
||||
.eq(DataExtraDO::getId, id)
|
||||
.then()
|
||||
.get();
|
||||
if (data == null) {
|
||||
return Const.N_0;
|
||||
}
|
||||
DataExtraDO update = new DataExtraDO();
|
||||
update.setId(id);
|
||||
update.setValue(value);
|
||||
return dataExtraDAO.updateById(update);
|
||||
// 更新
|
||||
int effect = dataExtraDAO.updateById(update);
|
||||
// 删除缓存
|
||||
RedisMaps.delete(DataExtraCacheKeyDefine.DATA_EXTRA.format(data.getUserId(), data.getType(), data.getItem()));
|
||||
return effect;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -78,8 +98,16 @@ public class DataExtraServiceImpl implements DataExtraService {
|
||||
if (Maps.isEmpty(map)) {
|
||||
return;
|
||||
}
|
||||
// 查询数据
|
||||
List<DataExtraDO> list = this.getCacheSelectWrapper()
|
||||
.in(DataExtraDO::getId, map.keySet())
|
||||
.then()
|
||||
.list();
|
||||
if (list.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// 批量更新
|
||||
List<DataExtraDO> list = map.entrySet()
|
||||
List<DataExtraDO> update = map.entrySet()
|
||||
.stream()
|
||||
.map(s -> {
|
||||
DataExtraDO extra = new DataExtraDO();
|
||||
@@ -87,7 +115,9 @@ public class DataExtraServiceImpl implements DataExtraService {
|
||||
extra.setValue(s.getValue());
|
||||
return extra;
|
||||
}).collect(Collectors.toList());
|
||||
dataExtraDAO.updateBatch(list);
|
||||
dataExtraDAO.updateBatch(update);
|
||||
// 删除缓存
|
||||
this.deleteCache(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -110,6 +140,36 @@ public class DataExtraServiceImpl implements DataExtraService {
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExtraItemValueByCache(Long userId, String type, String item, Long relId) {
|
||||
return this.getExtraItemValuesByCache(userId, type, item).get(relId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Long, String> getExtraItemValuesByCache(Long userId, String type, String item) {
|
||||
// 查询缓存
|
||||
String key = DataExtraCacheKeyDefine.DATA_EXTRA.format(userId, type, item);
|
||||
Map<String, String> entities = RedisMaps.entities(key);
|
||||
if (Maps.isEmpty(entities)) {
|
||||
// 查询数据库
|
||||
DataExtraQueryRequest request = DataExtraQueryRequest.builder()
|
||||
.userId(userId)
|
||||
.type(type)
|
||||
.item(item)
|
||||
.build();
|
||||
Map<Long, String> extras = this.getExtraItemValues(request);
|
||||
entities = Maps.map(extras, String::valueOf, String::valueOf);
|
||||
// 设置屏障 防止穿透
|
||||
CacheBarriers.MAP.check(entities);
|
||||
// 设置缓存
|
||||
RedisMaps.putAll(key, DataExtraCacheKeyDefine.DATA_EXTRA, entities);
|
||||
}
|
||||
// 删除屏障
|
||||
CacheBarriers.MAP.remove(entities);
|
||||
// 转换
|
||||
return Maps.map(entities, Long::valueOf, Function.identity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataExtraDO getExtraItem(DataExtraQueryRequest request) {
|
||||
return dataExtraDAO.of()
|
||||
@@ -126,12 +186,62 @@ public class DataExtraServiceImpl implements DataExtraService {
|
||||
|
||||
@Override
|
||||
public Integer deleteByUserId(Long userId) {
|
||||
return dataExtraDAO.deleteByUserId(userId);
|
||||
List<DataExtraDO> list = this.getCacheSelectWrapper()
|
||||
.eq(DataExtraDO::getUserId, userId)
|
||||
.then()
|
||||
.list();
|
||||
if (list.isEmpty()) {
|
||||
return Const.N_0;
|
||||
}
|
||||
// 删除数据
|
||||
int effect = dataExtraDAO.deleteByUserId(userId);
|
||||
// 删除缓存
|
||||
this.deleteCache(list);
|
||||
return effect;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer deleteByRelId(String type, Long relId) {
|
||||
return dataExtraDAO.deleteByRelId(type, relId);
|
||||
List<DataExtraDO> list = this.getCacheSelectWrapper()
|
||||
.eq(DataExtraDO::getType, type)
|
||||
.eq(DataExtraDO::getRelId, relId)
|
||||
.then()
|
||||
.list();
|
||||
if (list.isEmpty()) {
|
||||
return Const.N_0;
|
||||
}
|
||||
// 删除数据
|
||||
int effect = dataExtraDAO.deleteByRelId(type, relId);
|
||||
// 删除缓存
|
||||
this.deleteCache(list);
|
||||
return effect;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取查询缓存参数 wrapper 不查询 longtext 加速查询
|
||||
*
|
||||
* @return wrapper
|
||||
*/
|
||||
private ThenLambdaWrapper<DataExtraDO> getCacheSelectWrapper() {
|
||||
return dataExtraDAO.of()
|
||||
.createWrapper()
|
||||
.select(DataExtraDO::getId, DataExtraDO::getUserId, DataExtraDO::getType, DataExtraDO::getItem);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
*
|
||||
* @param list list
|
||||
*/
|
||||
private void deleteCache(List<DataExtraDO> list) {
|
||||
if (Lists.isEmpty(list)) {
|
||||
return;
|
||||
}
|
||||
List<String> keys = list.stream()
|
||||
.map(s -> DataExtraCacheKeyDefine.DATA_EXTRA.format(s.getUserId(), s.getType(), s.getItem()))
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
RedisMaps.delete(keys);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
VITE_API_BASE_URL= 'http://127.0.0.1:9200/orion/api'
|
||||
VITE_WS_BASE_URL= 'ws://127.0.0.1:9200/orion/keep-alive'
|
||||
VITE_APP_VERSION= '1.0.0-beta.1'
|
||||
VITE_APP_VERSION= '1.0.0'
|
||||
VITE_SFTP_PREVIEW_MB= 2
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
VITE_API_BASE_URL= '/orion/api'
|
||||
VITE_WS_BASE_URL= '/orion/keep-alive'
|
||||
VITE_APP_VERSION= '1.0.0-beta.1'
|
||||
VITE_APP_VERSION= '1.0.0'
|
||||
VITE_SFTP_PREVIEW_MB= 2
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "orion-ops-pro-ui",
|
||||
"description": "Orion Ops Pro for Vue",
|
||||
"version": "1.0.0-beta.1",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"author": "Jiahang Li",
|
||||
"license": "Apache 2.0",
|
||||
|
||||
@@ -36,7 +36,7 @@ export interface HostQueryRequest extends Pagination {
|
||||
/**
|
||||
* 主机查询响应
|
||||
*/
|
||||
export interface HostQueryResponse extends TableData {
|
||||
export interface HostQueryResponse extends TableData, HostQueryResponseExtra {
|
||||
id: number;
|
||||
name: string;
|
||||
code: string;
|
||||
@@ -47,9 +47,15 @@ export interface HostQueryResponse extends TableData {
|
||||
updater: string;
|
||||
favorite: boolean;
|
||||
alias: string;
|
||||
color: string;
|
||||
tags: Array<{ id: number, name: string }>;
|
||||
groupIdList: Array<number>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 主机操作拓展
|
||||
*/
|
||||
export interface HostQueryResponseExtra {
|
||||
editable: boolean;
|
||||
loading: boolean;
|
||||
modCount: number;
|
||||
|
||||
@@ -160,7 +160,7 @@
|
||||
</li>
|
||||
<!-- 用户信息 -->
|
||||
<li>
|
||||
<a-dropdown trigger="click">
|
||||
<a-dropdown trigger="click" position="br">
|
||||
<!-- 头像 -->
|
||||
<a-avatar draggable="false"
|
||||
:size="32"
|
||||
@@ -256,10 +256,10 @@
|
||||
const localeRef = ref();
|
||||
|
||||
// 打开应用设置
|
||||
const openAppSetting = inject<() => void>(openAppSettingKey);
|
||||
const openAppSetting = inject(openAppSettingKey) as () => void;
|
||||
|
||||
// 注入收缩菜单
|
||||
const toggleDrawerMenu = inject<() => void>(toggleDrawerMenuKey);
|
||||
const toggleDrawerMenu = inject(toggleDrawerMenuKey) as () => void;
|
||||
|
||||
// 切换主题
|
||||
const handleToggleTheme = () => {
|
||||
|
||||
@@ -157,6 +157,7 @@ export default defineStore('terminal', {
|
||||
title: `(${nextSeq}) ${record.alias || record.name}`,
|
||||
hostId: record.id,
|
||||
address: record.address,
|
||||
color: record.color,
|
||||
icon: session.icon,
|
||||
type: session.type
|
||||
});
|
||||
|
||||
@@ -7,7 +7,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': '让每一次操作都安全可控可追溯',
|
||||
|
||||
@@ -170,7 +170,6 @@
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
color: var(--color-header-text-1);
|
||||
background: var(--color-bg-header-tabs);
|
||||
|
||||
&:hover {
|
||||
color: var(--color-header-text-2);
|
||||
@@ -232,11 +231,10 @@
|
||||
}
|
||||
|
||||
:deep(.arco-tabs-tab-active) {
|
||||
background: var(--color-bg-header-tabs-active);
|
||||
color: var(--color-header-text-2) !important;
|
||||
|
||||
.arco-tabs-tab-title {
|
||||
background: var(--color-bg-header-tabs-active);
|
||||
background: var(--color-bg-header-tabs-active) !important;
|
||||
}
|
||||
|
||||
&:hover::after {
|
||||
|
||||
@@ -21,7 +21,8 @@
|
||||
:key="tab.key">
|
||||
<!-- 标题 -->
|
||||
<template #title>
|
||||
<span class="tab-title-wrapper">
|
||||
<span class="tab-title-wrapper"
|
||||
:style="{ 'border-bottom': `2px ${tab.color || 'transparent'} solid` }">
|
||||
<span class="tab-title-icon">
|
||||
<component :is="tab.icon" />
|
||||
</span>
|
||||
@@ -101,8 +102,12 @@
|
||||
}
|
||||
|
||||
.tab-title-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 11px 18px 9px 14px;
|
||||
margin: 0 2px;
|
||||
|
||||
.tab-title-icon {
|
||||
font-size: 16px;
|
||||
@@ -214,13 +219,11 @@
|
||||
}
|
||||
|
||||
.arco-tabs-tab-title {
|
||||
padding: 11px 18px 7px 14px;
|
||||
background: var(--color-bg-panel-tabs);
|
||||
font-size: 14px;
|
||||
height: var(--panel-nav-height);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 4px transparent solid;
|
||||
|
||||
&::before {
|
||||
display: none;
|
||||
@@ -253,7 +256,7 @@
|
||||
}
|
||||
|
||||
:deep(.arco-tabs-tab-active) {
|
||||
background: var(--color-bg-panel-tabs-active);
|
||||
background: var(--color-bg-panel-tabs-active) !important;
|
||||
color: var(--color-panel-text-2) !important;
|
||||
|
||||
.arco-tabs-tab-title {
|
||||
|
||||
@@ -137,13 +137,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</a-tooltip>
|
||||
<!-- 连接设置 -->
|
||||
<!-- 主机设置 -->
|
||||
<a-tooltip position="top"
|
||||
:mini="true"
|
||||
:auto-fix-position="false"
|
||||
content-class="terminal-tooltip-content"
|
||||
arrow-class="terminal-tooltip-content"
|
||||
content="连接设置">
|
||||
content="主机设置">
|
||||
<div class="terminal-sidebar-icon-wrapper">
|
||||
<div class="terminal-sidebar-icon" @click="openSetting(item)">
|
||||
<icon-settings />
|
||||
@@ -185,7 +185,7 @@
|
||||
import { dataColor } from '@/utils';
|
||||
import { tagColor } from '@/views/asset/host-list/types/const';
|
||||
import { updateHostAlias } from '@/api/asset/host-extra';
|
||||
import { openSshSettingModalKey, PanelSessionType } from '../../types/terminal.const';
|
||||
import { openSettingModalKey, PanelSessionType } from '../../types/terminal.const';
|
||||
import { useTerminalStore } from '@/store';
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -231,7 +231,7 @@
|
||||
};
|
||||
|
||||
// 打开配置
|
||||
const openSetting = inject(openSshSettingModalKey) as (record: HostQueryResponse) => void;
|
||||
const openSetting = inject(openSettingModalKey) as (record: HostQueryResponse) => void;
|
||||
|
||||
// 设置收藏
|
||||
const setFavorite = async (item: HostQueryResponse) => {
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
class="list-view-container"
|
||||
:hostList="hostList"
|
||||
empty-value="暂无连接记录, 快去体验吧!" />
|
||||
<!-- 修改主机设置模态框 -->
|
||||
<ssh-extra-modal ref="sshModal" />
|
||||
<!-- 主机设置模态框 -->
|
||||
<host-setting-modal ref="settingModal" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -34,12 +34,12 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, provide, ref, watch } from 'vue';
|
||||
import { NewConnectionType, openSshSettingModalKey } from '../../types/terminal.const';
|
||||
import { NewConnectionType, openSettingModalKey } from '../../types/terminal.const';
|
||||
import { AuthorizedHostQueryResponse } from '@/api/asset/asset-authorized-data';
|
||||
import { HostQueryResponse } from '@/api/asset/host';
|
||||
import HostGroupView from './host-group-view.vue';
|
||||
import HostListView from './host-list-view.vue';
|
||||
import SshExtraModal from '../setting/ssh-extra-modal.vue';
|
||||
import HostSettingModal from '../setting/extra/host-setting-modal.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
hosts: AuthorizedHostQueryResponse,
|
||||
@@ -54,11 +54,11 @@
|
||||
? props.hosts.groupTree[0].key
|
||||
: 0
|
||||
);
|
||||
const sshModal = ref();
|
||||
const settingModal = ref();
|
||||
|
||||
// 暴露打开 ssh 配置模态框
|
||||
provide(openSshSettingModalKey, (record: any) => {
|
||||
sshModal.value?.open(record);
|
||||
provide(openSettingModalKey, (record: any) => {
|
||||
settingModal.value?.open(record);
|
||||
});
|
||||
|
||||
// 主机数据处理
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-alert class="mb16">刷新页面后生效</a-alert>
|
||||
<!-- 颜色选择 -->
|
||||
<div class="color-setting-container">
|
||||
<!-- 透明色 -->
|
||||
<div class="color-wrapper"
|
||||
:style="{ '--color': 'transparent' }"
|
||||
@click="clickColor('')">
|
||||
<div class="color-item">
|
||||
<span class="color-item-cancel">
|
||||
<icon-stop />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 其他颜色 -->
|
||||
<template v-for="color in toOptions(terminalTabColorKey)">
|
||||
<div class="color-wrapper"
|
||||
:class="[formModel.color === color.value ? 'selected-color' : '']"
|
||||
:style="{ '--color': `${color.value}` }"
|
||||
@click="clickColor(color.value as string)">
|
||||
<div class="color-item">
|
||||
<div class="color-item-dot" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'colorSettingForm'
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { ColorExtraSettingModel } from '../../../types/terminal.type';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { terminalTabColorKey } from '../../../types/terminal.const';
|
||||
import { getHostExtraItem } from '@/api/asset/host-extra';
|
||||
import { useDictStore } from '@/store';
|
||||
|
||||
const props = defineProps<{
|
||||
hostId: number,
|
||||
item: string
|
||||
}>();
|
||||
|
||||
const { toOptions } = useDictStore();
|
||||
|
||||
const formModel = ref<ColorExtraSettingModel>({
|
||||
color: ''
|
||||
});
|
||||
|
||||
// 渲染表单
|
||||
const renderForm = async () => {
|
||||
const { data } = await getHostExtraItem<ColorExtraSettingModel>({ hostId: props.hostId, item: props.item });
|
||||
formModel.value = data;
|
||||
};
|
||||
|
||||
// 设置颜色
|
||||
const clickColor = (color: string) => {
|
||||
formModel.value.color = color;
|
||||
};
|
||||
|
||||
// 获取值
|
||||
const getValue = async () => {
|
||||
return JSON.stringify(formModel.value);
|
||||
};
|
||||
|
||||
defineExpose({ getValue });
|
||||
|
||||
onMounted(renderForm);
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.color-setting-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.color-wrapper {
|
||||
margin-right: 8px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all .3s ease-in-out;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
|
||||
&:hover {
|
||||
background: var(--color-fill-3);
|
||||
}
|
||||
|
||||
.color-item {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&-dot {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 100%;
|
||||
background: var(--color);
|
||||
}
|
||||
|
||||
&-cancel {
|
||||
color: var(--color-content-text-1);
|
||||
font-size: 22px;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.color-wrapper.selected-color {
|
||||
|
||||
.color-item {
|
||||
border: 2px solid var(--color);
|
||||
|
||||
&-dot {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
box-sizing: border-box;
|
||||
background: var(--color);
|
||||
padding: 4px;
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,120 @@
|
||||
<template>
|
||||
<a-modal v-model:visible="visible"
|
||||
title-align="start"
|
||||
:title="title"
|
||||
:top="180"
|
||||
:width="600"
|
||||
:align-center="false"
|
||||
:draggable="true"
|
||||
:mask-closable="false"
|
||||
:unmount-on-close="true"
|
||||
ok-text="保存当前页"
|
||||
cancel-text="关闭"
|
||||
:ok-button-props="{ disabled: loading }"
|
||||
:cancel-button-props="{ disabled: loading }"
|
||||
:on-before-ok="handlerOk"
|
||||
@close="handleClose">
|
||||
<a-spin class="full" :loading="loading">
|
||||
<a-tabs v-model:active-key="activeItem"
|
||||
position="left"
|
||||
type="rounded"
|
||||
:lazy-load="true">
|
||||
<!-- SSH 配置 -->
|
||||
<a-tab-pane :key="ExtraSettingItems.SSH" title="SSH 配置">
|
||||
<ssh-setting-form ref="sshForm"
|
||||
:host-id="hostId as number"
|
||||
:item="ExtraSettingItems.SSH" />
|
||||
</a-tab-pane>
|
||||
<!-- 标签颜色 -->
|
||||
<a-tab-pane :key="ExtraSettingItems.COLOR" title="标签颜色">
|
||||
<color-setting-form ref="colorForm"
|
||||
:host-id="hostId as number"
|
||||
:item="ExtraSettingItems.COLOR" />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-spin>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'hostSettingModal'
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { HostQueryResponse } from '@/api/asset/host';
|
||||
import { ref } from 'vue';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import useVisible from '@/hooks/visible';
|
||||
import { ExtraSettingItems } from '../../../types/terminal.const';
|
||||
import { updateHostExtra } from '@/api/asset/host-extra';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import SshSettingForm from './ssh-setting-form.vue';
|
||||
import ColorSettingForm from './color-setting-form.vue';
|
||||
|
||||
const { visible, setVisible } = useVisible();
|
||||
const { loading, setLoading } = useLoading();
|
||||
|
||||
const activeItem = ref<string>(ExtraSettingItems.SSH);
|
||||
const title = ref<string>();
|
||||
const hostId = ref<number>();
|
||||
const sshForm = ref();
|
||||
const colorForm = ref();
|
||||
|
||||
// 打开配置
|
||||
const open = (record: HostQueryResponse) => {
|
||||
hostId.value = record.id;
|
||||
title.value = record.alias || `${record.name} (${record.code})`;
|
||||
setVisible(true);
|
||||
};
|
||||
|
||||
defineExpose({ open });
|
||||
|
||||
// 确定
|
||||
const handlerOk = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
let value;
|
||||
if (activeItem.value === ExtraSettingItems.SSH) {
|
||||
// SSH 配置
|
||||
value = await sshForm.value.getValue();
|
||||
} else if (activeItem.value === ExtraSettingItems.COLOR) {
|
||||
// 颜色配置
|
||||
value = await colorForm.value.getValue();
|
||||
}
|
||||
if (!value) {
|
||||
return false;
|
||||
}
|
||||
// 保存
|
||||
await updateHostExtra({
|
||||
hostId: hostId.value,
|
||||
item: activeItem.value,
|
||||
extra: value as string
|
||||
});
|
||||
Message.success('保存成功');
|
||||
} catch (e) {
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// 关闭
|
||||
const handleClose = () => {
|
||||
handlerClear();
|
||||
};
|
||||
|
||||
// 清空
|
||||
const handlerClear = () => {
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
:deep(.arco-tabs-pane) {
|
||||
border-left: 1px var(--color-neutral-3) solid;
|
||||
padding-left: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<a-form :model="formModel"
|
||||
ref="formRef"
|
||||
label-align="right"
|
||||
:style="{ width: '460px' }"
|
||||
:label-col-props="{ span: 6 }"
|
||||
:wrapper-col-props="{ span: 18 }"
|
||||
:rules="{}">
|
||||
<!-- 验证方式 -->
|
||||
<a-form-item field="authType" label="验证方式">
|
||||
<a-radio-group type="button"
|
||||
v-model="formModel.authType"
|
||||
:options="toRadioOptions(extraSshAuthTypeKey)" />
|
||||
</a-form-item>
|
||||
<!-- 用户名 -->
|
||||
<a-form-item v-if="formModel.authType === ExtraSshAuthType.CUSTOM_KEY"
|
||||
field="username"
|
||||
label="用户名">
|
||||
<a-input v-model="formModel.username" placeholder="请输入用户名" />
|
||||
</a-form-item>
|
||||
<!-- 主机秘钥 -->
|
||||
<a-form-item v-if="formModel.authType === ExtraSshAuthType.CUSTOM_KEY"
|
||||
field="keyId"
|
||||
label="主机秘钥"
|
||||
:rules="{ required: true, message: '请选择主机秘钥' }">
|
||||
<host-key-selector v-model="formModel.keyId"
|
||||
:authorized="true" />
|
||||
</a-form-item>
|
||||
<!-- 主机身份 -->
|
||||
<a-form-item v-if="formModel.authType === ExtraSshAuthType.CUSTOM_IDENTITY"
|
||||
field="identityId"
|
||||
label="主机身份"
|
||||
:rules="{ required: true, message: '请选择主机身份' }">
|
||||
<host-identity-selector v-model="formModel.identityId"
|
||||
:authorized="true" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'sshSettingForm'
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { SshExtraSettingModel } from '../../../types/terminal.type';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { getHostExtraItem } from '@/api/asset/host-extra';
|
||||
import { ExtraSshAuthType, extraSshAuthTypeKey } from '../../../types/terminal.const';
|
||||
import { useDictStore } from '@/store';
|
||||
import HostKeySelector from '@/components/asset/host-key/host-key-selector.vue';
|
||||
import HostIdentitySelector from '@/components/asset/host-identity/host-identity-selector.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
hostId: number,
|
||||
item: string
|
||||
}>();
|
||||
|
||||
const { toRadioOptions } = useDictStore();
|
||||
|
||||
const formRef = ref();
|
||||
const formModel = ref<SshExtraSettingModel>({
|
||||
authType: ExtraSshAuthType.DEFAULT
|
||||
});
|
||||
|
||||
// 渲染表单
|
||||
const renderForm = async () => {
|
||||
const { data } = await getHostExtraItem<SshExtraSettingModel>({ hostId: props.hostId, item: props.item });
|
||||
formModel.value = data;
|
||||
};
|
||||
|
||||
// 获取值
|
||||
const getValue = async () => {
|
||||
// 验证参数
|
||||
const error = await formRef.value.validate();
|
||||
if (error) {
|
||||
return false;
|
||||
}
|
||||
return JSON.stringify(formModel.value);
|
||||
};
|
||||
|
||||
defineExpose({ getValue });
|
||||
|
||||
onMounted(renderForm);
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,146 +0,0 @@
|
||||
<template>
|
||||
<a-modal v-model:visible="visible"
|
||||
body-class="modal-form"
|
||||
title-align="start"
|
||||
:title="title"
|
||||
:top="80"
|
||||
:align-center="false"
|
||||
:draggable="true"
|
||||
:mask-closable="false"
|
||||
:unmount-on-close="true"
|
||||
ok-text="保存"
|
||||
:ok-button-props="{ disabled: loading }"
|
||||
:cancel-button-props="{ disabled: loading }"
|
||||
:on-before-ok="handlerOk"
|
||||
@close="handleClose">
|
||||
<a-spin class="full" :loading="loading">
|
||||
<a-form :model="formModel"
|
||||
ref="formRef"
|
||||
label-align="right"
|
||||
:style="{ width: '460px' }"
|
||||
:label-col-props="{ span: 6 }"
|
||||
:wrapper-col-props="{ span: 18 }"
|
||||
:rules="{}">
|
||||
<!-- 验证方式 -->
|
||||
<a-form-item field="authType" label="验证方式">
|
||||
<a-radio-group type="button"
|
||||
v-model="formModel.authType"
|
||||
:options="toRadioOptions(extraSshAuthTypeKey)" />
|
||||
</a-form-item>
|
||||
<!-- 用户名 -->
|
||||
<a-form-item v-if="formModel.authType === ExtraSshAuthType.CUSTOM_KEY"
|
||||
field="username"
|
||||
label="用户名">
|
||||
<a-input v-model="formModel.username" placeholder="请输入用户名" />
|
||||
</a-form-item>
|
||||
<!-- 主机秘钥 -->
|
||||
<a-form-item v-if="formModel.authType === ExtraSshAuthType.CUSTOM_KEY"
|
||||
field="keyId"
|
||||
label="主机秘钥"
|
||||
:rules="{ required: true, message: '请选择主机秘钥' }">
|
||||
<host-key-selector v-model="formModel.keyId"
|
||||
:authorized="true" />
|
||||
</a-form-item>
|
||||
<!-- 主机身份 -->
|
||||
<a-form-item v-if="formModel.authType === ExtraSshAuthType.CUSTOM_IDENTITY"
|
||||
field="identityId"
|
||||
label="主机身份"
|
||||
:rules="{ required: true, message: '请选择主机身份' }">
|
||||
<host-identity-selector v-model="formModel.identityId"
|
||||
:authorized="true" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'sshExtraModal'
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { HostQueryResponse } from '@/api/asset/host';
|
||||
import type { SshExtraModel } from '../../types/terminal.type';
|
||||
import { ref } from 'vue';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import useVisible from '@/hooks/visible';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { getHostExtraItem, updateHostExtra } from '@/api/asset/host-extra';
|
||||
import { ExtraSshAuthType, extraSshAuthTypeKey } from '../../types/terminal.const';
|
||||
import { useDictStore } from '@/store';
|
||||
import HostIdentitySelector from '@/components/asset/host-identity/host-identity-selector.vue';
|
||||
import HostKeySelector from '@/components/asset/host-key/host-key-selector.vue';
|
||||
|
||||
const { toRadioOptions } = useDictStore();
|
||||
const { visible, setVisible } = useVisible();
|
||||
const { loading, setLoading } = useLoading();
|
||||
|
||||
const title = ref<string>();
|
||||
const hostId = ref<number>();
|
||||
const formRef = ref();
|
||||
const formModel = ref<SshExtraModel>({});
|
||||
|
||||
// 打开配置
|
||||
const open = (record: HostQueryResponse) => {
|
||||
hostId.value = record.id;
|
||||
title.value = record.alias || `${record.name} (${record.code})`;
|
||||
renderForm();
|
||||
setVisible(true);
|
||||
};
|
||||
|
||||
defineExpose({ open });
|
||||
|
||||
// 渲染表单
|
||||
const renderForm = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const { data } = await getHostExtraItem<SshExtraModel>({ hostId: hostId.value, item: 'ssh' });
|
||||
formModel.value = data;
|
||||
} catch (e) {
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 确定
|
||||
const handlerOk = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
// 验证参数
|
||||
const error = await formRef.value.validate();
|
||||
if (error) {
|
||||
return false;
|
||||
}
|
||||
// 修改
|
||||
await updateHostExtra({
|
||||
hostId: hostId.value,
|
||||
item: 'ssh',
|
||||
extra: JSON.stringify(formModel.value)
|
||||
});
|
||||
Message.success('保存成功');
|
||||
// 清空
|
||||
handlerClear();
|
||||
} catch (e) {
|
||||
return false;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 关闭
|
||||
const handleClose = () => {
|
||||
handlerClear();
|
||||
};
|
||||
|
||||
// 清空
|
||||
const handlerClear = () => {
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
</style>
|
||||
@@ -42,6 +42,12 @@ export const NewConnectionType = {
|
||||
LATEST: 'latest'
|
||||
};
|
||||
|
||||
// 主机额外配置项
|
||||
export const ExtraSettingItems = {
|
||||
SSH: 'ssh',
|
||||
COLOR: 'color'
|
||||
};
|
||||
|
||||
// 主机额外配置 ssh 认证方式
|
||||
export const ExtraSshAuthType = {
|
||||
// 使用默认认证方式
|
||||
@@ -318,8 +324,8 @@ export const TransferReceiverType = {
|
||||
DOWNLOAD_ERROR: 'downloadError',
|
||||
};
|
||||
|
||||
// 打开 sshSettingModal key
|
||||
export const openSshSettingModalKey = Symbol();
|
||||
// 打开 settingModal key
|
||||
export const openSettingModalKey = Symbol();
|
||||
|
||||
// 打开 sftpCreateModal key
|
||||
export const openSftpCreateModalKey = Symbol();
|
||||
@@ -360,10 +366,14 @@ export const connectStatusKey = 'terminalConnectStatus';
|
||||
// 终端类型
|
||||
export const terminalEmulationTypeKey = 'terminalEmulationType';
|
||||
|
||||
// 终端标签颜色
|
||||
export const terminalTabColorKey = 'terminalTabColor';
|
||||
|
||||
// 加载的字典值
|
||||
export const dictKeys = [
|
||||
fontFamilyKey, fontSizeKey,
|
||||
fontWeightKey, cursorStyleKey,
|
||||
newConnectionTypeKey, extraSshAuthTypeKey,
|
||||
connectStatusKey, terminalEmulationTypeKey
|
||||
connectStatusKey, terminalEmulationTypeKey,
|
||||
terminalTabColorKey
|
||||
];
|
||||
|
||||
@@ -23,6 +23,7 @@ export interface TerminalPanelTabItem extends TerminalTabItem {
|
||||
hostId: number;
|
||||
address: string;
|
||||
type: string;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
// sidebar 操作类型
|
||||
@@ -59,13 +60,18 @@ export interface ShortcutKeyItem {
|
||||
}
|
||||
|
||||
// ssh 额外配置
|
||||
export interface SshExtraModel {
|
||||
export interface SshExtraSettingModel {
|
||||
authType?: string;
|
||||
username?: string;
|
||||
keyId?: number;
|
||||
identityId?: number;
|
||||
}
|
||||
|
||||
// 颜色 额外配置
|
||||
export interface ColorExtraSettingModel {
|
||||
color: string;
|
||||
}
|
||||
|
||||
// session tab
|
||||
export interface PanelSessionTab {
|
||||
type: string;
|
||||
|
||||
2
pom.xml
2
pom.xml
@@ -22,7 +22,7 @@
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<revision>1.0.0-beta.1</revision>
|
||||
<revision>1.0.0</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>
|
||||
|
||||
@@ -108,6 +108,7 @@ INSERT INTO `dict_key` VALUES (27, 'hostConnectType', 'STRING', '[]', '主机连
|
||||
INSERT INTO `dict_key` VALUES (28, 'hostConnectStatus', 'STRING', '[{\"name\": \"color\", \"type\": \"COLOR\"}]', '主机连接状态', '2023-12-26 23:23:51', '2023-12-26 23:28:15', '1', '1', 0);
|
||||
INSERT INTO `dict_key` VALUES (29, 'terminalConnectStatus', 'INTEGER', '[{\"name\": \"status\", \"type\": \"STRING\"}]', '终端连接状态', '2024-01-09 00:32:00', '2024-01-09 00:32:16', '1', '1', 0);
|
||||
INSERT INTO `dict_key` VALUES (31, 'terminalEmulationType', 'STRING', '[]', '伪终端类型', '2024-01-11 23:35:01', '2024-01-11 23:35:01', '1', '1', 0);
|
||||
INSERT INTO `dict_key` VALUES (32, 'terminalTabColor', 'COLOR', '[]', '终端标签页颜色', '2024-03-01 15:01:44', '2024-03-01 15:01:44', '1', '1', 0);
|
||||
|
||||
-- 字典值
|
||||
INSERT INTO `dict_value` VALUES (3, 4, 'systemMenuType', '1', '父菜单', '{}', 10, '2023-10-26 15:58:59', '2023-10-26 15:58:59', '1', '1', 0);
|
||||
@@ -258,3 +259,13 @@ INSERT INTO `dict_value` VALUES (199, 2, 'operatorLogType', 'host-terminal:sftp-
|
||||
INSERT INTO `dict_value` VALUES (200, 2, 'operatorLogType', 'host-terminal:sftp-set-content', '修改文件内容', '{}', 80, '2024-02-23 17:54:37', '2024-02-23 17:54:37', '1', '1', 0);
|
||||
INSERT INTO `dict_value` VALUES (201, 2, 'operatorLogType', 'host-terminal:sftp-upload', '上传文件', '{}', 90, '2024-02-23 17:54:52', '2024-02-23 17:54:52', '1', '1', 0);
|
||||
INSERT INTO `dict_value` VALUES (202, 2, 'operatorLogType', 'host-terminal:sftp-download', '下载文件', '{}', 100, '2024-02-23 17:55:03', '2024-02-23 17:55:03', '1', '1', 0);
|
||||
INSERT INTO `dict_value` VALUES (203, 32, 'terminalTabColor', 'rgb(var(--red-6))', '红色', '{}', 10, '2024-03-01 15:07:41', '2024-03-01 15:07:41', '1', '1', 0);
|
||||
INSERT INTO `dict_value` VALUES (204, 32, 'terminalTabColor', 'rgb(var(--orange-6))', '橙色', '{}', 20, '2024-03-01 15:07:55', '2024-03-01 15:07:55', '1', '1', 0);
|
||||
INSERT INTO `dict_value` VALUES (205, 32, 'terminalTabColor', 'rgb(var(--yellow-6))', '黄色', '{}', 30, '2024-03-01 15:08:13', '2024-03-01 15:08:13', '1', '1', 0);
|
||||
INSERT INTO `dict_value` VALUES (206, 32, 'terminalTabColor', 'rgb(var(--green-6))', '绿色', '{}', 40, '2024-03-01 15:08:23', '2024-03-01 15:08:23', '1', '1', 0);
|
||||
INSERT INTO `dict_value` VALUES (207, 32, 'terminalTabColor', 'rgb(var(--cyan-6))', '青色', '{}', 50, '2024-03-01 15:08:46', '2024-03-01 15:08:46', '1', '1', 0);
|
||||
INSERT INTO `dict_value` VALUES (208, 32, 'terminalTabColor', 'rgb(var(--blue-6))', '浅蓝', '{}', 60, '2024-03-01 15:11:01', '2024-03-01 15:11:01', '1', '1', 0);
|
||||
INSERT INTO `dict_value` VALUES (209, 32, 'terminalTabColor', 'rgb(var(--arcoblue-6))', '蓝色', '{}', 70, '2024-03-01 15:11:11', '2024-03-01 15:11:11', '1', '1', 0);
|
||||
INSERT INTO `dict_value` VALUES (210, 32, 'terminalTabColor', 'rgb(var(--purple-6))', '紫色', '{}', 80, '2024-03-01 15:11:20', '2024-03-01 15:11:20', '1', '1', 0);
|
||||
INSERT INTO `dict_value` VALUES (211, 32, 'terminalTabColor', 'rgb(var(--pinkpurple-6))', '粉紫', '{}', 90, '2024-03-01 15:11:41', '2024-03-01 15:11:41', '1', '1', 0);
|
||||
INSERT INTO `dict_value` VALUES (213, 32, 'terminalTabColor', 'rgb(var(--gray-6))', '灰色', '{}', 100, '2024-03-01 15:12:01', '2024-03-01 15:39:34', '1', '1', 0);
|
||||
|
||||
Reference in New Issue
Block a user