mirror of
https://gitee.com/dromara/Jpom.git
synced 2024-12-04 21:08:30 +08:00
ssh 禁止命令修护,页面提示tooltip
This commit is contained in:
parent
58091acbb7
commit
f40224cdf7
@ -186,10 +186,16 @@ public class SshModel extends BaseModel {
|
||||
List<String> commands = StrUtil.split(inputItem, StrUtil.CR);
|
||||
commands.addAll(StrUtil.split(inputItem, "&"));
|
||||
for (String s : split) {
|
||||
//
|
||||
boolean anyMatch = commands.stream().anyMatch(item -> StrUtil.startWithAny(item, s + StrUtil.SPACE, ("&" + s + StrUtil.SPACE), StrUtil.SPACE + s + StrUtil.SPACE));
|
||||
if (anyMatch) {
|
||||
return false;
|
||||
}
|
||||
//
|
||||
anyMatch = commands.stream().anyMatch(item -> StrUtil.equals(item, s));
|
||||
if (anyMatch) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -212,8 +212,28 @@ public class SshHandler extends BaseHandler {
|
||||
ThreadUtil.execute(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加到命令队列
|
||||
*
|
||||
* @param msg 输入
|
||||
* @return 当前待确认待所有命令
|
||||
*/
|
||||
private String append(String msg) {
|
||||
char[] x = msg.toCharArray();
|
||||
if (x.length == 1 && x[0] == 127) {
|
||||
// 退格键
|
||||
int length = nowLineInput.length();
|
||||
if (length > 0) {
|
||||
nowLineInput.delete(length - 1, length);
|
||||
}
|
||||
} else {
|
||||
nowLineInput.append(msg);
|
||||
}
|
||||
return nowLineInput.toString();
|
||||
}
|
||||
|
||||
public boolean checkInput(String msg) {
|
||||
String allCommand = nowLineInput.append(msg).toString();
|
||||
String allCommand = this.append(msg);
|
||||
boolean refuse;
|
||||
if (StrUtil.equalsAny(msg, StrUtil.CR, StrUtil.TAB)) {
|
||||
String join = nowLineInput.toString();
|
||||
|
@ -5,8 +5,16 @@
|
||||
<a-button type="primary" @click="loadData">刷新</a-button>
|
||||
</div>
|
||||
<!-- 数据表格 -->
|
||||
<a-table :data-source="list" :loading="loading" :columns="columns" :style="{'max-height': tableHeight + 'px' }" :scroll="{x: 1110, y: tableHeight - 60}"
|
||||
:pagination="false" bordered :rowKey="(record, index) => index">
|
||||
<a-table
|
||||
:data-source="list"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:style="{ 'max-height': tableHeight + 'px' }"
|
||||
:scroll="{ x: 1110, y: tableHeight - 60 }"
|
||||
:pagination="false"
|
||||
bordered
|
||||
:rowKey="(record, index) => index"
|
||||
>
|
||||
<a-tooltip slot="name" slot-scope="text" placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
@ -50,30 +58,23 @@
|
||||
</a-select>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="报警联系人" prop="notifyUser" class="jpom-notify">
|
||||
<a-transfer
|
||||
:data-source="userList"
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
:target-keys="targetKeys"
|
||||
:render="item => item.title"
|
||||
@change="handleChange"
|
||||
/>
|
||||
<a-transfer :data-source="userList" show-search :filter-option="filterOption" :target-keys="targetKeys" :render="(item) => item.title" @change="handleChange" />
|
||||
</a-form-model-item>
|
||||
</a-form-model>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { getMonitorList, editMonitor, deleteMonitor } from '../../api/monitor';
|
||||
import { getAdminUserList } from '../../api/user';
|
||||
import { getNodeProjectList } from '../../api/node';
|
||||
import { parseTime } from '../../utils/time';
|
||||
import { mapGetters } from "vuex";
|
||||
import { getMonitorList, editMonitor, deleteMonitor } from "../../api/monitor";
|
||||
import { getAdminUserList } from "../../api/user";
|
||||
import { getNodeProjectList } from "../../api/node";
|
||||
import { parseTime } from "../../utils/time";
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
tableHeight: '70vh',
|
||||
tableHeight: "70vh",
|
||||
list: [],
|
||||
userList: [],
|
||||
nodeProjectList: [],
|
||||
@ -84,35 +85,36 @@ export default {
|
||||
temp: {},
|
||||
editMonitorVisible: false,
|
||||
columns: [
|
||||
{title: '名称', dataIndex: 'name', ellipsis: true, scopedSlots: {customRender: 'name'}, width: 150},
|
||||
{title: '开启状态', dataIndex: 'status', ellipsis: true, scopedSlots: {customRender: 'status'}, width: 150},
|
||||
{title: '自动重启', dataIndex: 'autoRestart', ellipsis: true, scopedSlots: {customRender: 'autoRestart'}, width: 150},
|
||||
{title: '报警状态', dataIndex: 'alarm', ellipsis: true, scopedSlots: {customRender: 'alarm'}, width: 150},
|
||||
{title: '创建人', dataIndex: 'parent', ellipsis: true, scopedSlots: {customRender: 'parent'}, width: 120},
|
||||
{title: '修改时间', dataIndex: 'modifyTime', customRender: (text) => {
|
||||
if (!text || text === '0') {
|
||||
return '';
|
||||
{ title: "名称", dataIndex: "name", ellipsis: true, scopedSlots: { customRender: "name" }, width: 150 },
|
||||
{ title: "开启状态", dataIndex: "status", ellipsis: true, scopedSlots: { customRender: "status" }, width: 150 },
|
||||
{ title: "自动重启", dataIndex: "autoRestart", ellipsis: true, scopedSlots: { customRender: "autoRestart" }, width: 150 },
|
||||
{ title: "报警状态", dataIndex: "alarm", ellipsis: true, scopedSlots: { customRender: "alarm" }, width: 150 },
|
||||
{ title: "创建人", dataIndex: "parent", ellipsis: true, scopedSlots: { customRender: "parent" }, width: 120 },
|
||||
{
|
||||
title: "修改时间",
|
||||
dataIndex: "modifyTime",
|
||||
customRender: (text) => {
|
||||
if (!text || text === "0") {
|
||||
return "";
|
||||
}
|
||||
return parseTime(text);
|
||||
}, width: 180},
|
||||
{title: '操作', dataIndex: 'operation', ellipsis: true, scopedSlots: {customRender: 'operation'}, width: 200}
|
||||
},
|
||||
width: 180,
|
||||
},
|
||||
{ title: "操作", dataIndex: "operation", ellipsis: true, scopedSlots: { customRender: "operation" }, width: 200 },
|
||||
],
|
||||
rules: {
|
||||
name: [
|
||||
{ required: true, message: 'Please input monitor name', trigger: 'blur' }
|
||||
],
|
||||
}
|
||||
}
|
||||
name: [{ required: true, message: "Please input monitor name", trigger: "blur" }],
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'getGuideFlag'
|
||||
])
|
||||
...mapGetters(["getGuideFlag"]),
|
||||
},
|
||||
watch: {
|
||||
getGuideFlag() {
|
||||
this.introGuide();
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.calcTableHeight();
|
||||
@ -124,14 +126,18 @@ export default {
|
||||
// 页面引导
|
||||
introGuide() {
|
||||
if (this.getGuideFlag) {
|
||||
this.$introJs().setOptions({
|
||||
this.$introJs()
|
||||
.setOptions({
|
||||
hidePrev: true,
|
||||
steps: [{
|
||||
title: 'Jpom 导航助手',
|
||||
element: document.querySelector('.jpom-notify'),
|
||||
intro: '如果这里的报警联系人无法选择,说明这里面的管理员没有设置邮箱,在右上角下拉菜单里面的用户资料里可以设置。'
|
||||
}]
|
||||
}).start();
|
||||
steps: [
|
||||
{
|
||||
title: "Jpom 导航助手",
|
||||
element: document.querySelector(".jpom-notify"),
|
||||
intro: "如果这里的报警联系人无法选择,说明这里面的管理员没有设置邮箱,在右上角下拉菜单里面的用户资料里可以设置。",
|
||||
},
|
||||
],
|
||||
})
|
||||
.start();
|
||||
return false;
|
||||
}
|
||||
this.$introJs().exit();
|
||||
@ -139,40 +145,40 @@ export default {
|
||||
// 计算表格高度
|
||||
calcTableHeight() {
|
||||
this.$nextTick(() => {
|
||||
this.tableHeight = window.innerHeight - this.$refs['filter'].clientHeight - 135;
|
||||
})
|
||||
this.tableHeight = window.innerHeight - this.$refs["filter"].clientHeight - 135;
|
||||
});
|
||||
},
|
||||
// 加载数据
|
||||
loadData() {
|
||||
this.loading = true;
|
||||
const params = {
|
||||
nodeId: ''
|
||||
}
|
||||
getMonitorList(params).then(res => {
|
||||
nodeId: "",
|
||||
};
|
||||
getMonitorList(params).then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.list = res.data;
|
||||
}
|
||||
this.loading = false;
|
||||
})
|
||||
});
|
||||
},
|
||||
// 加载用户列表
|
||||
loadUserList() {
|
||||
this.userList = [];
|
||||
getAdminUserList().then(res => {
|
||||
getAdminUserList().then((res) => {
|
||||
if (res.code === 200) {
|
||||
res.data.forEach(element => {
|
||||
res.data.forEach((element) => {
|
||||
this.userList.push({ key: element.value, title: element.title, disabled: element.disabled || false });
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
// 加载节点项目列表
|
||||
loadNodeProjectList() {
|
||||
getNodeProjectList().then(res => {
|
||||
getNodeProjectList().then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.nodeProjectList = res.data;
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
// 穿梭框筛选
|
||||
filterOption(inputValue, option) {
|
||||
@ -186,22 +192,22 @@ export default {
|
||||
handleSelectChange(value) {
|
||||
this.projectKeys = value;
|
||||
let projects = [];
|
||||
this.nodeProjectList.forEach(node => {
|
||||
this.nodeProjectList.forEach((node) => {
|
||||
let tempProjects = [];
|
||||
node.projects.forEach(project => {
|
||||
this.projectKeys.forEach(element => {
|
||||
node.projects.forEach((project) => {
|
||||
this.projectKeys.forEach((element) => {
|
||||
if (project.id === element) {
|
||||
tempProjects.push(project.id);
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
if (tempProjects.length > 0) {
|
||||
projects.push({
|
||||
node: node.id,
|
||||
projects: tempProjects
|
||||
})
|
||||
projects: tempProjects,
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
this.temp.projects = projects;
|
||||
},
|
||||
// 新增
|
||||
@ -211,7 +217,7 @@ export default {
|
||||
setTimeout(() => {
|
||||
this.introGuide();
|
||||
}, 500);
|
||||
})
|
||||
});
|
||||
},
|
||||
// 修改
|
||||
handleEdit(record) {
|
||||
@ -219,63 +225,63 @@ export default {
|
||||
this.targetKeys = this.temp.notifyUser;
|
||||
// 设置监控项目
|
||||
this.projectKeys = [];
|
||||
this.temp.projects.forEach(node => {
|
||||
node.projects.forEach(project => {
|
||||
this.temp.projects.forEach((node) => {
|
||||
node.projects.forEach((project) => {
|
||||
this.projectKeys.push(project);
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
this.editMonitorVisible = true;
|
||||
},
|
||||
handleEditMonitorOk() {
|
||||
// 检验表单
|
||||
this.$refs['editMonitorForm'].validate((valid) => {
|
||||
this.$refs["editMonitorForm"].validate((valid) => {
|
||||
if (!valid) {
|
||||
return false;
|
||||
}
|
||||
const params = {
|
||||
...this.temp,
|
||||
status: this.temp.status ? 'on' : 'off',
|
||||
autoRestart: this.temp.autoRestart ? 'on' : 'off',
|
||||
status: this.temp.status ? "on" : "off",
|
||||
autoRestart: this.temp.autoRestart ? "on" : "off",
|
||||
projects: JSON.stringify(this.temp.projects),
|
||||
notifyUser: JSON.stringify(this.targetKeys)
|
||||
}
|
||||
editMonitor(params).then(res => {
|
||||
notifyUser: JSON.stringify(this.targetKeys),
|
||||
};
|
||||
editMonitor(params).then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
this.$notification.success({
|
||||
message: res.msg,
|
||||
duration: 2
|
||||
duration: 2,
|
||||
});
|
||||
this.$refs['editMonitorForm'].resetFields();
|
||||
this.$refs["editMonitorForm"].resetFields();
|
||||
this.editMonitorVisible = false;
|
||||
this.loadData();
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
},
|
||||
// 删除
|
||||
handleDelete(record) {
|
||||
this.$confirm({
|
||||
title: '系统提示',
|
||||
content: '真的要删除监控么?',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
title: "系统提示",
|
||||
content: "真的要删除监控么?",
|
||||
okText: "确认",
|
||||
cancelText: "取消",
|
||||
onOk: () => {
|
||||
// 删除
|
||||
deleteMonitor(record.id).then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.$notification.success({
|
||||
message: res.msg,
|
||||
duration: 2
|
||||
duration: 2,
|
||||
});
|
||||
this.loadData();
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.filter {
|
||||
|
@ -17,9 +17,11 @@
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
<template slot="operation" slot-scope="text, record">
|
||||
<a-button type="primary" @click="handleTerminal(record)" :disabled="!record.sshId">终端</a-button>
|
||||
<a-button type="primary" @click="handleNode(record)" :disabled="record.openStatus === false">节点管理</a-button>
|
||||
<a-button type="primary" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-tooltip title="需要到编辑中去为一个节点绑定一个 ssh信息才能启用该功能">
|
||||
<a-button type="primary" @click="handleTerminal(record)" :disabled="!record.sshId">终端</a-button>
|
||||
</a-tooltip>
|
||||
<a-button type="danger" @click="handleDelete(record)">删除</a-button>
|
||||
</template>
|
||||
<!-- 嵌套表格 -->
|
||||
|
@ -52,7 +52,9 @@
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
<template slot="operation" v-if="!record.isDirectory" slot-scope="text, record">
|
||||
<a-button type="primary" v-if="record.textFileEdit" @click="handleEditFile(record)">编辑</a-button>
|
||||
<a-tooltip title="需要到 节点管理中的系统管理的白名单配置中配置允许编辑的文件后缀">
|
||||
<a-button type="primary" :disabled="!record.textFileEdit" @click="handleEditFile(record)">编辑</a-button>
|
||||
</a-tooltip>
|
||||
<a-button type="primary" @click="handleDownload(record)">下载</a-button>
|
||||
<a-button type="danger" @click="handleDelete(record)">删除</a-button>
|
||||
</template>
|
||||
@ -164,11 +166,11 @@ export default {
|
||||
unzip: false,
|
||||
},
|
||||
columns: [
|
||||
{ title: "文件名称", dataIndex: "filename", width: 100, ellipsis: true, scopedSlots: { customRender: "filename" } },
|
||||
{ title: "文件名称", dataIndex: "filename", ellipsis: true, scopedSlots: { customRender: "filename" } },
|
||||
{ title: "文件类型", dataIndex: "isDirectory", width: 100, ellipsis: true, scopedSlots: { customRender: "isDirectory" } },
|
||||
{ title: "文件大小", dataIndex: "fileSize", width: 120, ellipsis: true, scopedSlots: { customRender: "fileSize" } },
|
||||
{ title: "修改时间", dataIndex: "modifyTime", width: 180, ellipsis: true },
|
||||
{ title: "操作", dataIndex: "operation", scopedSlots: { customRender: "operation" } },
|
||||
{ title: "操作", dataIndex: "operation", width: 260, scopedSlots: { customRender: "operation" } },
|
||||
],
|
||||
rules: {
|
||||
url: [{ required: true, message: "远程下载Url不为空", trigger: "change" }],
|
||||
|
@ -35,7 +35,9 @@
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
<template slot="operation" slot-scope="text, record">
|
||||
<a-tooltip title="需要到 ssh 信息中配置允许编辑的文件后缀">
|
||||
<a-button type="primary" :disabled="!record.textFileEdit" @click="handleEdit(record)">编辑 </a-button>
|
||||
</a-tooltip>
|
||||
<!-- <a-button type="primary" :disabled="!record.textFileEdit" @click="handlePreview(record)">跟踪</a-button> -->
|
||||
<a-button type="primary" @click="handleDownload(record)">下载</a-button>
|
||||
<a-button type="danger" @click="handleDelete(record)">删除</a-button>
|
||||
@ -92,11 +94,11 @@ export default {
|
||||
key: "key",
|
||||
},
|
||||
columns: [
|
||||
{ title: "文件名称", dataIndex: "title", width: 100, ellipsis: true, scopedSlots: { customRender: "name" } },
|
||||
{ title: "文件名称", dataIndex: "title", ellipsis: true, scopedSlots: { customRender: "name" } },
|
||||
{ title: "文件类型", dataIndex: "dir", width: 100, ellipsis: true, scopedSlots: { customRender: "dir" } },
|
||||
{ title: "文件大小", dataIndex: "size", width: 120, ellipsis: true, scopedSlots: { customRender: "size" } },
|
||||
{ title: "修改时间", dataIndex: "modifyTime", width: 180, ellipsis: true },
|
||||
{ title: "操作", dataIndex: "operation", scopedSlots: { customRender: "operation" }, width: 280 },
|
||||
{ title: "操作", dataIndex: "operation", scopedSlots: { customRender: "operation" }, width: 270 },
|
||||
],
|
||||
editFileVisible: false,
|
||||
};
|
||||
|
@ -41,7 +41,7 @@
|
||||
<a-button type="danger" @click="handleDelete(record)">删除</a-button>
|
||||
</a-menu-item>
|
||||
<a-menu-item>
|
||||
<a-button type="primary" @click="handleViewLog(record)">操作日志</a-button>
|
||||
<a-button type="primary" @click="handleViewLog(record)">终端日志</a-button>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</a-dropdown>
|
||||
@ -83,15 +83,20 @@
|
||||
<template slot="label">
|
||||
文件目录
|
||||
<a-tooltip v-show="temp.type !== 'edit'">
|
||||
<template slot="title">
|
||||
绑定指定目录可以在线管理,同时构建 ssh 发布目录也需要在此配置
|
||||
</template>
|
||||
<template slot="title"> 绑定指定目录可以在线管理,同时构建 ssh 发布目录也需要在此配置 </template>
|
||||
<a-icon type="question-circle" theme="filled" />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-textarea v-model="temp.fileDirs" :auto-size="{ minRows: 3, maxRows: 5 }" placeholder="授权可以直接访问的目录,多个回车换行即可" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="禁止命令" prop="notAllowedCommand">
|
||||
<a-form-model-item prop="notAllowedCommand">
|
||||
<template slot="label">
|
||||
禁止命令
|
||||
<a-tooltip v-show="temp.type !== 'edit'">
|
||||
<template slot="title"> 限制禁止在在线终端执行的命令 </template>
|
||||
<a-icon type="question-circle" theme="filled" />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-textarea v-model="temp.notAllowedCommand" :auto-size="{ minRows: 3, maxRows: 5 }" placeholder="禁止命令是不允许在终端执行的名,多个逗号隔开" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="文件后缀" prop="suffix">
|
||||
@ -156,7 +161,7 @@
|
||||
<terminal v-if="terminalVisible" :sshId="temp.id" />
|
||||
</a-modal>
|
||||
<!-- 操作日志 -->
|
||||
<a-modal v-model="viewOperationLog" title="操作日志" width="60vw" :footer="null" :maskClosable="false">
|
||||
<a-modal v-model="viewOperationLog" title="操作日志" width="80vw" :footer="null" :maskClosable="false">
|
||||
<div ref="filter" class="filter">
|
||||
<a-range-picker class="filter-item" :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss" @change="onchangeListLogTime" />
|
||||
<a-button type="primary" @click="handleListLog">搜索</a-button>
|
||||
|
@ -12,14 +12,7 @@
|
||||
<div class="title">Agent最新版本:{{ agentVersion | version }}</div>
|
||||
<div class="version">打包时间:{{ agentTimeStamp | version }}</div>
|
||||
<div class="action">
|
||||
<a-upload
|
||||
name="file"
|
||||
accept=".jar,.zip"
|
||||
action=""
|
||||
:showUploadList="false"
|
||||
:multiple="false"
|
||||
:before-upload="beforeUpload"
|
||||
>
|
||||
<a-upload name="file" accept=".jar,.zip" action="" :showUploadList="false" :multiple="false" :before-upload="beforeUpload">
|
||||
<a-button type="primary">
|
||||
<a-icon type="upload" />
|
||||
上传新版本
|
||||
@ -29,17 +22,14 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-div">
|
||||
<a-table :columns="columns" :data-source="listComputed" bordered rowKey="id" class="table" :pagination="false"
|
||||
:row-selection="rowSelection">
|
||||
<a-table :columns="columns" :data-source="listComputed" bordered rowKey="id" class="table" :pagination="false" :row-selection="rowSelection">
|
||||
<template slot="version" slot-scope="text">
|
||||
{{ text | version }}
|
||||
</template>
|
||||
<template slot="status" slot-scope="text">
|
||||
<div class="restarting" v-if="text && text.type === 'restarting'">
|
||||
重启中,大约需要30秒
|
||||
</div>
|
||||
<div class="restarting" v-if="text && text.type === 'restarting'">重启中,大约需要30秒</div>
|
||||
<div class="uploading" v-if="text && text.type === 'uploading'">
|
||||
<div class="text">{{ text.percent === 100 ? '上传成功' : '正在上传文件' }}</div>
|
||||
<div class="text">{{ text.percent === 100 ? "上传成功" : "正在上传文件" }}</div>
|
||||
<a-progress :percent="text.percent" />
|
||||
</div>
|
||||
</template>
|
||||
@ -52,49 +42,47 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {getNodeGroupList, uploadAgentFile} from "@/api/node"
|
||||
import { getNodeGroupList, uploadAgentFile } from "@/api/node";
|
||||
import { mapGetters } from "vuex";
|
||||
|
||||
export default {
|
||||
name: "NodeUpdate",
|
||||
filters: {
|
||||
version(value) {
|
||||
return value && value || '未知'
|
||||
return (value && value) || "未知";
|
||||
},
|
||||
status(value) {
|
||||
return value && value || '未知'
|
||||
}
|
||||
return (value && value) || "未知";
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
agentVersion: '',
|
||||
agentTimeStamp: '',
|
||||
agentVersion: "",
|
||||
agentTimeStamp: "",
|
||||
websocket: null,
|
||||
groupFilter: undefined,
|
||||
groupList: [],
|
||||
list: [],
|
||||
columns: [
|
||||
{title: '节点 ID', dataIndex: 'id', ellipsis: true, scopedSlots: {customRender: 'id'}},
|
||||
{title: '节点名称', dataIndex: 'name', ellipsis: true, scopedSlots: {customRender: 'name'}},
|
||||
{title: '分组', dataIndex: 'group', ellipsis: true, scopedSlots: {customRender: 'group'}},
|
||||
{title: 'Agent版本号', dataIndex: 'version', ellipsis: true, scopedSlots: {customRender: 'version'}},
|
||||
{title: '打包时间', dataIndex: 'timeStamp', ellipsis: true, scopedSlots: {customRender: 'timeStamp'}},
|
||||
{title: '状态', dataIndex: 'status', ellipsis: true, scopedSlots: {customRender: 'status'}},
|
||||
{ title: "节点 ID", dataIndex: "id", ellipsis: true, scopedSlots: { customRender: "id" } },
|
||||
{ title: "节点名称", dataIndex: "name", ellipsis: true, scopedSlots: { customRender: "name" } },
|
||||
{ title: "分组", dataIndex: "group", ellipsis: true, scopedSlots: { customRender: "group" } },
|
||||
{ title: "Agent版本号", dataIndex: "version", width: "100", ellipsis: true, scopedSlots: { customRender: "version" } },
|
||||
{ title: "打包时间", dataIndex: "timeStamp", ellipsis: true, scopedSlots: { customRender: "timeStamp" } },
|
||||
{ title: "状态", dataIndex: "status", ellipsis: true, scopedSlots: { customRender: "status" } },
|
||||
// {title: '自动更新', dataIndex: 'autoUpdate', ellipsis: true, scopedSlots: {customRender: 'autoUpdate'}},
|
||||
{title: '操作', dataIndex: 'operation', width: '250px', scopedSlots: {customRender: 'operation'}, align: 'left'}
|
||||
{ title: "操作", dataIndex: "operation", width: "100", scopedSlots: { customRender: "operation" }, align: "left" },
|
||||
],
|
||||
nodeVersion: {},
|
||||
nodeStatus: {},
|
||||
tableSelections: []
|
||||
}
|
||||
tableSelections: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'getLongTermToken'
|
||||
]),
|
||||
...mapGetters(["getLongTermToken"]),
|
||||
listComputed() {
|
||||
const data = []
|
||||
this.list.forEach(item => {
|
||||
const data = [];
|
||||
this.list.forEach((item) => {
|
||||
if (item.group === this.groupFilter || this.groupFilter === undefined) {
|
||||
let itemData = this.nodeVersion[item.id];
|
||||
if (itemData) {
|
||||
@ -107,94 +95,96 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
item.status = this.nodeStatus[item.id]
|
||||
data.push(item)
|
||||
item.status = this.nodeStatus[item.id];
|
||||
data.push(item);
|
||||
}
|
||||
})
|
||||
return data
|
||||
});
|
||||
return data;
|
||||
},
|
||||
rowSelection() {
|
||||
return {
|
||||
onChange: this.tableSelectionChange,
|
||||
selectedRowKeys: this.tableSelections
|
||||
}
|
||||
selectedRowKeys: this.tableSelections,
|
||||
};
|
||||
},
|
||||
socketUrl() {
|
||||
const protocol = location.protocol === 'https:' ? 'wss://' : 'ws://';
|
||||
const protocol = location.protocol === "https:" ? "wss://" : "ws://";
|
||||
return `${protocol}${location.host}/node_update?userId=${this.getLongTermToken}&nodeId=system&type=nodeUpdate`;
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.initWebsocket()
|
||||
this.loadGroupList()
|
||||
this.initWebsocket();
|
||||
this.loadGroupList();
|
||||
},
|
||||
methods: {
|
||||
initWebsocket() {
|
||||
if (!this.socket || this.socket.readyState !== this.socket.OPEN || this.socket.readyState !== this.socket.CONNECTING) {
|
||||
this.socket = new WebSocket(this.socketUrl)
|
||||
this.socket = new WebSocket(this.socketUrl);
|
||||
}
|
||||
|
||||
this.socket.onopen = () => {
|
||||
this.init()
|
||||
}
|
||||
this.init();
|
||||
};
|
||||
this.socket.onmessage = ({ data: socketData }) => {
|
||||
const msgObj = JSON.parse(socketData)
|
||||
const msgObj = JSON.parse(socketData);
|
||||
if (msgObj) {
|
||||
const {command, data, nodeId} = msgObj
|
||||
this[`${command}Result`] && this[`${command}Result`](data, nodeId)
|
||||
}
|
||||
const { command, data, nodeId } = msgObj;
|
||||
this[`${command}Result`] && this[`${command}Result`](data, nodeId);
|
||||
}
|
||||
};
|
||||
},
|
||||
init() {
|
||||
this.getNodeList()
|
||||
this.getAgentVersion()
|
||||
this.getNodeList();
|
||||
this.getAgentVersion();
|
||||
},
|
||||
loadGroupList() {
|
||||
getNodeGroupList().then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
this.groupList = data
|
||||
this.groupList = data;
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
refresh() {
|
||||
if (this.socket) {
|
||||
this.socket.close()
|
||||
this.socket.close();
|
||||
}
|
||||
this.nodeStatus = {}
|
||||
this.nodeVersion = {}
|
||||
this.initWebsocket()
|
||||
this.loadGroupList()
|
||||
this.nodeStatus = {};
|
||||
this.nodeVersion = {};
|
||||
this.initWebsocket();
|
||||
this.loadGroupList();
|
||||
},
|
||||
batchUpdate() {
|
||||
if (this.tableSelections.length === 0) {
|
||||
this.$notification.warning({
|
||||
message: "请选择要升级的节点"
|
||||
})
|
||||
message: "请选择要升级的节点",
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.updateNode()
|
||||
this.updateNode();
|
||||
},
|
||||
updateNodeHandler(record) {
|
||||
this.tableSelections = [record.id]
|
||||
this.updateNode()
|
||||
this.tableSelections = [record.id];
|
||||
this.updateNode();
|
||||
},
|
||||
tableSelectionChange(selectedRowKeys) {
|
||||
this.tableSelections = selectedRowKeys
|
||||
this.tableSelections = selectedRowKeys;
|
||||
},
|
||||
sendMsg(command, params) {
|
||||
this.socket.send(JSON.stringify({
|
||||
this.socket.send(
|
||||
JSON.stringify({
|
||||
command,
|
||||
params
|
||||
}))
|
||||
params,
|
||||
})
|
||||
);
|
||||
},
|
||||
getNodeList() {
|
||||
this.sendMsg('getNodeList')
|
||||
this.sendMsg("getNodeList");
|
||||
},
|
||||
getNodeListResult(data) {
|
||||
this.list = data
|
||||
this.list = data;
|
||||
},
|
||||
getAgentVersion() {
|
||||
this.sendMsg('getAgentVersion')
|
||||
this.sendMsg("getAgentVersion");
|
||||
},
|
||||
getAgentVersionResult(data) {
|
||||
try {
|
||||
@ -202,57 +192,57 @@ export default {
|
||||
this.agentVersion = newData.version;
|
||||
this.agentTimeStamp = newData.timeStamp;
|
||||
} catch (e) {
|
||||
this.agentVersion = data
|
||||
this.agentVersion = data;
|
||||
}
|
||||
},
|
||||
getVersionResult(data, nodeId) {
|
||||
this.nodeVersion = Object.assign({}, this.nodeVersion, {
|
||||
[nodeId]: data
|
||||
})
|
||||
[nodeId]: data,
|
||||
});
|
||||
},
|
||||
onErrorResult(data) {
|
||||
this.$notification.warning({
|
||||
message: data
|
||||
})
|
||||
message: data,
|
||||
});
|
||||
},
|
||||
updateNode() {
|
||||
this.sendMsg("updateNode", {
|
||||
ids: this.tableSelections
|
||||
})
|
||||
this.tableSelections = []
|
||||
ids: this.tableSelections,
|
||||
});
|
||||
this.tableSelections = [];
|
||||
},
|
||||
updateNodeResult(data, nodeId) {
|
||||
const {completeSize, size} = data
|
||||
const { completeSize, size } = data;
|
||||
this.nodeStatus = Object.assign({}, this.nodeStatus, {
|
||||
[nodeId]: {
|
||||
...data,
|
||||
type: 'uploading',
|
||||
percent: (completeSize / size) * 100
|
||||
}
|
||||
})
|
||||
type: "uploading",
|
||||
percent: (completeSize / size) * 100,
|
||||
},
|
||||
});
|
||||
},
|
||||
restartResult(data, nodeId) {
|
||||
this.nodeStatus = Object.assign({}, this.nodeStatus, {
|
||||
[nodeId]: {
|
||||
type: 'restarting'
|
||||
}
|
||||
})
|
||||
type: "restarting",
|
||||
},
|
||||
});
|
||||
},
|
||||
beforeUpload(file) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
formData.append("file", file);
|
||||
uploadAgentFile(formData).then(({ code, msg }) => {
|
||||
if (code === 200) {
|
||||
this.$notification.success({message: "上传成功"})
|
||||
this.getAgentVersion()
|
||||
this.$notification.success({ message: "上传成功" });
|
||||
this.getAgentVersion();
|
||||
} else {
|
||||
this.$notification.error({message: msg})
|
||||
}
|
||||
})
|
||||
return false
|
||||
}
|
||||
}
|
||||
this.$notification.error({ message: msg });
|
||||
}
|
||||
});
|
||||
return false;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="stylus">
|
||||
@ -310,6 +300,4 @@ export default {
|
||||
.ant-select, .ant-btn {
|
||||
margin-right 20px
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
@ -15,16 +15,7 @@
|
||||
<a-button type="primary" @click="loadData">刷新</a-button>
|
||||
</div>
|
||||
<!-- 表格 -->
|
||||
<a-table
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:data-source="list"
|
||||
:style="{ 'max-height': tableHeight + 'px' }"
|
||||
:scroll="{ x: 970, y: tableHeight - 120 }"
|
||||
bordered
|
||||
rowKey="id"
|
||||
:pagination="pagination"
|
||||
>
|
||||
<a-table :loading="loading" :columns="columns" :data-source="list" :style="{ 'max-height': tableHeight + 'px' }" :scroll="{ y: tableHeight - 120 }" bordered rowKey="id" :pagination="pagination">
|
||||
<a-tooltip slot="name" slot-scope="text" placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
@ -117,14 +108,21 @@
|
||||
<a-textarea :auto-size="{ minRows: 3, maxRows: 3 }" v-model="temp.rsaPrv" placeholder="私钥,不填将使用默认的 $HOME/.ssh 目录中的配置。支持配置文件目录:file:"></a-textarea>
|
||||
</a-tooltip>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item v-if="temp.id" label="隐藏字段">
|
||||
<a-button type="danger" @click="restHideField(temp)">清除</a-button>
|
||||
</a-form-model-item>
|
||||
<!-- 公钥暂时没用用到 -->
|
||||
<a-form-model-item label="公钥" prop="rsaPub" v-if="false">
|
||||
<a-textarea :auto-size="{ minRows: 3, maxRows: 3 }" v-model="temp.rsaPub" placeholder="公钥,不填将使用默认的 $HOME/.ssh 目录中的配置。支持配置文件目录:file:"></a-textarea>
|
||||
</a-form-model-item>
|
||||
</template>
|
||||
<a-form-model-item v-if="temp.id">
|
||||
<template slot="label">
|
||||
隐藏字段
|
||||
<a-tooltip>
|
||||
<template slot="title"> 密码字段和私钥字段在编辑的时候不会返回,如果需要重置或者清空就请点我 </template>
|
||||
<a-icon type="question-circle" theme="filled" />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-button style="margin-left: 10px" type="danger" @click="restHideField(temp)">清除</a-button>
|
||||
</a-form-model-item>
|
||||
</a-form-model>
|
||||
</a-modal>
|
||||
</div>
|
||||
@ -186,7 +184,7 @@ export default {
|
||||
{
|
||||
title: "操作",
|
||||
dataIndex: "operation",
|
||||
width: 240,
|
||||
width: 160,
|
||||
scopedSlots: { customRender: "operation" },
|
||||
align: "left",
|
||||
},
|
||||
|
@ -26,7 +26,7 @@
|
||||
</a-upload>
|
||||
<a-button type="primary" class="upload-btn" :disabled="fileList.length === 0" @click="startUpload">上传升级文件</a-button>
|
||||
</a-spin>
|
||||
<div>
|
||||
<div style="margin-top: 20px">
|
||||
<markdown-it-vue class="md-body" :content="changelog" :options="markdownOptions" />
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user