From 54f54c1f55d500cc405c93b80bf87e78a4c6d3ba Mon Sep 17 00:00:00 2001 From: chaos-zhu Date: Sun, 4 Aug 2024 04:17:16 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=20=E4=BC=98=E5=8C=96=E7=9B=91?= =?UTF-8?q?=E6=8E=A7=E6=9C=8D=E5=8A=A1,=E6=97=A0=E6=84=9F=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/app/controller/host.js | 5 +- server/app/socket/clients.js | 72 +++++++++++-------- web/src/store/index.js | 14 ++-- .../views/server/components/host-table.vue | 21 +++--- .../views/terminal/components/info-side.vue | 40 +---------- 5 files changed, 63 insertions(+), 89 deletions(-) diff --git a/server/app/controller/host.js b/server/app/controller/host.js index f590b35..1a0a84a 100644 --- a/server/app/controller/host.js +++ b/server/app/controller/host.js @@ -6,7 +6,7 @@ async function getHostList({ res }) { data?.sort((a, b) => Number(b.index || 0) - Number(a.index || 0)) for (const item of data) { let { username, port, authType, _id: id, credential } = item - console.log('解密凭证title: ', credential) + // console.log('解密凭证title: ', credential) if (credential) credential = await AESDecryptSync(credential) const isConfig = Boolean(username && port && (item[authType])) Object.assign(item, { id, isConfig, password: '', privateKey: '', credential }) @@ -51,11 +51,10 @@ async function updateHost({ res, request }) { } } = request let isBatch = Array.isArray(hosts) - console.log('isBatch:', isBatch) if (isBatch) { if (!hosts.length) return res.fail({ msg: 'hosts为空' }) let hostList = await readHostList() - console.log('批量修改: ', isBatch) + // console.log('批量修改实例') let newHostList = [] for (let oldRecord of hostList) { let record = hosts.find(item => item.host === oldRecord.host) diff --git a/server/app/socket/clients.js b/server/app/socket/clients.js index e1f235f..7237ca2 100644 --- a/server/app/socket/clients.js +++ b/server/app/socket/clients.js @@ -1,12 +1,13 @@ const { Server: ServerIO } = require('socket.io') -const { io: ClientIO } = require('socket.io-client') +const { io: ClientIO, connect } = require('socket.io-client') const { readHostList } = require('../utils') const { clientPort } = require('../config') const { verifyAuthSync } = require('../utils') -let clientSockets = {}, clientsData = {} - -async function getClientsInfo(socketId) { +let clientsData = {} +async function getClientsInfo(clientSockets, clear = false) { + clientSockets = [] + if (clear) clientsData = {} let hostList = await readHostList() hostList ?.map(({ host, name }) => { @@ -14,11 +15,11 @@ async function getClientsInfo(socketId) { path: '/client/os-info', forceNew: true, timeout: 5000, - reconnectionDelay: 3000, - reconnectionAttempts: 3 + reconnectionDelay: 5000, + reconnectionAttempts: 1000 }) // 将与客户端连接的socket实例保存起来,web端断开时关闭这些连接 - clientSockets[socketId].push(clientSocket) + clientSockets.push(clientSocket) return { host, name, @@ -26,23 +27,37 @@ async function getClientsInfo(socketId) { } }) .map(({ host, name, clientSocket }) => { + clientsData[host] = { connect: false } clientSocket .on('connect', () => { consola.success('client connect success:', host, name) clientSocket.on('client_data', (osData) => { - clientsData[host] = osData + try { + // clientsData[host] = { connect: true, osData: JSON.parse(osData) } + clientsData[host] = { connect: true, ...osData } + } catch (error) { + console.warn('client_data, parse osData error: ', error.message) + } }) clientSocket.on('client_error', (error) => { - clientsData[host] = error + clientsData[host] = { connect: true, error: `client_error: ${ error }` } }) }) - .on('connect_error', (error) => { + .on('connect_error', (error) => { // 连接失败 // consola.error('client connect fail:', host, name, error.message) - clientsData[host] = null + try { + clientsData[host] = { connect: false, error: `client_connect_error: ${ error }` } + } catch (error) { + console.warn('connect_error: ', error.message) + } }) - .on('disconnect', () => { - consola.info('client connect disconnect:', host, name) - clientsData[host] = null + .on('disconnect', (error) => { // 一方主动断开连接 + // consola.info('client connect disconnect:', host, name) + try { + clientsData[host] = { connect: false, error: `client_disconnect: ${ error }` } + } catch (error) { + console.warn('disconnect: ', error.message) + } }) }) } @@ -59,7 +74,6 @@ module.exports = (httpServer) => { // 前者兼容nginx反代, 后者兼容nodejs自身服务 let clientIp = socket.handshake.headers['x-forwarded-for'] || socket.handshake.address socket.on('init_clients_data', async ({ token }) => { - // 校验登录态 const { code, msg } = await verifyAuthSync(token, clientIp) if (code !== 1) { socket.emit('token_verify_fail', msg || '鉴权失败') @@ -67,30 +81,28 @@ module.exports = (httpServer) => { return } - // 收集web端连接的id - clientSockets[socket.id] = [] - consola.info('client连接socketId: ', socket.id, 'clients-socket已连接数: ', Object.keys(clientSockets).length) + let clientSockets = [] + clientSockets.push(socket) - // 获取客户端数据 - getClientsInfo(socket.id) - - // 立即推送一次 + getClientsInfo(clientSockets, true) socket.emit('clients_data', clientsData) - // 向web端推送数据 + socket.on('refresh_clients_data', async () => { + consola.info('refresh clients-socket: ', clientSockets.length) + getClientsInfo(clientSockets, false) + }) + let timer = null timer = setInterval(() => { socket.emit('clients_data', clientsData) - }, 1000) + }, 1500) - // 关闭连接 socket.on('disconnect', () => { - // 防止内存泄漏 if (timer) clearInterval(timer) - // 当web端与服务端断开连接时, 服务端与每个客户端的socket也应该断开连接 - clientSockets[socket.id].forEach(socket => socket.close && socket.close()) - delete clientSockets[socket.id] - consola.info('断开socketId: ', socket.id, 'clients-socket剩余连接数: ', Object.keys(clientSockets).length) + clientSockets.forEach(socket => socket.close && socket.close()) + clientSockets = null + clientsData = null + consola.info('clients-socket 连接断开: ', socket.id) }) }) }) diff --git a/web/src/store/index.js b/web/src/store/index.js index 2dd8e4e..76201af 100644 --- a/web/src/store/index.js +++ b/web/src/store/index.js @@ -40,32 +40,27 @@ const useStore = defineStore({ await this.getHostList() await this.getSSHList() await this.getScriptList() - // await this.getLocalScriptList() + this.wsHostStatus() }, async getHostList() { const { data: hostList } = await $api.getHostList() - // console.log('hostList:', hostList) this.$patch({ hostList }) - this.wsHostStatus() + this.HostStatusSocket?.emit('refresh_clients_data') }, async getGroupList() { const { data: groupList } = await $api.getGroupList() - // console.log('groupList:', groupList) this.$patch({ groupList }) }, async getSSHList() { const { data: sshList } = await $api.getSSHList() - // console.log('sshList:', sshList) this.$patch({ sshList }) }, async getScriptList() { const { data: scriptList } = await $api.getScriptList() - // console.log('scriptList:', scriptList) this.$patch({ scriptList }) }, async getLocalScriptList() { const { data: localScriptList } = await $api.getLocalScriptList() - // console.log('localScriptList:', localScriptList) this.$patch({ localScriptList }) }, // getHostPing() { @@ -93,10 +88,11 @@ const useStore = defineStore({ let token = this.token socketInstance.emit('init_clients_data', { token }) socketInstance.on('clients_data', (data) => { + // console.log(data) this.hostList.forEach(item => { const { host } = item - if (data[host] === null) return { ...item } - return Object.assign(item, data[host]) + if (data[host] === null) return + return Object.assign(item, { monitorData: data[host] }) }) }) socketInstance.on('token_verify_fail', (message) => { diff --git a/web/src/views/server/components/host-table.vue b/web/src/views/server/components/host-table.vue index df85625..c964ce5 100644 --- a/web/src/views/server/components/host-table.vue +++ b/web/src/views/server/components/host-table.vue @@ -14,7 +14,7 @@ --> @@ -59,14 +59,19 @@ const emit = defineEmits(['update-list', 'update-host', 'select-change',]) let tableData = ref([]) -watch(() => props.hosts, (newVal) => { - console.log('newVal:', newVal) - tableData.value = newVal?.map(item => { - // eslint-disable-next-line no-unused-vars - let { cpuInfo, memInfo, osInfo, driveInfo, ipInfo, netstatInfo, ...rest } = item - return rest +const updateTableData = () => { + console.log('refresh server table data') + tableData.value = props.hosts?.map(item => { + let { monitorData, ...rest } = item + return { ...rest, connect: monitorData?.connect || false } }) || [] -}, { immediate: true, deep: false }) +} + +const connectValues = computed(() => { + return props.hosts.map(item => item.monitorData?.connect).join(',') +}) +// 监听到连接状态变化,刷新列表 +watch(connectValues, updateTableData) const hostInfo = computed(() => props.hostInfo || {}) // const host = computed(() => hostInfo.value?.host) diff --git a/web/src/views/terminal/components/info-side.vue b/web/src/views/terminal/components/info-side.vue index 0e415e4..6681d30 100644 --- a/web/src/views/terminal/components/info-side.vue +++ b/web/src/views/terminal/components/info-side.vue @@ -226,7 +226,7 @@ const pingTimer = ref(null) const sftpStatus = ref(false) const token = computed(() => $store.token) -const hostData = computed(() => props.hostInfo) +const hostData = computed(() => props.hostInfo.monitorData || {}) const host = computed(() => hostData.value.host) const ipInfo = computed(() => hostData.value?.ipInfo || {}) // const isError = computed(() => !Boolean(hostData.value?.osInfo)) @@ -269,44 +269,6 @@ const clickInputCommand = () => { emit('click-input-command') } -const connectIO = () => { - socket.value = socketIo($serviceURI, { - path: '/host-status', - forceNew: true, - timeout: 5000, - reconnectionDelay: 3000, - reconnectionAttempts: 3 - }) - - socket.value.on('connect', () => { - console.log('/host-status socket已连接:', socket.value.id) - socket.value.emit('init_host_data', { token: token.value, host: props.host }) - // getHostPing() - socket.value.on('host_data', (data) => { - if (!data) return hostData.value = null - hostData.value = data - }) - }) - - socket.value.on('connect_error', (err) => { - console.error('host status websocket 连接错误:', err) - $notification({ - title: '连接客户端失败(重连中...)', - message: '请检查客户端服务是否正常', - type: 'error' - }) - }) - - socket.value.on('disconnect', () => { - hostData.value = null - $notification({ - title: '客户端连接主动断开(重连中...)', - message: '请检查客户端服务是否正常', - type: 'error' - }) - }) -} - const handleCopy = async () => { await navigator.clipboard.writeText(host.value) $message.success({ message: 'success', center: true })