fix ssh delete

This commit is contained in:
bwcx_jzy 2022-01-24 12:47:05 +08:00
parent 0fae9b0e2c
commit 3031eeef9c
No known key found for this signature in database
GPG Key ID: 5E48E9372088B9E5
12 changed files with 156 additions and 55 deletions

View File

@ -26,6 +26,8 @@
10. 【server】升级 SpringBoot 版本 2.6.2
11. 脚本模版执行目录修改为脚本所在目录
12. 【server】SSH 命令模版支持取消默认加载环境变量:`#disabled-template-auto-evn`
13. 【server】优化页面分页交互逻辑,只有一页不显示分页条
14. 【server】修复删除 SSH 没有删除执行日志
> 特别提醒:强烈建议升级该版本,当前版本完善了权限拦截相关问题
------

View File

@ -92,8 +92,9 @@ public class IndexController extends BaseAgentController {
List<NodeScriptModel> list = nodeScriptServer.list();
JSONObject jsonObject = new JSONObject();
jsonObject.put("javaVirtualCount", JvmUtil.getJavaVirtualCount());
jsonObject.put("osName", JpomManifest.getInstance().getOsName());
jsonObject.put("jpomVersion", JpomManifest.getInstance().getVersion());
JpomManifest instance = JpomManifest.getInstance();
jsonObject.put("osName", instance.getOsName());
jsonObject.put("jpomVersion", instance.getVersion());
jsonObject.put("javaVersion", SystemUtil.getJavaRuntimeInfo().getVersion());
// 获取JVM中内存总大小
long totalMemory = SystemUtil.getTotalMemory();
@ -105,7 +106,8 @@ public class IndexController extends BaseAgentController {
jsonObject.put("count", CollUtil.size(nodeProjectInfoModels));
jsonObject.put("scriptCount", CollUtil.size(list));
// 运行时间
jsonObject.put("runTime", JpomManifest.getInstance().getUpTime());
jsonObject.put("runTime", instance.getUpTimeStr());
jsonObject.put("runTimeLong", instance.getUpTime());
return JsonMessage.getString(200, "", jsonObject);
}
}

View File

@ -20,7 +20,7 @@ whitelistDirectory:
# 白名单目录是否验证包含关系
checkStartsWith: true
log:
# 自动备份控制台日志防止日志文件过大目前暂只支持linux 不停服备份
# 自动备份控制台日志防止日志文件过大目前暂只支持linux 不停服备份,配置为 none 则不备份避免占用硬盘空间
autoBackConsoleCron: 0 0/10 * * * ?
# 当文件多大时自动备份
autoBackSize: 50MB

View File

