✨ 支持服务器批量指令下发
This commit is contained in:
parent
22c4e2cd46
commit
7513825d28
@ -151,8 +151,8 @@ function initScriptsDB() {
|
|||||||
// eslint-disable-next-line no-async-promise-executor
|
// eslint-disable-next-line no-async-promise-executor
|
||||||
return new Promise(async (resolve) => {
|
return new Promise(async (resolve) => {
|
||||||
let scriptList = await readScriptList()
|
let scriptList = await readScriptList()
|
||||||
let clientInstallScript = 'wget https://mirror.ghproxy.com/https://raw.githubusercontent.com/chaos-zhu/easynode/main/client/easynode-client-install.sh | bash'
|
let clientInstallScript = 'wget https://mirror.ghproxy.com/https://raw.githubusercontent.com/chaos-zhu/easynode/main/client/easynode-client-install.sh && sh easynode-client-install.sh'
|
||||||
let clientUninstallScript = 'wget https://mirror.ghproxy.com/https://raw.githubusercontent.com/chaos-zhu/easynode/main/client/easynode-client-uninstall.sh | bash'
|
let clientUninstallScript = 'wget https://mirror.ghproxy.com/https://raw.githubusercontent.com/chaos-zhu/easynode/main/client/easynode-client-uninstall.sh && sh easynode-client-uninstall.sh'
|
||||||
let clientVersion = process.env.CLIENT_VERSION
|
let clientVersion = process.env.CLIENT_VERSION
|
||||||
consola.info('客户端版本:', clientVersion)
|
consola.info('客户端版本:', clientVersion)
|
||||||
let installId = `clientInstall${ clientVersion }`
|
let installId = `clientInstall${ clientVersion }`
|
||||||
@ -162,12 +162,18 @@ function initScriptsDB() {
|
|||||||
let isClientUninstall = scriptList?.find(script => script._id = uninstallId)
|
let isClientUninstall = scriptList?.find(script => script._id = uninstallId)
|
||||||
let writeFlag = false
|
let writeFlag = false
|
||||||
if (!isClientInstall) {
|
if (!isClientInstall) {
|
||||||
scriptList.push({ _id: installId, name: `easynode-client-${ clientVersion }安装脚本`, remark: '系统内置|重启生成', content: clientInstallScript, index: 99 })
|
console.info('初始化客户端安装脚本')
|
||||||
|
scriptList.push({ _id: installId, name: `easynode-客户端-${ clientVersion }安装脚本`, remark: '系统内置|重启生成', content: clientInstallScript, index: 1 })
|
||||||
writeFlag = true
|
writeFlag = true
|
||||||
|
} else {
|
||||||
|
console.info('客户端安装脚本已存在')
|
||||||
}
|
}
|
||||||
if (!isClientUninstall) {
|
if (!isClientUninstall) {
|
||||||
scriptList.push({ _id: uninstallId, name: `easynode-client-${ clientVersion }卸载脚本`, remark: '系统内置|重启生成', content: clientUninstallScript, index: 98 })
|
console.info('初始化客户端卸载脚本')
|
||||||
|
scriptList.push({ _id: uninstallId, name: `easynode-客户端-${ clientVersion }卸载脚本`, remark: '系统内置|重启生成', content: clientUninstallScript, index: 0 })
|
||||||
writeFlag = true
|
writeFlag = true
|
||||||
|
} else {
|
||||||
|
console.info('客户端卸载脚本已存在')
|
||||||
}
|
}
|
||||||
if (writeFlag) await writeScriptList(scriptList)
|
if (writeFlag) await writeScriptList(scriptList)
|
||||||
resolve()
|
resolve()
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
const { Server } = require('socket.io')
|
const { Server } = require('socket.io')
|
||||||
const { Client: SSHClient } = require('ssh2')
|
const { Client: SSHClient } = require('ssh2')
|
||||||
const { readHostList, readSSHRecord, verifyAuthSync, AESDecryptSync, writeOneKeyRecord, throttle } = require('../utils')
|
const { readHostList, readSSHRecord, verifyAuthSync, AESDecryptSync, writeOneKeyRecord, shellThrottle } = require('../utils')
|
||||||
|
|
||||||
const execStatusEnum = {
|
const execStatusEnum = {
|
||||||
connecting: '连接中',
|
connecting: '连接中',
|
||||||
@ -27,10 +27,17 @@ function disconnectAllExecClient() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function execShell(socket, sshClient, curRes, resolve) {
|
function execShell(socket, sshClient, curRes, resolve) {
|
||||||
const throttledDataHandler = throttle((data) => {
|
const throttledDataHandler = shellThrottle(() => {
|
||||||
curRes.status = execStatusEnum.executing
|
|
||||||
curRes.result += data?.toString() || ''
|
|
||||||
socket.emit('output', execResult)
|
socket.emit('output', execResult)
|
||||||
|
// const memoryUsage = process.memoryUsage()
|
||||||
|
// const formattedMemoryUsage = {
|
||||||
|
// rss: (memoryUsage.rss / 1024 / 1024).toFixed(2) + ' MB', // Resident Set Size: total memory allocated for the process execution
|
||||||
|
// heapTotal: (memoryUsage.heapTotal / 1024 / 1024).toFixed(2) + ' MB', // Total size of the allocated heap
|
||||||
|
// heapUsed: (memoryUsage.heapUsed / 1024 / 1024).toFixed(2) + ' MB', // Actual memory used during the execution
|
||||||
|
// external: (memoryUsage.external / 1024 / 1024).toFixed(2) + ' MB', // Memory used by "external" components like V8 external memory
|
||||||
|
// arrayBuffers: (memoryUsage.arrayBuffers / 1024 / 1024).toFixed(2) + ' MB' // Memory allocated for ArrayBuffer and SharedArrayBuffer, including all Node.js Buffers
|
||||||
|
// }
|
||||||
|
// console.log(formattedMemoryUsage)
|
||||||
}, 500) // 防止内存爆破
|
}, 500) // 防止内存爆破
|
||||||
sshClient.exec(curRes.command, function(err, stream) {
|
sshClient.exec(curRes.command, function(err, stream) {
|
||||||
if (err) {
|
if (err) {
|
||||||
@ -41,8 +48,9 @@ function execShell(socket, sshClient, curRes, resolve) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
stream
|
stream
|
||||||
.on('close', () => {
|
.on('close', async () => {
|
||||||
throttledDataHandler.flush()
|
// ssh连接关闭后,再执行一次输出,防止最后一次节流函数发生在延迟时间内导致终端的输出数据丢失
|
||||||
|
await throttledDataHandler.last() // 等待最后一次节流函数执行完成,再执行一次数据输出
|
||||||
// console.log('onekey终端执行完成, 关闭连接: ', curRes.host)
|
// console.log('onekey终端执行完成, 关闭连接: ', curRes.host)
|
||||||
if (curRes.status === execStatusEnum.executing) {
|
if (curRes.status === execStatusEnum.executing) {
|
||||||
curRes.status = execStatusEnum.execSuccess
|
curRes.status = execStatusEnum.execSuccess
|
||||||
@ -53,16 +61,16 @@ function execShell(socket, sshClient, curRes, resolve) {
|
|||||||
})
|
})
|
||||||
.on('data', (data) => {
|
.on('data', (data) => {
|
||||||
// console.log(curRes.host, '执行中: \n' + data)
|
// console.log(curRes.host, '执行中: \n' + data)
|
||||||
// curRes.status = execStatusEnum.executing
|
curRes.status = execStatusEnum.executing
|
||||||
// curRes.result += data.toString()
|
curRes.result += data.toString()
|
||||||
// socket.emit('output', execResult)
|
// socket.emit('output', execResult)
|
||||||
throttledDataHandler(data)
|
throttledDataHandler(data)
|
||||||
})
|
})
|
||||||
.stderr
|
.stderr
|
||||||
.on('data', (data) => {
|
.on('data', (data) => {
|
||||||
// console.log(curRes.host, '命令执行过程中产生错误: ' + data)
|
// console.log(curRes.host, '命令执行过程中产生错误: ' + data)
|
||||||
// curRes.status = execStatusEnum.executing
|
curRes.status = execStatusEnum.executing
|
||||||
// curRes.result += data.toString()
|
curRes.result += data.toString()
|
||||||
// socket.emit('output', execResult)
|
// socket.emit('output', execResult)
|
||||||
throttledDataHandler(data)
|
throttledDataHandler(data)
|
||||||
})
|
})
|
||||||
@ -147,6 +155,7 @@ module.exports = (httpServer) => {
|
|||||||
consola.error('onekey终端连接失败:', err.level)
|
consola.error('onekey终端连接失败:', err.level)
|
||||||
curRes.status = execStatusEnum.connectFail
|
curRes.status = execStatusEnum.connectFail
|
||||||
curRes.result += err.message
|
curRes.result += err.message
|
||||||
|
socket.emit('output', execResult)
|
||||||
resolve(curRes)
|
resolve(curRes)
|
||||||
})
|
})
|
||||||
.connect({
|
.connect({
|
||||||
@ -157,6 +166,7 @@ module.exports = (httpServer) => {
|
|||||||
consola.error('创建终端错误:', err.message)
|
consola.error('创建终端错误:', err.message)
|
||||||
curRes.status = execStatusEnum.connectFail
|
curRes.status = execStatusEnum.connectFail
|
||||||
curRes.result += err.message
|
curRes.result += err.message
|
||||||
|
socket.emit('output', execResult)
|
||||||
resolve(curRes)
|
resolve(curRes)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -21,7 +21,7 @@ const {
|
|||||||
} = require('./storage')
|
} = require('./storage')
|
||||||
const { RSADecryptSync, AESEncryptSync, AESDecryptSync, SHA1Encrypt } = require('./encrypt')
|
const { RSADecryptSync, AESEncryptSync, AESDecryptSync, SHA1Encrypt } = require('./encrypt')
|
||||||
const { verifyAuthSync, isProd } = require('./verify-auth')
|
const { verifyAuthSync, isProd } = require('./verify-auth')
|
||||||
const { getNetIPInfo, throwError, isIP, randomStr, getUTCDate, formatTimestamp, throttle } = require('./tools')
|
const { getNetIPInfo, throwError, isIP, randomStr, getUTCDate, formatTimestamp, shellThrottle } = require('./tools')
|
||||||
const { emailTransporter, sendEmailToConfList } = require('./email')
|
const { emailTransporter, sendEmailToConfList } = require('./email')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@ -31,7 +31,7 @@ module.exports = {
|
|||||||
randomStr,
|
randomStr,
|
||||||
getUTCDate,
|
getUTCDate,
|
||||||
formatTimestamp,
|
formatTimestamp,
|
||||||
throttle,
|
shellThrottle,
|
||||||
verifyAuthSync,
|
verifyAuthSync,
|
||||||
isProd,
|
isProd,
|
||||||
RSADecryptSync,
|
RSADecryptSync,
|
||||||
|
@ -204,40 +204,25 @@ function resolvePath(dir, path) {
|
|||||||
return path.resolve(dir, path)
|
return path.resolve(dir, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
function throttle(func, limit) {
|
let shellThrottle = (fn, delay = 1000) => {
|
||||||
let lastFunc
|
let timer = null
|
||||||
let lastRan
|
let args = null
|
||||||
let pendingArgs = null
|
function throttled() {
|
||||||
|
args = arguments
|
||||||
const runner = () => {
|
if (!timer) {
|
||||||
func.apply(this, pendingArgs)
|
timer = setTimeout(() => {
|
||||||
lastRan = Date.now()
|
fn(...args)
|
||||||
pendingArgs = null
|
timer = null
|
||||||
}
|
}, delay)
|
||||||
|
|
||||||
const throttled = function() {
|
|
||||||
const context = this
|
|
||||||
const args = arguments
|
|
||||||
pendingArgs = args
|
|
||||||
if (!lastRan || (Date.now() - lastRan >= limit)) {
|
|
||||||
if (lastFunc) {
|
|
||||||
clearTimeout(lastFunc)
|
|
||||||
}
|
|
||||||
runner.apply(context, args)
|
|
||||||
} else {
|
|
||||||
clearTimeout(lastFunc)
|
|
||||||
lastFunc = setTimeout(() => {
|
|
||||||
runner.apply(context, args)
|
|
||||||
}, limit - (Date.now() - lastRan))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function delayMs() {
|
||||||
throttled.flush = () => {
|
return new Promise(resolve => setTimeout(resolve, delay))
|
||||||
if (pendingArgs) {
|
}
|
||||||
runner.apply(this, pendingArgs)
|
throttled.last = async () => {
|
||||||
}
|
await delayMs()
|
||||||
|
fn(...args)
|
||||||
}
|
}
|
||||||
|
|
||||||
return throttled
|
return throttled
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,5 +234,5 @@ module.exports = {
|
|||||||
getUTCDate,
|
getUTCDate,
|
||||||
formatTimestamp,
|
formatTimestamp,
|
||||||
resolvePath,
|
resolvePath,
|
||||||
throttle
|
shellThrottle
|
||||||
}
|
}
|
@ -4,8 +4,8 @@
|
|||||||
"description": "easynode-server",
|
"description": "easynode-server",
|
||||||
"bin": "./bin/www",
|
"bin": "./bin/www",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"local": "cross-env EXEC_ENV=local nodemon ./app/index.js --max-old-space-size=4096",
|
"local": "cross-env EXEC_ENV=local nodemon index.js",
|
||||||
"prod": "cross-env EXEC_ENV=production nodemon ./app/index.js",
|
"prod": "cross-env EXEC_ENV=production nodemon index.js",
|
||||||
"start": "node ./index.js",
|
"start": "node ./index.js",
|
||||||
"lint": "eslint . --ext .js,.vue",
|
"lint": "eslint . --ext .js,.vue",
|
||||||
"lint:fix": "eslint . --ext .js,.jsx,.cjs,.mjs --fix"
|
"lint:fix": "eslint . --ext .js,.jsx,.cjs,.mjs --fix"
|
||||||
|
@ -61,35 +61,31 @@ const useStore = defineStore({
|
|||||||
// console.log('scriptList:', scriptList)
|
// console.log('scriptList:', scriptList)
|
||||||
this.$patch({ scriptList })
|
this.$patch({ scriptList })
|
||||||
},
|
},
|
||||||
getHostPing() {
|
// getHostPing() {
|
||||||
setTimeout(() => {
|
// setInterval(() => {
|
||||||
this.hostList.forEach((item) => {
|
// this.hostList.forEach((item) => {
|
||||||
const { host } = item
|
// const { host } = item
|
||||||
ping(`http://${ host }:${ this.$clientPort }`)
|
// ping(`http://${ host }:${ this.$clientPort }`)
|
||||||
.then((res) => {
|
// .then((res) => {
|
||||||
item.ping = res
|
// item.ping = res
|
||||||
})
|
// })
|
||||||
})
|
// })
|
||||||
// console.clear()
|
// }, 2000)
|
||||||
// console.warn('Please tick \'Preserve Log\'')
|
// },
|
||||||
}, 1500)
|
|
||||||
},
|
|
||||||
async wsHostStatus() {
|
async wsHostStatus() {
|
||||||
if (this.HostStatusSocket) this.HostStatusSocket.close()
|
if (this.HostStatusSocket) this.HostStatusSocket.close()
|
||||||
let socketInstance = io(this.serviceURI, {
|
let socketInstance = io(this.serviceURI, {
|
||||||
path: '/clients',
|
path: '/clients',
|
||||||
forceNew: true,
|
forceNew: true,
|
||||||
reconnectionDelay: 5000,
|
reconnectionDelay: 5000,
|
||||||
reconnectionAttempts: 2
|
reconnectionAttempts: 3
|
||||||
})
|
})
|
||||||
this.HostStatusSocket = socketInstance
|
this.HostStatusSocket = socketInstance
|
||||||
socketInstance.on('connect', () => {
|
socketInstance.on('connect', () => {
|
||||||
let flag = 5
|
|
||||||
console.log('clients websocket 已连接: ', socketInstance.id)
|
console.log('clients websocket 已连接: ', socketInstance.id)
|
||||||
let token = this.token
|
let token = this.token
|
||||||
socketInstance.emit('init_clients_data', { token })
|
socketInstance.emit('init_clients_data', { token })
|
||||||
socketInstance.on('clients_data', (data) => {
|
socketInstance.on('clients_data', (data) => {
|
||||||
if ((flag++ % 5) === 0) this.getHostPing()
|
|
||||||
this.hostList.forEach(item => {
|
this.hostList.forEach(item => {
|
||||||
const { host } = item
|
const { host } = item
|
||||||
if (data[host] === null) return { ...item }
|
if (data[host] === null) return { ...item }
|
||||||
|
@ -36,15 +36,15 @@
|
|||||||
>
|
>
|
||||||
<el-form-item label="凭证名称" prop="name">
|
<el-form-item label="凭证名称" prop="name">
|
||||||
<el-input
|
<el-input
|
||||||
v-model.trim="sshForm.name"
|
v-model="sshForm.name"
|
||||||
clearable
|
clearable
|
||||||
placeholder=""
|
placeholder=""
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="认证方式" prop="type">
|
<el-form-item label="认证方式" prop="type">
|
||||||
<el-radio v-model.trim="sshForm.authType" value="privateKey">密钥</el-radio>
|
<el-radio v-model="sshForm.authType" value="privateKey">密钥</el-radio>
|
||||||
<el-radio v-model.trim="sshForm.authType" value="password">密码</el-radio>
|
<el-radio v-model="sshForm.authType" value="password">密码</el-radio>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="sshForm.authType === 'privateKey'" prop="privateKey" label="密钥">
|
<el-form-item v-if="sshForm.authType === 'privateKey'" prop="privateKey" label="密钥">
|
||||||
<el-button type="primary" size="small" @click="handleClickUploadBtn">
|
<el-button type="primary" size="small" @click="handleClickUploadBtn">
|
||||||
@ -58,7 +58,7 @@
|
|||||||
@change="handleSelectPrivateKeyFile"
|
@change="handleSelectPrivateKeyFile"
|
||||||
>
|
>
|
||||||
<el-input
|
<el-input
|
||||||
v-model.trim="sshForm.privateKey"
|
v-model="sshForm.privateKey"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
:rows="5"
|
:rows="5"
|
||||||
clearable
|
clearable
|
||||||
|
@ -54,7 +54,7 @@
|
|||||||
>
|
>
|
||||||
<el-form-item label="分组名称" prop="name">
|
<el-form-item label="分组名称" prop="name">
|
||||||
<el-input
|
<el-input
|
||||||
v-model.trim="groupForm.name"
|
v-model="groupForm.name"
|
||||||
clearable
|
clearable
|
||||||
placeholder=""
|
placeholder=""
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
|
@ -1,10 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="onekey_container">
|
<div class="onekey_container">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<el-button type="primary" @click="addOnekey">
|
<el-button
|
||||||
批量下发指令
|
type="primary"
|
||||||
|
:disabled="isExecuting"
|
||||||
|
:loading="isExecuting"
|
||||||
|
@click="addOnekey"
|
||||||
|
>
|
||||||
|
{{ isExecuting ? `执行中,剩余${timeRemaining}秒` : '批量下发指令' }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-show="recordList.length" type="danger" @click="handleRemoveAll">
|
<el-button
|
||||||
|
v-show="recordList.length"
|
||||||
|
:disabled="isExecuting"
|
||||||
|
type="danger"
|
||||||
|
@click="handleRemoveAll"
|
||||||
|
>
|
||||||
删除全部记录
|
删除全部记录
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
@ -28,7 +38,7 @@
|
|||||||
<span style="letter-spacing: 2px;"> {{ row.host }} </span>
|
<span style="letter-spacing: 2px;"> {{ row.host }} </span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="command" label="指令">
|
<el-table-column prop="command" label="指令" show-overflow-tooltip>
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span style="letter-spacing: 2px;background: rgba(227, 230, 235, 0.7);color: rgb(54, 52, 52);"> {{ row.command }} </span>
|
<span style="letter-spacing: 2px;background: rgba(227, 230, 235, 0.7);color: rgb(54, 52, 52);"> {{ row.command }} </span>
|
||||||
</template>
|
</template>
|
||||||
@ -125,7 +135,7 @@
|
|||||||
:rows="5"
|
:rows="5"
|
||||||
clearable
|
clearable
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
placeholder="shell script"
|
placeholder="shell script, ex: ping -c 10 google.com"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -166,17 +176,21 @@ let penddingRecord = ref([])
|
|||||||
let checkAll = ref(false)
|
let checkAll = ref(false)
|
||||||
let indeterminate = ref(false)
|
let indeterminate = ref(false)
|
||||||
const updateFormRef = ref(null)
|
const updateFormRef = ref(null)
|
||||||
|
let timeRemaining = ref(0)
|
||||||
|
const isClient = ref(false)
|
||||||
|
|
||||||
let formData = reactive({
|
let formData = reactive({
|
||||||
hosts: [],
|
hosts: [],
|
||||||
command: 'ping -c 10 google.com',
|
command: '',
|
||||||
timeout: 60
|
timeout: 120
|
||||||
})
|
})
|
||||||
|
|
||||||
const token = computed(() => $store.token)
|
const token = computed(() => $store.token)
|
||||||
const hostList = computed(() => $store.hostList)
|
const hostList = computed(() => $store.hostList)
|
||||||
let scriptList = computed(() => $store.scriptList)
|
let scriptList = computed(() => $store.scriptList)
|
||||||
|
let isExecuting = computed(() => timeRemaining.value > 0)
|
||||||
const hasConfigHostList = computed(() => hostList.value.filter(item => item.isConfig))
|
const hasConfigHostList = computed(() => hostList.value.filter(item => item.isConfig))
|
||||||
|
|
||||||
const tableData = computed(() => {
|
const tableData = computed(() => {
|
||||||
return penddingRecord.value.concat(recordList.value).map(item => {
|
return penddingRecord.value.concat(recordList.value).map(item => {
|
||||||
item.loading = false
|
item.loading = false
|
||||||
@ -210,12 +224,17 @@ watch(() => formData.hosts, (val) => {
|
|||||||
|
|
||||||
const createExecShell = (hosts = [], command = 'ls', timeout = 60) => {
|
const createExecShell = (hosts = [], command = 'ls', timeout = 60) => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
|
timeRemaining.value = Number(formData.timeout)
|
||||||
|
let timer = null
|
||||||
socket.value = io($serviceURI, {
|
socket.value = io($serviceURI, {
|
||||||
path: '/onekey',
|
path: '/onekey',
|
||||||
forceNew: false,
|
forceNew: false,
|
||||||
reconnectionAttempts: 1
|
reconnectionAttempts: 1
|
||||||
})
|
})
|
||||||
socket.value.on('connect', () => {
|
socket.value.on('connect', () => {
|
||||||
|
timer = setInterval(() => {
|
||||||
|
timeRemaining.value -= 1
|
||||||
|
}, 1000)
|
||||||
console.log('onekey socket已连接:', socket.value.id)
|
console.log('onekey socket已连接:', socket.value.id)
|
||||||
|
|
||||||
socket.value.on('ready', () => {
|
socket.value.on('ready', () => {
|
||||||
@ -274,6 +293,10 @@ const createExecShell = (hosts = [], command = 'ls', timeout = 60) => {
|
|||||||
|
|
||||||
socket.value.on('disconnect', () => {
|
socket.value.on('disconnect', () => {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
timeRemaining.value = 0
|
||||||
|
if (isClient.value) $store.getHostList() // 如果是客户端安装/卸载脚本,更新下host
|
||||||
|
isClient.value = false
|
||||||
|
clearInterval(timer)
|
||||||
console.warn('onekey websocket 连接断开')
|
console.warn('onekey websocket 连接断开')
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -302,6 +325,7 @@ let selectAllHost = (val) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let handleImportScript = (scriptObj) => {
|
let handleImportScript = (scriptObj) => {
|
||||||
|
isClient.value = scriptObj.id.startsWith('client')
|
||||||
formData.command = scriptObj.content
|
formData.command = scriptObj.content
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
>
|
>
|
||||||
<el-form-item label="名称" prop="name">
|
<el-form-item label="名称" prop="name">
|
||||||
<el-input
|
<el-input
|
||||||
v-model.trim="formData.name"
|
v-model="formData.name"
|
||||||
clearable
|
clearable
|
||||||
placeholder=""
|
placeholder=""
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
@ -42,7 +42,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="备注" prop="remark">
|
<el-form-item label="备注" prop="remark">
|
||||||
<el-input
|
<el-input
|
||||||
v-model.trim="formData.remark"
|
v-model="formData.remark"
|
||||||
clearable
|
clearable
|
||||||
placeholder=""
|
placeholder=""
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
@ -58,7 +58,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item prop="content" label="内容">
|
<el-form-item prop="content" label="内容">
|
||||||
<el-input
|
<el-input
|
||||||
v-model.trim="formData.content"
|
v-model="formData.content"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
:rows="5"
|
:rows="5"
|
||||||
clearable
|
clearable
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item key="name" label="名称" prop="name">
|
<el-form-item key="name" label="名称" prop="name">
|
||||||
<el-input
|
<el-input
|
||||||
v-model.trim="hostForm.name"
|
v-model="hostForm.name"
|
||||||
clearable
|
clearable
|
||||||
placeholder=""
|
placeholder=""
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
@ -79,9 +79,9 @@
|
|||||||
</el-autocomplete>
|
</el-autocomplete>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item key="authType" label="认证方式" prop="authType">
|
<el-form-item key="authType" label="认证方式" prop="authType">
|
||||||
<el-radio v-model.trim="hostForm.authType" value="privateKey">密钥</el-radio>
|
<el-radio v-model="hostForm.authType" value="privateKey">密钥</el-radio>
|
||||||
<el-radio v-model.trim="hostForm.authType" value="password">密码</el-radio>
|
<el-radio v-model="hostForm.authType" value="password">密码</el-radio>
|
||||||
<el-radio v-model.trim="hostForm.authType" value="credential">凭据</el-radio>
|
<el-radio v-model="hostForm.authType" value="credential">凭据</el-radio>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item
|
<el-form-item
|
||||||
v-if="hostForm.authType === 'privateKey'"
|
v-if="hostForm.authType === 'privateKey'"
|
||||||
@ -103,7 +103,7 @@
|
|||||||
@change="handleSelectPrivateKeyFile"
|
@change="handleSelectPrivateKeyFile"
|
||||||
>
|
>
|
||||||
<el-input
|
<el-input
|
||||||
v-model.trim="hostForm.privateKey"
|
v-model="hostForm.privateKey"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
:rows="5"
|
:rows="5"
|
||||||
clearable
|
clearable
|
||||||
@ -206,7 +206,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item key="remark" label="备注" prop="remark">
|
<el-form-item key="remark" label="备注" prop="remark">
|
||||||
<el-input
|
<el-input
|
||||||
v-model.trim="hostForm.remark"
|
v-model="hostForm.remark"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
:rows="3"
|
:rows="3"
|
||||||
clearable
|
clearable
|
||||||
|
@ -25,15 +25,14 @@
|
|||||||
<!-- <div size="small">{{ ipInfo.country || '--' }} {{ ipInfo.regionName }} {{ ipInfo.city }}</div> -->
|
<!-- <div size="small">{{ ipInfo.country || '--' }} {{ ipInfo.regionName }} {{ ipInfo.city }}</div> -->
|
||||||
<div size="small">{{ ipInfo.country || '--' }} {{ ipInfo.regionName }}</div>
|
<div size="small">{{ ipInfo.country || '--' }} {{ ipInfo.regionName }}</div>
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item>
|
<!-- <el-descriptions-item>
|
||||||
<template #label>
|
<template #label>
|
||||||
<div class="item-title">
|
<div class="item-title">
|
||||||
延迟
|
延迟
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<span style="margin-right: 10px;" class="host-ping">{{ ping }}</span>
|
<span style="margin-right: 10px;" class="host-ping">{{ ping }}</span>
|
||||||
<!-- <span>(http)</span> -->
|
</el-descriptions-item> -->
|
||||||
</el-descriptions-item>
|
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
|
|
||||||
<!-- <el-divider content-position="center">实时监控</el-divider> -->
|
<!-- <el-divider content-position="center">实时监控</el-divider> -->
|
||||||
|
Loading…
x
Reference in New Issue
Block a user