Merge pull request #109 from dromara/dev

Dev
This commit is contained in:
李佳航
2025-06-06 16:39:28 +08:00
committed by GitHub
29 changed files with 217 additions and 65 deletions

View File

@@ -1,6 +1,6 @@
#/bin/bash
set -e
version=2.3.8
version=2.3.9
docker build -t orion-visor-adminer:${version} .
docker tag orion-visor-adminer:${version} registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-adminer:${version}
docker tag orion-visor-adminer:${version} registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-adminer:latest

View File

@@ -1,6 +1,6 @@
#/bin/bash
set -e
version=2.3.8
version=2.3.9
cp -r ../../sql ./sql
docker build -t orion-visor-mysql:${version} .
rm -rf ./sql

View File

@@ -1,6 +1,6 @@
#/bin/bash
set -e
version=2.3.8
version=2.3.9
docker push registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-adminer:${version}
docker push registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-mysql:${version}
docker push registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-redis:${version}

View File

@@ -1,6 +1,6 @@
#/bin/bash
set -e
version=2.3.8
version=2.3.9
docker build -t orion-visor-redis:${version} .
docker tag orion-visor-redis:${version} registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-redis:${version}
docker tag orion-visor-redis:${version} registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-redis:latest

View File

@@ -1,6 +1,6 @@
#/bin/bash
set -e
version=2.3.8
version=2.3.9
mv ../../orion-visor-launch/target/orion-visor-launch.jar ./orion-visor-launch.jar
docker build -t orion-visor-service:${version} .
rm -rf ./orion-visor-launch.jar

View File

@@ -1,6 +1,6 @@
#/bin/bash
set -e
version=2.3.8
version=2.3.9
mv ../../orion-visor-ui/dist ./dist
docker build -t orion-visor-ui:${version} .
rm -rf ./orion-visor-launch.jar

View File

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

View File

@@ -14,7 +14,7 @@
<url>https://github.com/dromara/orion-visor</url>
<properties>
<revision>2.3.8</revision>
<revision>2.3.9</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