@ -263,11 +263,15 @@ public class JpomManifest {
this.dataPath = dataPath;
}
public String getUpTime() {
public String getUpTimeStr() {
long uptime = SystemUtil.getRuntimeMXBean().getUptime();
return DateUtil.formatBetween(uptime, BetweenFormatter.Level.SECOND);
}
public long getUpTime() {
return SystemUtil.getRuntimeMXBean().getUptime();
}
public String getOsName() {
return osName;
}

View File

@ -227,7 +227,10 @@ public class SshController extends BaseServerController {
public String del(@ValidatorItem(value = ValidatorRule.NOT_BLANK) String id) {
boolean checkSsh = buildInfoService.checkReleaseMethod(id, BuildReleaseMethod.Ssh);
Assert.state(!checkSsh, "当前ssh存在构建项不能删除");
sshService.delByKey(id, getRequest());
HttpServletRequest request = getRequest();
sshService.delByKey(id, request);
//
int logCount = sshTerminalExecuteLogService.delByWorkspace(request, entity -> entity.set("sshId", id));
return JsonMessage.getString(200, "操作成功");
}

View File

@ -216,7 +216,10 @@ public class NodeMonitor {
nodeStatModel.setNetworkTime(statusData.getIntValue("networkTime"));
nodeStatModel.setJpomVersion(statusData.getString("jpomVersion"));
nodeStatModel.setOsName(statusData.getString("osName"));
nodeStatModel.setUpTimeStr(statusData.getString("runTime"));
String runTime = statusData.getString("runTime");
String runTimeLong = statusData.getString("runTimeLong");
// 兼容数据
nodeStatModel.setUpTimeStr(StrUtil.emptyToDefault(runTimeLong, runTime));
nodeStatModel.setFailureMsg(StrUtil.emptyToDefault(statusData.getString("failureMsg"), StrUtil.EMPTY));
//
Integer statusInteger = statusData.getInteger("status");

View File

@ -367,10 +367,7 @@ public class ServerExtConfigBean implements DisposableBean {
public int getNodeHeartSecond() {
int integer = ObjectUtil.defaultIfNull(nodeHeartSecond, 30);
if (integer <= 0) {
return integer;
}
return integer;
return Math.max(integer, 5);
}
/**

View File

@ -80,7 +80,7 @@ node:
system:
# cron 定时器是否开启匹配秒
timerMatchSecond: false
# 节点心跳监控时间 (需要大于零) 单位秒
# 节点心跳监控时间 (需要大于零) 单位秒 最小配置 5秒
nodeHeartSecond: 30
# 旧包文件保留个数
oldJarsCount: 2

View File

@ -34,13 +34,28 @@
<a-select v-model="listQuery['order_field']" allowClear placeholder="请选择排序字段" class="search-input-item">
<a-select-option value="networkTime">网络延迟</a-select-option>
<a-select-option value="occupyCpu">cpu</a-select-option>
<a-select-option value="occupyDisk">disk</a-select-option>
<a-select-option value="occupyMemoryUsed">memoryUsed</a-select-option>
<a-select-option value="occupyMemory">memory</a-select-option>
<a-select-option value="occupyDisk">硬盘</a-select-option>
<a-select-option value="occupyMemoryUsed">内存Used</a-select-option>
<a-select-option value="occupyMemory">内存</a-select-option>
</a-select>
<a-tooltip title="按住 Ctr 或者 Alt 键点击按钮快速回到第一页">
<a-button :loading="loading" type="primary" @click="loadData">搜索</a-button>
</a-tooltip>
<a-tooltip placement="bottom">
<template slot="title">
<div>
<ul>
<li>监控数据目前采用原生命令获取,和真实情况有一定差异可以当做参考依据</li>
<li>监控频率可以到服务端配置文件中修改</li>
<li>悬停到仪表盘上显示具体含义</li>
<li>点击仪表盘查看监控历史数据</li>
<li>点击延迟可以查看对应节点网络延迟历史数据</li>
<li>只有 linux 系统才有内存Used </li>
</ul>
</div>
</template>
<a-icon type="question-circle" theme="filled" />
</a-tooltip>
</a-space>
</div>
</a-space>
@ -51,7 +66,11 @@
<template slot="title">
<a-row :gutter="[4, 0]">
<a-col :span="17" style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap">
<a-tooltip :title="`${item.name} ${item.url}`">
<a-tooltip>
<template slot="title">
<div>节点名称{{ item.name }}</div>
<div>节点地址{{ item.url }}</div>
</template>
{{ item.name }}
</a-tooltip>
</a-col>
@ -63,8 +82,8 @@
</a-row>
</template>
<div>
<a-space>
<a-row :gutter="[16, 0]" style="margin-left: -16px">
<a-col :span="8">
<a-tooltip @click="item.status === 4 ? null : handleHistory(item, 'nodeTop')" :title="`CPU 占用率:${item.occupyCpu}%`">
<a-progress
type="circle"
@ -79,6 +98,8 @@
:percent="item.occupyCpu"
/>
</a-tooltip>
</a-col>
<a-col :span="8">
<a-tooltip @click="item.status === 4 ? null : handleHistory(item, 'nodeTop')" :title="`硬盘占用率:${item.occupyDisk}%`">
<a-progress
type="circle"
@ -93,6 +114,8 @@
:percent="item.occupyDisk"
/>
</a-tooltip>
</a-col>
<a-col :span="8">
<a-tooltip @click="item.status === 4 ? null : handleHistory(item, 'nodeTop')" :title="`内存占用率:${item.occupyDisk}%`">
<a-progress
:width="80"
@ -107,16 +130,16 @@
:percent="item.occupyMemoryUsed && item.occupyMemoryUsed !== -1 ? item.occupyMemoryUsed : item.occupyMemory"
/>
</a-tooltip>
</a-space>
</div>
</a-col>
</a-row>
<a-space direction="vertical"> <div></div></a-space>
<a-row :gutter="[16, 8]">
<a-row :gutter="[16, 8]" style="text-align: center; margin-left: -16px">
<a-col :span="8">
<a-tooltip @click="item.status === 4 ? null : handleHistory(item, 'networkTime')" :title="`${item.status === 4 ? '' : '延迟' + item.networkTime + 'ms 点击查看历史趋势'}`">
<a-tooltip @click="item.status === 4 ? null : handleHistory(item, 'networkTime')" :title="`${item.status === 4 ? '-' : '延迟' + item.networkTime + 'ms 点击查看历史趋势'}`">
<a-statistic
title="延迟"
:value="item.networkTime"
valueStyle="font-size: 14px;"
valueStyle="font-size: 14px;overflow: hidden; text-overflow: ellipsis; white-space: nowrap"
:formatter="
(v) => {
return item.networkTime === -1 ? '-' : item.networkTime + 'ms';
@ -125,17 +148,31 @@
/>
</a-tooltip>
</a-col>
<a-col :span="16">
<a-statistic
title="运行时间"
:value="item.networkTime"
valueStyle="font-size: 14px;"
:formatter="
(v) => {
return item.upTimeStr || '-';
}
"
/>
<a-col :span="8">
<a-tooltip :title="formatDuration(item.upTimeStr) || '-'">
<a-statistic
title="运行时间"
valueStyle="font-size: 14px;overflow: hidden; text-overflow: ellipsis; white-space: nowrap"
:formatter="
(v) => {
return formatDuration(item.upTimeStr, '', 1) || '-';
}
"
/>
</a-tooltip>
</a-col>
<a-col :span="8">
<a-tooltip :title="`${item.status === 4 ? '-' : parseTime(item.modifyTimeMillis)}`">
<a-statistic
title="更新时间"
valueStyle="font-size: 14px;overflow: hidden; text-overflow: ellipsis; white-space: nowrap"
:formatter="
(v) => {
return item.status === 4 ? '-' : parseTime(item.modifyTimeMillis, '{h}:{i}:{s}');
}
"
/>
</a-tooltip>
</a-col>
</a-row>
</a-card>
@ -177,7 +214,7 @@
</template>
<script>
import { getStatist, status, statusStat } from "@/api/node-stat";
import { parseTime } from "@/utils/time";
import { parseTime, formatDuration } from "@/utils/time";
import { PAGE_DEFAULT_SHOW_TOTAL, PAGE_DEFAULT_LIST_QUERY } from "@/utils/const";
import NodeTop from "@/pages/node/node-layout/node-top";
@ -212,7 +249,8 @@ export default {
},
methods: {
PAGE_DEFAULT_SHOW_TOTAL,
parseTime,
formatDuration,
//
loadData(pointerEvent) {
//this.list = [];
@ -244,7 +282,7 @@ export default {
onFinish() {
this.loadData();
},
parseTime,
//
handleHistory(record, type) {
this.monitorVisible = true;

View File

@ -251,7 +251,24 @@
bordered
:rowKey="(record, index) => index"
>
<a-tooltip slot="commands" slot-scope="text" placement="topLeft" :title="text">
<a-tooltip
slot="commands"
slot-scope="text"
placement="topLeft"
:title="text"
v-clipboard:copy="text"
v-clipboard:success="
() => {
tempVue.prototype.$notification.success({ message: '复制成功' });
}
"
v-clipboard:error="
() => {
tempVue.prototype.$notification.error({ message: '复制失败' });
}
"
>
<a-icon type="copy" />
<span>{{ text }}</span>
</a-tooltip>
<a-tooltip slot="modifyUser" slot-scope="text, item" placement="topLeft" :title="item.modifyUser || item.userId">
@ -261,9 +278,9 @@
<a-tooltip slot="userAgent" slot-scope="text" placement="topLeft" :title="text">
<span>{{ text }}</span>
</a-tooltip>
<a-tooltip slot="refuse" slot-scope="text" placement="topLeft" :title="text">
<template slot="refuse" slot-scope="text">
<span>{{ text ? "成功" : "拒绝" }}</span>
</a-tooltip>
</template>
</a-table>
</a-modal>
</div>
@ -274,6 +291,7 @@ import SshFile from "@/pages/ssh/ssh-file";
import Terminal from "@/pages/ssh/terminal";
import { parseTime } from "@/utils/time";
import { PAGE_DEFAULT_LIMIT, PAGE_DEFAULT_SIZW_OPTIONS, PAGE_DEFAULT_SHOW_TOTAL, PAGE_DEFAULT_LIST_QUERY } from "@/utils/const";
import Vue from "vue";
export default {
components: {
@ -312,6 +330,7 @@ export default {
title: "执行命令",
dataIndex: "commands",
width: 200,
ellipsis: true,
scopedSlots: { customRender: "commands" },
},
{
@ -337,12 +356,8 @@ export default {
} /*width: 180*/,
},
],
viewOperationLogTotal: 0,
viewOperationLogListQuery: {
page: 1,
limit: 10,
total: 0,
},
viewOperationLogListQuery: Object.assign({}, PAGE_DEFAULT_LIST_QUERY),
columns: [
{ title: "名称", dataIndex: "name", sorter: true, ellipsis: true },
@ -400,6 +415,7 @@ export default {
url: [{ required: true, message: "Please input url", trigger: "blur" }],
path: [{ required: true, message: "Please input path", trigger: "blur" }],
},
tempVue: null,
};
},
computed: {
@ -511,6 +527,10 @@ export default {
this.temp = Object.assign(record);
this.viewOperationLogListQuery.sshId = this.temp.id;
this.viewOperationLog = true;
this.viewOperationLogList = [];
this.viewOperationLogListQuery.total = 0;
this.viewOperationLogListQuery.page = 1;
this.tempVue = Vue;
this.handleListLog();
},
handleListLog() {

View File

@ -181,12 +181,16 @@ export default {
});
},
initWebsocket(ids) {
if (!this.socket || this.socket.readyState !== this.socket.OPEN || this.socket.readyState !== this.socket.CONNECTING) {
this.socket = new WebSocket(this.socketUrl);
if (this.socket) {
this.initHeart(ids);
return;
}
// if (!this.socket || this.socket.readyState !== this.socket.OPEN || this.socket.readyState !== this.socket.CONNECTING) {
this.socket = new WebSocket(this.socketUrl);
// }
this.socket.onopen = () => {
this.init(ids);
this.initHeart(ids);
};
this.socket.onmessage = ({ data: socketData }) => {
let msgObj;
@ -231,7 +235,7 @@ export default {
}
});
},
init(ids) {
initHeart(ids) {
this.sendMsg("getNodeList:" + ids.join(","));
this.getAgentVersion();
// 线
@ -241,11 +245,11 @@ export default {
}, 2000);
},
refresh() {
if (this.socket) {
this.socket.close();
}
this.nodeStatus = {};
this.nodeVersion = {};
// if (this.socket) {
// this.socket.close();
// }
//this.nodeStatus = {};
//this.nodeVersion = {};
this.getNodeList();
},
batchUpdate() {

View File

@ -84,3 +84,31 @@ export function itemGroupBy(arr, groupKey, key, dataKey) {
}
return newArr;
}
/**
* 格式化时长
* @param {String} ms
* @param {String} seg 分割符
* @param {String} levelCount 格式化个数
* @returns
*/
export function formatDuration(ms, seg, levelCount) {
if (isNaN(new Number(ms))) {
return ms;
}
seg = seg || "";
levelCount = levelCount || 5;
if (ms < 0) ms = -ms;
const time = {
: Math.floor(ms / 86400000),
小时: Math.floor(ms / 3600000) % 24,
分钟: Math.floor(ms / 60000) % 60,
: Math.floor(ms / 1000) % 60,
毫秒: Math.floor(ms) % 1000,
};
return Object.entries(time)
.filter((val) => val[1] !== 0)
.map(([key, val]) => `${val}${key}`)
.splice(0, levelCount)
.join(seg);
}