🔨 修改终端逻辑.
This commit is contained in:
@@ -27,7 +27,7 @@ spring:
|
|||||||
guacd:
|
guacd:
|
||||||
host: ${GUACD_HOST:127.0.0.1}
|
host: ${GUACD_HOST:127.0.0.1}
|
||||||
port: ${GUACD_PORT:4822}
|
port: ${GUACD_PORT:4822}
|
||||||
drive-path: ${GUACD_DRIVE_PATH:/home/guacd}
|
drive-path: ${GUACD_DRIVE_PATH:/home/guacd/drive}
|
||||||
|
|
||||||
management:
|
management:
|
||||||
endpoints:
|
endpoints:
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import org.apache.ibatis.annotations.Param;
|
|||||||
import org.dromara.visor.framework.mybatis.core.mapper.IMapper;
|
import org.dromara.visor.framework.mybatis.core.mapper.IMapper;
|
||||||
import org.dromara.visor.framework.mybatis.core.query.Conditions;
|
import org.dromara.visor.framework.mybatis.core.query.Conditions;
|
||||||
import org.dromara.visor.module.asset.entity.domain.HostConfigDO;
|
import org.dromara.visor.module.asset.entity.domain.HostConfigDO;
|
||||||
import org.dromara.visor.module.asset.entity.po.HostTypeCountPO;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -134,11 +133,4 @@ public interface HostConfigDAO extends IMapper<HostConfigDO> {
|
|||||||
*/
|
*/
|
||||||
int setIdentityIdWithNull(@Param("identityIdList") List<Long> identityIdList);
|
int setIdentityIdWithNull(@Param("identityIdList") List<Long> identityIdList);
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询启用的主机类型数量
|
|
||||||
*
|
|
||||||
* @return count
|
|
||||||
*/
|
|
||||||
List<HostTypeCountPO> selectEnabledTypeCount();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ public class HostKeyCreateRequest implements Serializable {
|
|||||||
@Schema(description = "私钥文本")
|
@Schema(description = "私钥文本")
|
||||||
private String privateKey;
|
private String privateKey;
|
||||||
|
|
||||||
@NotBlank
|
|
||||||
@ParamDecrypt
|
@ParamDecrypt
|
||||||
@Schema(description = "密码")
|
@Schema(description = "密码")
|
||||||
private String password;
|
private String password;
|
||||||
|
|||||||
@@ -1,82 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
|
||||||
*
|
|
||||||
* https://visor.dromara.org
|
|
||||||
* https://visor.dromara.org.cn
|
|
||||||
* https://visor.orionsec.cn
|
|
||||||
*
|
|
||||||
* Members:
|
|
||||||
* Jiahang Li - ljh1553488six@139.com - author
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.dromara.visor.module.asset.service.impl;
|
|
||||||
|
|
||||||
import cn.orionsec.kit.lang.utils.Objects1;
|
|
||||||
import cn.orionsec.kit.lang.utils.collect.Maps;
|
|
||||||
import com.alibaba.fastjson.JSONObject;
|
|
||||||
import org.dromara.visor.common.entity.chart.PieChartData;
|
|
||||||
import org.dromara.visor.framework.redis.core.utils.RedisStrings;
|
|
||||||
import org.dromara.visor.framework.redis.core.utils.barrier.CacheBarriers;
|
|
||||||
import org.dromara.visor.module.asset.dao.HostConfigDAO;
|
|
||||||
import org.dromara.visor.module.asset.define.cache.AssetStatisticsCacheKeyDefine;
|
|
||||||
import org.dromara.visor.module.asset.entity.po.HostTypeCountPO;
|
|
||||||
import org.dromara.visor.module.asset.enums.HostTypeEnum;
|
|
||||||
import org.dromara.visor.module.asset.service.AssetStatisticsService;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 资产模块统计服务实现
|
|
||||||
*
|
|
||||||
* @author Jiahang Li
|
|
||||||
* @version 1.0.0
|
|
||||||
* @since 2025/3/7 16:26
|
|
||||||
*/
|
|
||||||
@Service
|
|
||||||
public class AssetStatisticsServiceImpl implements AssetStatisticsService {
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private HostConfigDAO hostConfigDAO;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PieChartData getHostTypeChart() {
|
|
||||||
// 查询缓存
|
|
||||||
JSONObject cache = RedisStrings.getJson(AssetStatisticsCacheKeyDefine.HOST_TYPE_COUNT);
|
|
||||||
if (Maps.isEmpty(cache)) {
|
|
||||||
cache = new JSONObject();
|
|
||||||
// 查询数据库
|
|
||||||
List<HostTypeCountPO> typeCountList = hostConfigDAO.selectEnabledTypeCount();
|
|
||||||
for (HostTypeCountPO typeCount : typeCountList) {
|
|
||||||
cache.put(typeCount.getType(), typeCount.getCount());
|
|
||||||
}
|
|
||||||
// 设置屏障 防止穿透
|
|
||||||
CacheBarriers.STRING_MAP.check(cache);
|
|
||||||
// 设置缓存
|
|
||||||
RedisStrings.set(AssetStatisticsCacheKeyDefine.HOST_TYPE_COUNT, cache);
|
|
||||||
}
|
|
||||||
// 删除屏障
|
|
||||||
CacheBarriers.STRING_MAP.remove(cache);
|
|
||||||
// 查询类型数量
|
|
||||||
Map<String, Integer> data = new HashMap<>();
|
|
||||||
for (HostTypeEnum value : HostTypeEnum.values()) {
|
|
||||||
data.put(value.name(), Objects1.def(cache.getInteger(value.name()), 0));
|
|
||||||
}
|
|
||||||
return new PieChartData(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -37,12 +37,10 @@ import org.dromara.visor.common.enums.EnableStatus;
|
|||||||
import org.dromara.visor.common.utils.Valid;
|
import org.dromara.visor.common.utils.Valid;
|
||||||
import org.dromara.visor.framework.biz.operator.log.core.utils.OperatorLogs;
|
import org.dromara.visor.framework.biz.operator.log.core.utils.OperatorLogs;
|
||||||
import org.dromara.visor.framework.redis.core.utils.RedisMaps;
|
import org.dromara.visor.framework.redis.core.utils.RedisMaps;
|
||||||
import org.dromara.visor.framework.redis.core.utils.RedisStrings;
|
|
||||||
import org.dromara.visor.framework.redis.core.utils.barrier.CacheBarriers;
|
import org.dromara.visor.framework.redis.core.utils.barrier.CacheBarriers;
|
||||||
import org.dromara.visor.module.asset.convert.HostConvert;
|
import org.dromara.visor.module.asset.convert.HostConvert;
|
||||||
import org.dromara.visor.module.asset.dao.HostConfigDAO;
|
import org.dromara.visor.module.asset.dao.HostConfigDAO;
|
||||||
import org.dromara.visor.module.asset.dao.HostDAO;
|
import org.dromara.visor.module.asset.dao.HostDAO;
|
||||||
import org.dromara.visor.module.asset.define.cache.AssetStatisticsCacheKeyDefine;
|
|
||||||
import org.dromara.visor.module.asset.define.cache.HostCacheKeyDefine;
|
import org.dromara.visor.module.asset.define.cache.HostCacheKeyDefine;
|
||||||
import org.dromara.visor.module.asset.entity.domain.HostDO;
|
import org.dromara.visor.module.asset.entity.domain.HostDO;
|
||||||
import org.dromara.visor.module.asset.entity.dto.HostCacheDTO;
|
import org.dromara.visor.module.asset.entity.dto.HostCacheDTO;
|
||||||
@@ -346,7 +344,6 @@ public class HostServiceImpl implements HostService {
|
|||||||
@Override
|
@Override
|
||||||
public void clearCache() {
|
public void clearCache() {
|
||||||
RedisMaps.scanKeysDelete(HostCacheKeyDefine.HOST_INFO.format("*"));
|
RedisMaps.scanKeysDelete(HostCacheKeyDefine.HOST_INFO.format("*"));
|
||||||
RedisStrings.delete(AssetStatisticsCacheKeyDefine.HOST_TYPE_COUNT.getKey());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -16,12 +16,6 @@
|
|||||||
<result column="deleted" property="deleted"/>
|
<result column="deleted" property="deleted"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<!-- 类型数量结果映射 -->
|
|
||||||
<resultMap id="TypeCountResultMap" type="org.dromara.visor.module.asset.entity.po.HostTypeCountPO">
|
|
||||||
<result column="type" property="type"/>
|
|
||||||
<result column="total_count" property="count"/>
|
|
||||||
</resultMap>
|
|
||||||
|
|
||||||
<!-- 通用查询结果列 -->
|
<!-- 通用查询结果列 -->
|
||||||
<sql id="Base_Column_List">
|
<sql id="Base_Column_List">
|
||||||
id, host_id, type, status, config, create_time, update_time, creator, updater, deleted
|
id, host_id, type, status, config, create_time, update_time, creator, updater, deleted
|
||||||
@@ -45,12 +39,4 @@
|
|||||||
</foreach>
|
</foreach>
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
<select id="selectEnabledTypeCount" resultMap="TypeCountResultMap">
|
|
||||||
SELECT type, COUNT(1) total_count
|
|
||||||
FROM host_config
|
|
||||||
WHERE deleted = 0
|
|
||||||
AND status = 'ENABLED'
|
|
||||||
GROUP BY type
|
|
||||||
</select>
|
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
import type { PieChartData } from '@/types/global';
|
|
||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询主机类型图表
|
|
||||||
*/
|
|
||||||
export function getHostTypeChart() {
|
|
||||||
return axios.get<PieChartData>('/asset/statistics/host-type-chart');
|
|
||||||
}
|
|
||||||
@@ -7,11 +7,8 @@ import axios from 'axios';
|
|||||||
*/
|
*/
|
||||||
export interface TerminalWorkplaceStatisticsResponse {
|
export interface TerminalWorkplaceStatisticsResponse {
|
||||||
todayTerminalConnectCount: number;
|
todayTerminalConnectCount: number;
|
||||||
todayTerminalCommandCount: number;
|
|
||||||
weekTerminalConnectCount: number;
|
weekTerminalConnectCount: number;
|
||||||
weekTerminalCommandCount: number;
|
|
||||||
terminalConnectChart: LineSingleChartData;
|
terminalConnectChart: LineSingleChartData;
|
||||||
terminalCommandChart: LineSingleChartData;
|
|
||||||
terminalConnectList: Array<TerminalConnectLogQueryResponse>;
|
terminalConnectList: Array<TerminalConnectLogQueryResponse>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ export interface TerminalRdpGraphSetting {
|
|||||||
// SSH 操作栏设置
|
// SSH 操作栏设置
|
||||||
export interface TerminalSshActionBarSetting {
|
export interface TerminalSshActionBarSetting {
|
||||||
connectStatus?: boolean;
|
connectStatus?: boolean;
|
||||||
share?: boolean;
|
|
||||||
|
|
||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-container" v-if="render">
|
<div class="layout-container" v-if="render">
|
||||||
<!-- 列表-表格 -->
|
<!-- 列表-表格 -->
|
||||||
<connect-session-table @open-watcher="openWatcher"
|
<connect-session-table />
|
||||||
@open-event="(s) => eventDrawer.open(s)" />
|
|
||||||
<!-- 监视模态框 -->
|
|
||||||
<xterm-watcher-modal ref="watcherModal" />
|
|
||||||
<!-- 事件抽屉 -->
|
|
||||||
<connect-event-drawer ref="eventDrawer" />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -20,28 +15,11 @@
|
|||||||
import { ref, onBeforeMount } from 'vue';
|
import { ref, onBeforeMount } from 'vue';
|
||||||
import { useDictStore } from '@/store';
|
import { useDictStore } from '@/store';
|
||||||
import { dictKeys } from './types/const';
|
import { dictKeys } from './types/const';
|
||||||
import { openNewRoute } from '@/router';
|
|
||||||
import ConnectSessionTable from './components/connect-session-table.vue';
|
import ConnectSessionTable from './components/connect-session-table.vue';
|
||||||
import ConnectEventDrawer from '../connect-log/components/connect-event-drawer.vue';
|
|
||||||
import XtermWatcherModal from '@/components/xterm/watcher/modal/index.vue';
|
|
||||||
|
|
||||||
const render = ref(false);
|
const render = ref(false);
|
||||||
const watcherModal = ref();
|
|
||||||
const eventDrawer = ref();
|
const eventDrawer = ref();
|
||||||
|
|
||||||
// 打开监视
|
|
||||||
const openWatcher = (sessionId: string, newWindow: boolean) => {
|
|
||||||
if (newWindow) {
|
|
||||||
// 跳转新页面
|
|
||||||
openNewRoute({
|
|
||||||
name: 'terminalWatcher',
|
|
||||||
query: { sessionId }
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
watcherModal.value.open(sessionId);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 加载字典配置
|
// 加载字典配置
|
||||||
onBeforeMount(async () => {
|
onBeforeMount(async () => {
|
||||||
const dictStore = useDictStore();
|
const dictStore = useDictStore();
|
||||||
|
|||||||
@@ -262,28 +262,22 @@
|
|||||||
连接
|
连接
|
||||||
</a-button>
|
</a-button>
|
||||||
<!-- 多协议连接 -->
|
<!-- 多协议连接 -->
|
||||||
<a-popover v-else-if="(record.types?.length || 0) > 1"
|
<a-popover v-if="(record.types?.length || 0) > 1"
|
||||||
v-permission="['terminal:terminal:access']"
|
|
||||||
:title="undefined"
|
:title="undefined"
|
||||||
:content-style="{ padding: '8px' }">
|
:content-style="{ padding: '8px' }">
|
||||||
<a-button type="text" size="mini">
|
<a-button v-permission="['terminal:terminal:access']"
|
||||||
|
type="text"
|
||||||
|
size="mini">
|
||||||
连接
|
连接
|
||||||
</a-button>
|
</a-button>
|
||||||
<template #content>
|
<template #content>
|
||||||
<a-space>
|
<a-space>
|
||||||
<!-- SSH -->
|
<a-button v-for="type in record.types"
|
||||||
<a-button v-if="record.types.includes(HostType.SSH.value)"
|
:key="type"
|
||||||
type="text"
|
type="text"
|
||||||
size="mini"
|
size="mini"
|
||||||
@click="openNewRoute({ name: 'terminal', query: { connect: record.id, type: record.types[0] } })">
|
@click="openNewRoute({ name: 'terminal', query: { connect: record.id, type} })">
|
||||||
SSH
|
{{ type }}
|
||||||
</a-button>
|
|
||||||
<!-- RDP -->
|
|
||||||
<a-button v-if="record.types.includes(HostType.RDP.value)"
|
|
||||||
type="text"
|
|
||||||
size="mini"
|
|
||||||
@click="openNewRoute({ name: 'terminal', query: { connect: record.id, type: 'RDP' } })">
|
|
||||||
RDP
|
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
@@ -346,7 +340,7 @@
|
|||||||
import { reactive, ref, onMounted } from 'vue';
|
import { reactive, ref, onMounted } from 'vue';
|
||||||
import { deleteHost, batchDeleteHost, getHostPage, updateHostStatus } from '@/api/asset/host';
|
import { deleteHost, batchDeleteHost, getHostPage, updateHostStatus } from '@/api/asset/host';
|
||||||
import { Message, Modal } from '@arco-design/web-vue';
|
import { Message, Modal } from '@arco-design/web-vue';
|
||||||
import { tagColor, hostTypeKey, hostStatusKey, HostType, hostOsTypeKey, hostArchTypeKey, TableName } from '../types/const';
|
import { tagColor, hostTypeKey, hostStatusKey, hostOsTypeKey, hostArchTypeKey, TableName } from '../types/const';
|
||||||
import { useTablePagination, useRowSelection, useTableColumns } from '@/hooks/table';
|
import { useTablePagination, useRowSelection, useTableColumns } from '@/hooks/table';
|
||||||
import { useQueryOrder, ASC } from '@/hooks/query-order';
|
import { useQueryOrder, ASC } from '@/hooks/query-order';
|
||||||
import { useCacheStore, useDictStore } from '@/store';
|
import { useCacheStore, useDictStore } from '@/store';
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export const terminalLogColumns = [
|
|||||||
dataIndex: 'hostName',
|
dataIndex: 'hostName',
|
||||||
slotName: 'hostName',
|
slotName: 'hostName',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
minWidth: 98,
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
}, {
|
}, {
|
||||||
title: '类型',
|
title: '类型',
|
||||||
@@ -20,7 +21,7 @@ export const terminalLogColumns = [
|
|||||||
dataIndex: 'startTime',
|
dataIndex: 'startTime',
|
||||||
slotName: 'startTime',
|
slotName: 'startTime',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
width: 180,
|
width: 168,
|
||||||
render: ({ record }) => {
|
render: ({ record }) => {
|
||||||
return dateFormat(new Date(record.startTime));
|
return dateFormat(new Date(record.startTime));
|
||||||
},
|
},
|
||||||
@@ -40,6 +41,7 @@ export const batchExecColumns = [
|
|||||||
dataIndex: 'description',
|
dataIndex: 'description',
|
||||||
slotName: 'description',
|
slotName: 'description',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
minWidth: 98,
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
tooltip: true,
|
tooltip: true,
|
||||||
}, {
|
}, {
|
||||||
@@ -53,7 +55,7 @@ export const batchExecColumns = [
|
|||||||
dataIndex: 'startTime',
|
dataIndex: 'startTime',
|
||||||
slotName: 'startTime',
|
slotName: 'startTime',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
width: 180,
|
width: 168,
|
||||||
render: ({ record }) => {
|
render: ({ record }) => {
|
||||||
return dateFormat(new Date(record.startTime));
|
return dateFormat(new Date(record.startTime));
|
||||||
},
|
},
|
||||||
@@ -72,6 +74,7 @@ export const userLoginColumns = [
|
|||||||
title: '登录设备',
|
title: '登录设备',
|
||||||
dataIndex: 'content',
|
dataIndex: 'content',
|
||||||
slotName: 'content',
|
slotName: 'content',
|
||||||
|
minWidth: 98,
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
tooltip: true,
|
tooltip: true,
|
||||||
}, {
|
}, {
|
||||||
|
|||||||
@@ -30,12 +30,6 @@
|
|||||||
:default-checked="true"
|
:default-checked="true"
|
||||||
type="round" />
|
type="round" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<!-- 分享按钮 -->
|
|
||||||
<a-form-item field="share" label="分享按钮">
|
|
||||||
<a-switch v-model="formModel.share"
|
|
||||||
:default-checked="true"
|
|
||||||
type="round" />
|
|
||||||
</a-form-item>
|
|
||||||
</a-space>
|
</a-space>
|
||||||
</a-form>
|
</a-form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -311,7 +311,7 @@
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
transition: .3s all;
|
transition: .3s all;
|
||||||
background: var(--color-bg-rdp-toolbar);
|
background: var(--color-bg-rdp-toolbar);
|
||||||
filter: contrast(150%) brightness(80%);
|
filter: contrast(50%) brightness(50%);
|
||||||
|
|
||||||
&.top {
|
&.top {
|
||||||
width: 240px;
|
width: 240px;
|
||||||
|
|||||||
@@ -73,6 +73,11 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
|
:deep(> div) {
|
||||||
|
position: relative;
|
||||||
|
z-index: 8;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.rdp-status-mask {
|
.rdp-status-mask {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export default class TerminalPanelManager implements ITerminalPanelManager {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.active = 0;
|
this.active = 0;
|
||||||
this.panels = [new TerminalTabManager(), new TerminalTabManager()];
|
this.panels = [new TerminalTabManager()];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取当前面板
|
// 获取当前面板
|
||||||
|
|||||||
@@ -96,7 +96,6 @@ export const TerminalCloseCode = {
|
|||||||
export const TerminalMessages = {
|
export const TerminalMessages = {
|
||||||
sessionClosed: '会话已结束...',
|
sessionClosed: '会话已结束...',
|
||||||
waitingReconnect: '输入回车重新连接...',
|
waitingReconnect: '输入回车重新连接...',
|
||||||
waitingJoin: '等待分享者同意...',
|
|
||||||
loggedElsewhere: '该账号已在另一台设备登录',
|
loggedElsewhere: '该账号已在另一台设备登录',
|
||||||
rdpConnectTimeout: '请检查远程计算机网络及其他配置是否正常',
|
rdpConnectTimeout: '请检查远程计算机网络及其他配置是否正常',
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user