@@ -39,9 +39,9 @@ import java.util.function.Function;
*/
public class ReplaceVersion {
private static final String TARGET_VERSION = "2.3.7";
private static final String TARGET_VERSION = "2.3.8";
private static final String REPLACE_VERSION = "2.3.8";
private static final String REPLACE_VERSION = "2.3.9";
private static final String PATH = new File("").getAbsolutePath();

View File

@@ -84,6 +84,17 @@ public class HostController {
return hostService.updateHostById(request);
}
@DemoDisableApi
@OperatorLog(HostOperatorType.CREATE)
@PostMapping("/copy")
@Operation(summary = "复制主机")
@PreAuthorize("@ss.hasPermission('asset:host:create')")
public Long copyHost(@Validated @RequestBody HostUpdateRequest request) {
Long id = request.getId();
request.setId(null);
return hostService.copyHost(id, request);
}
@DemoDisableApi
@OperatorLog(HostOperatorType.UPDATE_STATUS)
@PutMapping("/update-status")

View File

@@ -61,6 +61,8 @@ public interface HostConvert {
HostBaseVO toBase(HostDO domain);
HostCreateRequest toCreate(HostUpdateRequest request);
List<HostVO> toList(List<HostDO> domain);
List<HostBaseVO> toBaseList(List<HostDO> domain);

View File

@@ -57,6 +57,20 @@ public interface HostConfigDAO extends IMapper<HostConfigDO> {
.getOne();
}
/**
* 通过 hostId 查询
*
* @param hostId hostId
* @return config
*/
default List<HostConfigDO> selectByHostId(Long hostId) {
return this.of()
.createWrapper()
.eq(HostConfigDO::getHostId, hostId)
.then()
.list();
}
/**
* 更新配置状态
*

View File

@@ -26,6 +26,8 @@ import org.dromara.visor.common.handler.data.model.GenericsDataModel;
import org.dromara.visor.module.asset.entity.request.host.HostConfigQueryRequest;
import org.dromara.visor.module.asset.entity.request.host.HostConfigUpdateRequest;
import java.util.List;
/**
* 主机配置 服务类
*
@@ -43,6 +45,15 @@ public interface HostConfigService {
*/
Integer updateHostConfig(HostConfigUpdateRequest request);
/**
* 复制主机配置
*
* @param originId originId
* @param newId newId
* @param types types
*/
void copyHostConfig(Long originId, Long newId, List<String> types);
/**
* 获取主机配置
*

View File

@@ -75,4 +75,12 @@ public interface HostExtraService {
*/
Integer updateHostExtra(HostExtraUpdateRequest request);
/**
* 复制主机拓展信息
*
* @param originId originId
* @param newId newId
*/
void copyHostExtra(Long originId, Long newId);
}

View File

@@ -47,6 +47,15 @@ public interface HostService {
*/
Long createHost(HostCreateRequest request);
/**
* 复制主机
*
* @param originId originId
* @param request request
* @return id
*/
Long copyHost(Long originId, HostUpdateRequest request);
/**
* 通过 id 更新主机
*

View File

@@ -22,6 +22,8 @@
*/
package org.dromara.visor.module.asset.service.impl;
import cn.orionsec.kit.lang.function.Functions;
import cn.orionsec.kit.lang.utils.Strings;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.dromara.visor.common.constant.ErrorMessage;
@@ -42,6 +44,10 @@ import org.dromara.visor.module.asset.service.HostConfigService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 主机配置 服务实现类
@@ -102,6 +108,34 @@ public class HostConfigServiceImpl implements HostConfigService {
}
}
@Override
public void copyHostConfig(Long originId, Long newId, List<String> types) {
// 查询原始主机配置
Map<String, String> originHostConfigMap = hostConfigDAO.selectByHostId(originId)
.stream()
.collect(Collectors.toMap(HostConfigDO::getType,
HostConfigDO::getConfig,
Functions.right()));
// 新增
List<HostConfigDO> records = new ArrayList<>();
for (String type : types) {
// 获取原始配置
String configValue = originHostConfigMap.get(type);
if (Strings.isBlank(configValue)) {
// 获取默认值
configValue = HostTypeEnum.of(type).getDefault().serial();
}
HostConfigDO newConfig = HostConfigDO.builder()
.hostId(newId)
.type(type)
.status(EnableStatus.ENABLED.name())
.config(configValue)
.build();
records.add(newConfig);
}
hostConfigDAO.insertBatch(records);
}
@Override
public <T extends GenericsDataModel> T getHostConfig(Long hostId, String type) {
// 查询配置信息

View File

@@ -123,6 +123,29 @@ public class HostExtraServiceImpl implements HostExtraService {
return dataExtraApi.updateExtraValue(beforeExtraItem.getId(), newExtra.serial());
}
@Override
public void copyHostExtra(Long originId, Long newId) {
// 查询原始配置
DataExtraQueryDTO query = DataExtraQueryDTO.builder()
.userId(Const.SYSTEM_USER_ID)
.relId(originId)
.build();
List<DataExtraDTO> items = dataExtraApi.getExtraItems(query, DataExtraTypeEnum.HOST);
if (items.isEmpty()) {
return;
}
// 插入新配置
List<DataExtraSetDTO> newItems = items.stream()
.map(s -> DataExtraSetDTO.builder()
.userId(Const.SYSTEM_USER_ID)
.relId(newId)
.item(s.getItem())
.value(s.getValue())
.build())
.collect(Collectors.toList());
dataExtraApi.addExtraItems(newItems, DataExtraTypeEnum.HOST);
}
/**
* 检查配置项并且转为视图 (不存在则初始化默认值)
*

View File

@@ -49,10 +49,7 @@ import org.dromara.visor.module.asset.entity.vo.HostVO;
import org.dromara.visor.module.asset.enums.HostExtraItemEnum;
import org.dromara.visor.module.asset.enums.HostStatusEnum;
import org.dromara.visor.module.asset.handler.host.extra.model.HostSpecExtraModel;
import org.dromara.visor.module.asset.service.ExecJobHostService;
import org.dromara.visor.module.asset.service.ExecTemplateHostService;
import org.dromara.visor.module.asset.service.HostExtraService;
import org.dromara.visor.module.asset.service.HostService;
import org.dromara.visor.module.asset.service.*;
import org.dromara.visor.module.infra.api.DataExtraApi;
import org.dromara.visor.module.infra.api.DataGroupRelApi;
import org.dromara.visor.module.infra.api.FavoriteApi;
@@ -91,6 +88,9 @@ public class HostServiceImpl implements HostService {
@Resource
private HostConfigDAO hostConfigDAO;
@Resource
private HostConfigService hostConfigService;
@Resource
private HostExtraService hostExtraService;
@@ -138,6 +138,23 @@ public class HostServiceImpl implements HostService {
return id;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Long copyHost(Long originId, HostUpdateRequest request) {
log.info("HostService-copyHost originId: {}, request: {}", originId, JSON.toJSONString(request));
// 查询原始主机
HostDO originHost = hostDAO.selectById(originId);
Valid.notNull(originHost, ErrorMessage.HOST_ABSENT);
// 创建主机
Long newId = SpringHolder.getBean(HostService.class)
.createHost(HostConvert.MAPPER.toCreate(request));
// 复制主机额外信息
hostExtraService.copyHostExtra(originId, newId);
// 复制主机配置信息
hostConfigService.copyHostConfig(originId, newId, request.getTypes());
return newId;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Integer updateHostById(HostUpdateRequest request) {

View File

@@ -3,4 +3,4 @@ VITE_API_BASE_URL=http://127.0.0.1:9200/orion-visor/api
# websocket 路径
VITE_WS_BASE_URL=ws://127.0.0.1:9200/orion-visor/keep-alive
# 版本号
VITE_APP_VERSION=2.3.8
VITE_APP_VERSION=2.3.9

View File

@@ -3,4 +3,4 @@ VITE_API_BASE_URL=/orion-visor/api
# websocket 路径
VITE_WS_BASE_URL=/orion-visor/keep-alive
# 版本号
VITE_APP_VERSION=2.3.8
VITE_APP_VERSION=2.3.9

View File

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

View File

@@ -1,4 +1,4 @@
import type { HostSpecExtraModel, HostExtraUpdateRequest } from './host-extra';
import type { HostExtraUpdateRequest, HostSpecExtraModel } from './host-extra';
import type { TableData } from '@arco-design/web-vue';
import type { DataGrid, OrderDirection, Pagination } from '@/types/global';
import axios from 'axios';
@@ -114,6 +114,13 @@ export function updateHost(request: HostUpdateRequest) {
return axios.put('/asset/host/update', request);
}
/**
* 复制主机
*/
export function copyHost(request: HostUpdateRequest) {
return axios.post('/asset/host/copy', request);
}
/**
* 通过 id 更新主机状态
*/

View File

@@ -23,6 +23,7 @@ export interface AppInfoResponse {
*/
export interface AppReleaseResponse {
tagName: string;
releaseTime: string;
body: string;
}

View File

@@ -60,7 +60,7 @@
position="left"
type="warning"
@ok="deleteNode(node.key)">
<span v-permission="['asset:host-group:delete']"
<span v-permission="['asset:host-group:update']"
class="tree-icon"
title="删除">
<icon-delete />

View File

@@ -21,7 +21,7 @@
<host-form-info ref="infoRef"
class="form-panel"
@change-type="(ts: string[]) => types = ts"
@updated="updateHostInfo" />
@updated="onUpdateHostInfo" />
</a-tab-pane>
<!-- 规格配置 -->
<a-tab-pane v-permission="['asset:host:update']"
@@ -31,7 +31,7 @@
<host-form-spec v-if="hostId"
class="form-panel"
:hostId="hostId"
@updated="incrUpdatedCount" />
@updated="onUpdateHostSpec" />
</a-tab-pane>
<!-- SSH 配置 -->
<a-tab-pane v-permission="['asset:host:update-config']"
@@ -59,12 +59,10 @@
import { Message } from '@arco-design/web-vue';
import { useCacheStore } from '@/store';
import { HostType } from '../types/const';
import { useCounter } from '@vueuse/core';
import HostFormInfo from './host-form-info.vue';
import HostFormSsh from './host-form-ssh.vue';
import HostFormSpec from './host-form-spec.vue';
import HostFormSsh from './host-form-ssh.vue';
const { count: updatedCount, inc: incrUpdatedCount, reset: resetCounter } = useCounter();
const { visible, setVisible } = useVisible();
const activeTab = ref<string>('info');
@@ -72,6 +70,7 @@
const hostId = ref<number>();
const types = ref<string[]>([]);
const infoRef = ref();
const hostViewUpdated = ref(false);
const emits = defineEmits(['reload']);
@@ -93,7 +92,7 @@
// 打开复制
const openCopy = (id: number) => {
init('复制主机', id);
init('复制主机', undefined);
nextTick(() => {
infoRef.value.openCopy(id);
});
@@ -104,9 +103,9 @@
title.value = _title;
activeTab.value = 'info';
hostId.value = id;
hostViewUpdated.value = false;
types.value = [];
checkHostGroup();
resetCounter();
setVisible(true);
};
@@ -123,14 +122,20 @@
defineExpose({ openAdd, openUpdate, openCopy });
// 更新主机信息
const updateHostInfo = (id: number) => {
const onUpdateHostInfo = (id: number) => {
hostId.value = id;
incrUpdatedCount();
hostViewUpdated.value = true;
};
// 更新主机信息
const onUpdateHostSpec = () => {
hostViewUpdated.value = true;
};
// 处理关闭
const handleClose = () => {
if (updatedCount.value) {
// 修改主机视图信息后刷新列表
if (hostViewUpdated.value) {
emits('reload');
}
};

View File

@@ -5,28 +5,6 @@
label-align="right"
:auto-label-width="true"
:rules="hostFormRules">
<!-- 主机协议 -->
<a-form-item field="types" label="主机协议">
<a-select v-model="formModel.types"
placeholder="请选择支持的主机协议"
:options="toOptions(hostTypeKey)"
multiple
allow-clear />
</a-form-item>
<!-- 系统类型 -->
<a-form-item field="osType" label="系统类型">
<a-select v-model="formModel.osType"
placeholder="请选择系统类型"
:options="toOptions(hostOsTypeKey)"
allow-clear />
</a-form-item>
<!-- 系统架构 -->
<a-form-item field="archType" label="系统架构">
<a-select v-model="formModel.archType"
placeholder="请选择系统架构"
:options="toOptions(hostArchTypeKey)"
allow-clear />
</a-form-item>
<!-- 主机名称 -->
<a-form-item field="name" label="主机名称">
<a-input v-model="formModel.name"
@@ -45,6 +23,28 @@
placeholder="请输入主机地址"
allow-clear />
</a-form-item>
<!-- 系统类型 -->
<a-form-item field="osType" label="系统类型">
<a-select v-model="formModel.osType"
placeholder="请选择系统类型"
:options="toOptions(hostOsTypeKey)"
allow-clear />
</a-form-item>
<!-- 系统架构 -->
<a-form-item field="archType" label="系统架构">
<a-select v-model="formModel.archType"
placeholder="请选择系统架构"
:options="toOptions(hostArchTypeKey)"
allow-clear />
</a-form-item>
<!-- 主机协议 -->
<a-form-item field="types" label="主机协议">
<a-select v-model="formModel.types"
placeholder="请选择支持的主机协议"
:options="toOptions(hostTypeKey)"
multiple
allow-clear />
</a-form-item>
<!-- 主机分组 -->
<a-form-item field="groupIdList" label="主机分组">
<host-group-tree-selector v-model="formModel.groupIdList"
@@ -90,7 +90,7 @@
import { ref } from 'vue';
import useLoading from '@/hooks/loading';
import { hostFormRules } from '../types/form.rules';
import { createHost, getHost, updateHost } from '@/api/asset/host';
import { createHost, getHost, updateHost, copyHost } from '@/api/asset/host';
import { Message } from '@arco-design/web-vue';
import { pick } from 'lodash';
import { tagColor, hostTypeKey, hostOsTypeKey, HostOsType, hostArchTypeKey } from '../types/const';
@@ -118,6 +118,7 @@
};
};
const isCopy = ref(false);
const formRef = ref();
const formModel = ref<HostUpdateRequest>({});
@@ -135,6 +136,7 @@
// 打开复制
const openCopy = async (id: number) => {
renderForm({ ...defaultForm() });
isCopy.value = true;
await fetchHostRender(id);
};
@@ -188,7 +190,12 @@
if (error) {
return;
}
if (!formModel.value.id) {
if (isCopy.value) {
// 复制
const { data } = await copyHost(formModel.value);
Message.success('复制成功');
emits('updated', data);
} else if (!formModel.value.id) {
// 新增
const { data } = await createHost(formModel.value);
Message.success('创建成功');

View File

@@ -14,7 +14,7 @@ const columns = [
title: '主机信息',
dataIndex: 'hostInfo',
slotName: 'hostInfo',
minWidth: 344,
width: 288,
align: 'left',
fixed: 'left',
default: true,
@@ -22,7 +22,7 @@ const columns = [
title: '主机规格',
dataIndex: 'hostSpec',
slotName: 'hostSpec',
width: 188,
width: 198,
align: 'left',
default: true,
}, {

View File

@@ -32,7 +32,11 @@
<b v-if="app.version && repo.tagName && ('v' + app.version) !== repo.tagName"
class="span-green ml8">新版本已发布, 请及时升级版本</b>
</a-descriptions-item>
<!-- 当前后端版本 -->
<!-- 最近更新时间 -->
<a-descriptions-item label="最近更新时间">
<span>{{ repo.releaseTime }}</span>
</a-descriptions-item>
<!-- 最新更新日志 -->
<a-descriptions-item label="最新更新日志">
<a-textarea v-model="repo.body"
:auto-size="{ minRows: 3, maxRows: 16 }"
@@ -51,22 +55,23 @@
<script lang="ts" setup>
import type { AppInfoResponse, AppReleaseResponse } from '@/api/system/setting';
import { onMounted, reactive } from 'vue';
import { getAppLatestRelease, getSystemAppInfo } from '@/api/system/setting';
import { onMounted, ref } from 'vue';
import { copy } from '@/hooks/copy';
import useLoading from '@/hooks/loading';
import { getAppLatestRelease, getSystemAppInfo } from '@/api/system/setting';
const { loading, setLoading } = useLoading();
const webVersion = import.meta.env.VITE_APP_VERSION;
const app = reactive<AppInfoResponse>({
const app = ref<AppInfoResponse>({
version: '',
uuid: '',
});
const repo = reactive<AppReleaseResponse>({
const repo = ref<AppReleaseResponse>({
tagName: '',
releaseTime: '',
body: '',
});
@@ -75,8 +80,7 @@
setLoading(true);
try {
const { data } = await getSystemAppInfo();
app.version = data.version;
app.uuid = data.uuid;
app.value = data;
} catch (e) {
} finally {
setLoading(false);
@@ -87,8 +91,7 @@
onMounted(async () => {
try {
const { data } = await getAppLatestRelease();
repo.tagName = data.tagName;
repo.body = data.body;
repo.value = data;
} catch (e) {
}
});

View File

@@ -22,7 +22,7 @@
</modules>
<properties>
<revision>2.3.8</revision>
<revision>2.3.9</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>