🆕 重构监控数据结构

This commit is contained in:
chaoszhu 2024-07-21 23:58:25 +08:00
parent 5b2b776155
commit 655e9bc8af
11 changed files with 132 additions and 169 deletions

View File

@ -1,7 +1,10 @@
const { readGroupList, writeGroupList, readHostList, writeHostList, randomStr } = require('../utils')
async function getGroupList({ res }) {
const data = await readGroupList()
let data = await readGroupList()
data = data.map(item => {
return { ...item, id: item._id }
})
data?.sort((a, b) => Number(b.index || 0) - Number(a.index || 0))
res.success({ data })
}

View File

@ -5,9 +5,11 @@ async function getHostList({ res }) {
let data = await readHostList()
data?.sort((a, b) => Number(b.index || 0) - Number(a.index || 0))
data = data.map((item) => {
const isConfig = Boolean(item.username && item.port && (item[item.authType]))
const { username, port, authType, _id: id } = item
const isConfig = Boolean(username && port && (item[authType]))
return {
...item,
id,
isConfig,
password: '',
privateKey: ''

View File

@ -25,6 +25,7 @@ app.config.globalProperties.$store = useStore()
const serviceURI = import.meta.env.DEV ? process.env.serviceURI : location.origin
app.config.globalProperties.$serviceURI = serviceURI
app.config.globalProperties.$clientPort = process.env.clientPort || 22022
app.config.globalProperties.$store.$patch({ serviceURI })
console.warn('ISDEV: ', import.meta.env.DEV)
console.warn('serviceURI: ', serviceURI)

View File

@ -5,8 +5,6 @@ import { createRouter, createWebHistory } from 'vue-router'
// import terminal from '@views/terminal/index.vue'
// import test from '@views/test/index.vue'
const HostList = () => import('@views/list/index.vue')
const Login = () => import('@views/login/index.vue')
const Container = () => import('@views/index.vue')
const Server = () => import('@views/server/index.vue')

View File

@ -1,3 +1,4 @@
import { io } from 'socket.io-client'
import { defineStore, acceptHMRUpdate } from 'pinia'
import $api from '@/api'
import ping from '@/utils/ping'
@ -5,9 +6,11 @@ import ping from '@/utils/ping'
const useStore = defineStore({
id: 'global',
state: () => ({
serviceURI: null,
hostList: [],
groupList: [],
sshList: [],
HostStatusSocket: null,
user: localStorage.getItem('user') || null,
token: sessionStorage.getItem('token') || localStorage.getItem('token') || null,
title: ''
@ -31,18 +34,15 @@ const useStore = defineStore({
this.$patch({ token: null })
},
async getMainData() {
const { data: groupList } = await $api.getGroupList()
const { data: hostList } = await $api.getHostList()
const { data: sshList } = await $api.getSSHList()
// console.log('hostList:', hostList)
// console.log('groupList:', groupList)
// console.log('sshList:', sshList)
this.$patch({ groupList, hostList, sshList })
await this.getGroupList()
await this.getHostList()
await this.getSSHList()
},
async getHostList() {
const { data: hostList } = await $api.getHostList()
// console.log('hostList:', hostList)
this.$patch({ hostList })
this.wsHostStatus()
},
async getGroupList() {
const { data: groupList } = await $api.getGroupList()
@ -67,11 +67,39 @@ const useStore = defineStore({
// console.warn('Please tick \'Preserve Log\'')
}, 1500)
},
async sortHostList(list) {
let hostList = list.map(({ host }) => {
return this.hostList.find(item => item.host === host)
async wsHostStatus() {
if (this.HostStatusSocket) this.HostStatusSocket.close()
let socketInstance = io(this.serviceURI, {
path: '/clients',
forceNew: true,
reconnectionDelay: 5000,
reconnectionAttempts: 2
})
this.HostStatusSocket = socketInstance
socketInstance.on('connect', () => {
let flag = 5
console.log('clients websocket 已连接: ', socketInstance.id)
let token = this.token
socketInstance.emit('init_clients_data', { token })
socketInstance.on('clients_data', (data) => {
if ((flag++ % 5) === 0) this.getHostPing()
this.hostList.forEach(item => {
const { host } = item
if (data[host] === null) return { ...item }
return Object.assign(item, data[host])
})
})
socketInstance.on('token_verify_fail', (message) => {
console.log('token 验证失败:', message)
// $router.push('/login')
})
})
socketInstance.on('disconnect', () => {
console.error('clients websocket 连接断开')
})
socketInstance.on('connect_error', (message) => {
console.error('clients websocket 连接出错: ', message)
})
this.$patch({ hostList })
}
}
})

View File

@ -37,14 +37,12 @@
</template>
<script setup>
import { ref, onBeforeUnmount, getCurrentInstance, computed, watch, onMounted } from 'vue'
import { io } from 'socket.io-client'
import { ref, onBeforeUnmount, getCurrentInstance, computed, watch } from 'vue'
import HostCard from './components/host-card.vue'
import HostForm from './components/host-form.vue'
const { proxy: { $store, $notification, $router, $serviceURI, $message } } = getCurrentInstance()
const { proxy: { $store, $message } } = getCurrentInstance()
const socket = ref(null)
const updateHostData = ref(null)
const hostFormVisible = ref(false)
const hiddenIp = ref(Number(localStorage.getItem('hiddenIp') || 0))
@ -53,7 +51,6 @@ const activeGroup = ref([])
const handleUpdateList = async () => {
try {
await $store.getHostList()
connectIo()
} catch (err) {
$message.error('获取实例列表失败')
console.error('获取实例列表失败: ', err)
@ -98,60 +95,6 @@ watch(groupHostList, () => {
deep: false
})
let hostList = computed(() => $store.hostList)
const unwatchHost = watch(hostList, () => {
connectIo()
})
const connectIo = () => {
if (socket.value) socket.value.close()
if (typeof(unwatchHost) === 'function') unwatchHost()
let socketInstance = io($serviceURI, {
path: '/clients',
forceNew: true,
reconnectionDelay: 5000,
reconnectionAttempts: 2
})
socket.value = socketInstance
socketInstance.on('connect', () => {
let flag = 5
console.log('clients websocket 已连接: ', socketInstance.id)
let token = $store.token
socketInstance.emit('init_clients_data', { token })
socketInstance.on('clients_data', (data) => {
if ((flag++ % 5) === 0) $store.getHostPing()
$store.hostList.forEach(item => {
const { host } = item
if (data[host] === null) return { ...item }
return Object.assign(item, data[host])
})
})
socketInstance.on('token_verify_fail', (message) => {
$notification({
title: '鉴权失败',
message,
type: 'error'
})
$router.push('/login')
})
})
socketInstance.on('disconnect', () => {
console.error('clients websocket 连接断开')
})
socketInstance.on('connect_error', (message) => {
console.error('clients websocket 连接出错: ', message)
})
}
// onMounted(() => {
// connectIo()
// })
onBeforeUnmount(() => {
if (socket.value) socket.value.close()
})
</script>
<style lang="scss" scoped>

View File

@ -11,7 +11,7 @@
/>
</div> -->
</header>
<el-divider class="first-divider" content-position="center">POSITION</el-divider>
<el-divider class="first-divider" content-position="center">地理位置</el-divider>
<el-descriptions
class="margin-top"
:column="1"
@ -47,7 +47,7 @@
</el-descriptions-item>
</el-descriptions>
<el-divider content-position="center">INDICATOR</el-divider>
<el-divider content-position="center">实时监控</el-divider>
<el-descriptions
class="margin-top"
:column="1"
@ -118,7 +118,7 @@
</el-descriptions-item>
</el-descriptions>
<el-divider content-position="center">INFORMATION</el-divider>
<el-divider content-position="center">系统信息</el-divider>
<el-descriptions
class="margin-top"
:column="1"
@ -208,16 +208,12 @@
import { ref, onMounted, onBeforeUnmount, computed, getCurrentInstance } from 'vue'
import socketIo from 'socket.io-client'
const { proxy: { $router, $serviceURI, $message, $notification, $tools } } = getCurrentInstance()
const { proxy: { $router, $store, $serviceURI, $message, $notification, $tools } } = getCurrentInstance()
const props = defineProps({
token: {
hostInfo: {
required: true,
type: String
},
host: {
required: true,
type: String
type: Object
},
visible: {
required: true,
@ -233,11 +229,13 @@ const emit = defineEmits(['update:inputCommandStyle', 'connect-sftp', 'click-inp
const socket = ref(null)
const name = ref('')
const hostData = ref(null)
const ping = ref(0)
const pingTimer = ref(null)
const sftpStatus = ref(false)
const token = computed(() => $store.token)
const hostData = computed(() => props.hostInfo)
const host = computed(() => hostData.value.host)
const ipInfo = computed(() => hostData.value?.ipInfo || {})
// const isError = computed(() => !Boolean(hostData.value?.osInfo))
const cpuInfo = computed(() => hostData.value?.cpuInfo || {})
@ -280,7 +278,6 @@ const clickInputCommand = () => {
}
const connectIO = () => {
const { host, token } = props
socket.value = socketIo($serviceURI, {
path: '/host-status',
forceNew: true,
@ -291,8 +288,8 @@ const connectIO = () => {
socket.value.on('connect', () => {
console.log('/host-status socket已连接', socket.value.id)
socket.value.emit('init_host_data', { token, host })
getHostPing()
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
@ -343,9 +340,9 @@ const getHostPing = () => {
}
onMounted(() => {
name.value = $router.currentRoute.value.query.name || ''
if (!props.host || !name.value) return $message.error('参数错误')
connectIO()
// name.value = $router.currentRoute.value.query.name || ''
// if (!props.host || !name.value) return $message.error('')
// connectIO()
})
onBeforeUnmount(() => {

View File

@ -12,7 +12,7 @@
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount, getCurrentInstance } from 'vue'
import { ref, onMounted, computed, onBeforeUnmount, getCurrentInstance } from 'vue'
import { Terminal } from 'xterm'
import 'xterm/css/xterm.css'
import { FitAddon } from 'xterm-addon-fit'
@ -22,20 +22,12 @@ import { WebLinksAddon } from 'xterm-addon-web-links'
import socketIo from 'socket.io-client'
const { io } = socketIo
const { proxy: { $api, $serviceURI, $notification, $router, $messageBox } } = getCurrentInstance()
const { proxy: { $api, $store, $serviceURI, $notification, $router, $messageBox } } = getCurrentInstance()
const props = defineProps({
token: {
required: true,
type: String
},
host: {
required: true,
type: String
},
tabKey: {
required: true,
type: String
}
})
@ -48,7 +40,7 @@ const searchBar = ref(null)
const isManual = ref(false)
const terminalRefs = ref(null)
const tabKey = ref(props.tabKey)
const token = computed(() => $store.token)
const getCommand = async () => {
let { data } = await $api.getCommand(props.host)
@ -56,7 +48,7 @@ const getCommand = async () => {
}
const connectIO = () => {
const { host, token } = props
const { host } = props
socket.value = io($serviceURI, {
path: '/terminal',
forceNew: false,
@ -65,7 +57,7 @@ const connectIO = () => {
socket.value.on('connect', () => {
console.log('/terminal socket已连接', socket.value.id)
socket.value.emit('create', { host, token })
socket.value.emit('create', { host, token: token.value })
socket.value.on('connect_success', () => {
onData()
socket.value.on('connect_terminal', () => {
@ -241,7 +233,7 @@ const handleInputCommand = (command) => {
onMounted(async () => {
createLocalTerminal()
await getCommand()
// await getCommand()
connectIO()
})
@ -255,8 +247,7 @@ defineExpose({
focusTab,
handleResize,
handleInputCommand,
handleClear,
tabKey
handleClear
})
</script>

View File

@ -3,8 +3,7 @@
<InfoSide
ref="infoSideRef"
v-model:show-input-command="showInputCommand"
:token="token"
:host="host"
:host-info="curHost"
:visible="visible"
@connect-sftp="connectSftp"
@click-input-command="clickInputCommand"
@ -18,26 +17,23 @@
<svg-icon name="icon-jiantou_zuoyouqiehuan" class="svg-icon" />
</div>
<el-tabs
v-model="activeTab"
v-model="activeTabIndex"
type="border-card"
addable
tab-position="top"
@tab-remove="removeTab"
@tab-change="tabChange"
@tab-add="tabAdd"
>
<el-tab-pane
v-for="item in terminalTabs"
:key="item.key"
:label="item.title"
:name="item.key"
:closable="closable"
v-for="(item, index) in terminalTabs"
:key="index"
:label="item.name"
:name="index"
:closable="true"
>
<TerminalTab
ref="terminalTabRefs"
:token="token"
:host="host"
:tab-key="item.key"
:host="item.host"
/>
</el-tab-pane>
</el-tabs>
@ -51,7 +47,7 @@
</template>
<script setup>
import { ref, reactive, computed, onBeforeMount,defineProps, getCurrentInstance } from 'vue'
import { ref, reactive, defineEmits, computed, onBeforeMount,defineProps, getCurrentInstance, watch } from 'vue'
import TerminalTab from './terminal-tab.vue'
import InfoSide from './info-side.vue'
import SftpFooter from './sftp-footer.vue'
@ -60,16 +56,16 @@ import InputCommand from '@/components/input-command/index.vue'
const { proxy: { $store, $router, $route, $nextTick } } = getCurrentInstance()
const props = defineProps({
ternimalTabs: {
terminalTabs: {
type: Array,
required: true
}
})
const name = ref('')
const host = ref('')
const activeTab = ref('')
const terminalTabs = reactive([])
const emit = defineEmits(['closed', 'removeTab',])
const activeTabIndex = ref(0)
// const terminalTabs = reactive([])
const isFullScreen = ref(false)
const timer = ref(null)
const showSftp = ref(false)
@ -77,21 +73,24 @@ const showInputCommand = ref(false)
const visible = ref(true)
const infoSideRef = ref(null)
const terminalTabRefs = ref([])
const token = computed(() => $store.token)
const ternimalTabs = computed(() => props.ternimalTabs)
const terminalTabs = computed(() => props.terminalTabs)
const curHost = computed(() => terminalTabs.value[activeTabIndex.value])
const closable = computed(() => terminalTabs.length > 1)
// const closable = computed(() => terminalTabs.length > 1)
onBeforeMount(() => {
if (!token.value) return $router.push('login')
let { host: routeHost, name: routeName } = $route.query
name.value = routeName
host.value = routeHost
document.title = `${ document.title }-${ routeName }`
let key = Date.now().toString()
terminalTabs.push({ title: routeName, key })
activeTab.value = key
registryDbClick()
watch(terminalTabs, () => {
console.log('add tab:', terminalTabs.value)
let len = terminalTabs.value.length
if (len > 0) {
activeTabIndex.value = len - 1
// registryDbClick()
// tabChange(terminalTabs.value[0].key)
}
}, {
immediate: true,
deep: false
})
// const windowBeforeUnload = () => {
@ -114,23 +113,23 @@ const tabAdd = () => {
timer.value = setTimeout(() => {
let title = name.value
let key = Date.now().toString()
terminalTabs.push({ title, key })
activeTab.value = key
terminalTabs.value.push({ title, key })
activeTabIndex.value = key
tabChange(key)
registryDbClick()
// registryDbClick()
}, 200)
}
const removeTab = (removeKey) => {
let idx = terminalTabs.findIndex(({ key }) => removeKey === key)
terminalTabs.splice(idx, 1)
if (removeKey !== activeTab.value) return
activeTab.value = terminalTabs[0].key
const removeTab = (index) => {
// terminalTabs.value.splice(index, 1)
emit('removeTab', index)
if (index !== activeTabIndex.value) return
activeTabIndex.value = 0
}
const tabChange = async (key) => {
const tabChange = async (index) => {
await $nextTick()
const curTabTerminal = terminalTabRefs.value.find(({ tabKey }) => key === tabKey)
const curTabTerminal = terminalTabRefs.value[index]
curTabTerminal?.focusTab()
}
@ -140,23 +139,20 @@ const handleFullScreen = () => {
isFullScreen.value = !isFullScreen.value
}
const registryDbClick = () => {
$nextTick(() => {
let tabItems = Array.from(document.getElementsByClassName('el-tabs__item'))
tabItems.forEach(item => {
item.removeEventListener('dblclick', handleDblclick)
item.addEventListener('dblclick', handleDblclick)
})
})
}
// const registryDbClick = () => {
// $nextTick(() => {
// let tabItems = Array.from(document.getElementsByClassName('el-tabs__item'))
// tabItems.forEach(item => {
// item.removeEventListener('dblclick', handleDblclick)
// item.addEventListener('dblclick', handleDblclick)
// })
// })
// }
const handleDblclick = (e) => {
if (terminalTabs.length > 1) {
let key = e.target.id.substring(4)
// console.log('dblclick', key)
removeTab(key)
}
}
// const handleDblclick = (e) => {
// let key = e.target.id.substring(4)
// removeTab(key)
// }
const handleVisibleSidebar = () => {
visible.value = !visible.value
@ -171,7 +167,7 @@ const resizeTerminal = () => {
}
const handleInputCommand = async (command) => {
const curTabTerminal = terminalTabRefs.value.find(({ tabKey }) => activeTab.value === tabKey)
const curTabTerminal = terminalTabRefs.value.find(({ tabKey }) => activeTabIndex.value === tabKey)
await $nextTick()
curTabTerminal?.focusTab()
curTabTerminal.handleInputCommand(`${ command }\n`)

View File

@ -37,7 +37,7 @@
</el-table>
</div>
<div v-else>
<Terminal :ternimal-tabs="ternimalTabs" />
<Terminal :terminal-tabs="terminalTabs" @remove-tab="handleRemoveTab" />
</div>
<HostForm
v-model:show="hostFormVisible"
@ -49,17 +49,17 @@
</template>
<script setup>
import { ref, computed, onActivated, getCurrentInstance } from 'vue'
import { ref, computed, onActivated, getCurrentInstance, reactive } from 'vue'
import Terminal from './components/terminal.vue'
import HostForm from '../server/components/host-form.vue'
const { proxy: { $store, $message } } = getCurrentInstance()
let ternimalTabs = ref([])
let terminalTabs = reactive([])
const hostFormVisible = ref(false)
const updateHostData = ref(null)
let showLinkTips = computed(() => !Boolean(ternimalTabs.value.length))
let showLinkTips = computed(() => !Boolean(terminalTabs.length))
let hostList = computed(() => $store.hostList)
@ -69,7 +69,7 @@ let isAllConfssh = computed(() => {
function linkTerminal(row) {
// console.log(row)
ternimalTabs.value.push(row)
terminalTabs.push(row)
}
function handleUpdateHost(row) {
@ -77,6 +77,10 @@ function handleUpdateHost(row) {
updateHostData.value = { ...row }
}
function handleRemoveTab(index) {
terminalTabs.splice(index, 1)
}
const handleUpdateList = async () => {
try {
await $store.getHostList()