Compare commits
No commits in common. "5a86bab647e7ba1a9d56faf553cb35f1d798667d" and "edaef2a78cba77307a132ae7ed98ec836c15596c" have entirely different histories.
5a86bab647
...
edaef2a78c
|
@ -2513,52 +2513,3 @@ func (b *browserService) GetSlowLogs(server string, num int64) (resp types.JSRes
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetClientList get all connected client info
|
|
||||||
func (b *browserService) GetClientList(server string) (resp types.JSResp) {
|
|
||||||
item, err := b.getRedisClient(server, -1)
|
|
||||||
if err != nil {
|
|
||||||
resp.Msg = err.Error()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
parseContent := func(content string) []map[string]string {
|
|
||||||
lines := strings.Split(content, "\n")
|
|
||||||
list := make([]map[string]string, 0, len(lines))
|
|
||||||
for _, line := range lines {
|
|
||||||
line = strings.TrimSpace(line)
|
|
||||||
if len(line) > 0 {
|
|
||||||
items := strings.Split(line, " ")
|
|
||||||
itemKV := map[string]string{}
|
|
||||||
for _, it := range items {
|
|
||||||
kv := strings.SplitN(it, "=", 2)
|
|
||||||
if len(kv) > 1 {
|
|
||||||
itemKV[kv[0]] = kv[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
list = append(list, itemKV)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return list
|
|
||||||
}
|
|
||||||
|
|
||||||
client, ctx := item.client, item.ctx
|
|
||||||
var fullList []map[string]string
|
|
||||||
var mutex sync.Mutex
|
|
||||||
if cluster, ok := client.(*redis.ClusterClient); ok {
|
|
||||||
cluster.ForEachMaster(ctx, func(ctx context.Context, cli *redis.Client) error {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
fullList = append(fullList, parseContent(cli.ClientList(ctx).Val())...)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
fullList = append(fullList, parseContent(client.ClientList(ctx).Val())...)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp.Success = true
|
|
||||||
resp.Data = map[string]any{
|
|
||||||
"list": fullList,
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
|
@ -14,21 +14,20 @@ import {
|
||||||
toArray,
|
toArray,
|
||||||
toNumber,
|
toNumber,
|
||||||
} from 'lodash'
|
} from 'lodash'
|
||||||
import { computed, h, onMounted, onUnmounted, reactive, ref, shallowRef, toRaw, watch } from 'vue'
|
import { computed, onMounted, onUnmounted, reactive, ref, shallowRef, toRaw, watch } from 'vue'
|
||||||
import IconButton from '@/components/common/IconButton.vue'
|
import IconButton from '@/components/common/IconButton.vue'
|
||||||
import Filter from '@/components/icons/Filter.vue'
|
import Filter from '@/components/icons/Filter.vue'
|
||||||
import Refresh from '@/components/icons/Refresh.vue'
|
import Refresh from '@/components/icons/Refresh.vue'
|
||||||
import useBrowserStore from 'stores/browser.js'
|
import useBrowserStore from 'stores/browser.js'
|
||||||
import { timeout } from '@/utils/promise.js'
|
import { timeout } from '@/utils/promise.js'
|
||||||
import AutoRefreshForm from '@/components/common/AutoRefreshForm.vue'
|
import AutoRefreshForm from '@/components/common/AutoRefreshForm.vue'
|
||||||
import { NButton, NIcon, NSpace, useThemeVars } from 'naive-ui'
|
import { NIcon, useThemeVars } from 'naive-ui'
|
||||||
import { Line } from 'vue-chartjs'
|
import { Line } from 'vue-chartjs'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { convertBytes, formatBytes } from '@/utils/byte_convert.js'
|
import { convertBytes, formatBytes } from '@/utils/byte_convert.js'
|
||||||
import usePreferencesStore from 'stores/preferences.js'
|
import usePreferencesStore from 'stores/preferences.js'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import useConnectionStore from 'stores/connections.js'
|
import useConnectionStore from 'stores/connections.js'
|
||||||
import { toHumanReadable } from '@/utils/date.js'
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
server: String,
|
server: String,
|
||||||
|
@ -502,87 +501,6 @@ const byteChartOption = computed(() => {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const clientInfo = reactive({
|
|
||||||
loading: false,
|
|
||||||
content: [],
|
|
||||||
})
|
|
||||||
const onShowClients = async (show) => {
|
|
||||||
if (show) {
|
|
||||||
try {
|
|
||||||
clientInfo.loading = true
|
|
||||||
clientInfo.content = await browserStore.getClientList(props.server)
|
|
||||||
} finally {
|
|
||||||
clientInfo.loading = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const clientTableColumns = computed(() => {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
key: 'title',
|
|
||||||
title: () => {
|
|
||||||
return h(NSpace, { wrap: false, wrapItem: false, justify: 'center' }, () => [
|
|
||||||
h('span', { style: { fontWeight: '550', fontSize: '15px' } }, i18n.t('status.client.title')),
|
|
||||||
h(IconButton, {
|
|
||||||
icon: Refresh,
|
|
||||||
size: 16,
|
|
||||||
onClick: () => onShowClients(true),
|
|
||||||
}),
|
|
||||||
])
|
|
||||||
},
|
|
||||||
align: 'center',
|
|
||||||
titleAlign: 'center',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
key: 'no',
|
|
||||||
title: '#',
|
|
||||||
width: 60,
|
|
||||||
align: 'center',
|
|
||||||
titleAlign: 'center',
|
|
||||||
render: (row, index) => {
|
|
||||||
return index + 1
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'addr',
|
|
||||||
title: () => i18n.t('status.client.addr'),
|
|
||||||
sorter: 'default',
|
|
||||||
align: 'center',
|
|
||||||
titleAlign: 'center',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'db',
|
|
||||||
title: () => i18n.t('status.client.db'),
|
|
||||||
align: 'center',
|
|
||||||
titleAlign: 'center',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'age',
|
|
||||||
title: () => i18n.t('status.client.age'),
|
|
||||||
sorter: (row1, row2) => row1.age - row2.age,
|
|
||||||
defaultSortOrder: 'descend',
|
|
||||||
align: 'center',
|
|
||||||
titleAlign: 'center',
|
|
||||||
render: ({ age }, index) => {
|
|
||||||
return toHumanReadable(age)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'idle',
|
|
||||||
title: () => i18n.t('status.client.idle'),
|
|
||||||
sorter: (row1, row2) => row1.age - row2.age,
|
|
||||||
align: 'center',
|
|
||||||
titleAlign: 'center',
|
|
||||||
render: ({ idle }, index) => {
|
|
||||||
return toHumanReadable(idle)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -650,24 +568,7 @@ const clientTableColumns = computed(() => {
|
||||||
<n-gi :span="6">
|
<n-gi :span="6">
|
||||||
<n-statistic
|
<n-statistic
|
||||||
:label="$t('status.connected_clients')"
|
:label="$t('status.connected_clients')"
|
||||||
:value="get(serverInfo, 'Clients.connected_clients', '0')">
|
:value="get(serverInfo, 'Clients.connected_clients', '0')" />
|
||||||
<template #suffix>
|
|
||||||
<n-tooltip trigger="click" width="70vw" @update-show="onShowClients">
|
|
||||||
<template #trigger>
|
|
||||||
<n-button :bordered="false" size="small">↘</n-button>
|
|
||||||
</template>
|
|
||||||
<n-data-table
|
|
||||||
:columns="clientTableColumns"
|
|
||||||
:data="clientInfo.content"
|
|
||||||
:loading="clientInfo.loading"
|
|
||||||
:single-column="false"
|
|
||||||
:single-line="false"
|
|
||||||
max-height="50vh"
|
|
||||||
size="small"
|
|
||||||
striped />
|
|
||||||
</n-tooltip>
|
|
||||||
</template>
|
|
||||||
</n-statistic>
|
|
||||||
</n-gi>
|
</n-gi>
|
||||||
<n-gi :span="6">
|
<n-gi :span="6">
|
||||||
<n-statistic :value="totalKeys">
|
<n-statistic :value="totalKeys">
|
||||||
|
|
|
@ -10,11 +10,11 @@ import { useI18n } from 'vue-i18n'
|
||||||
import IconButton from '@/components/common/IconButton.vue'
|
import IconButton from '@/components/common/IconButton.vue'
|
||||||
import Copy from '@/components/icons/Copy.vue'
|
import Copy from '@/components/icons/Copy.vue'
|
||||||
import { ClipboardSetText } from 'wailsjs/runtime/runtime.js'
|
import { ClipboardSetText } from 'wailsjs/runtime/runtime.js'
|
||||||
import { computed, onMounted, onUnmounted, reactive, watch } from 'vue'
|
import { computed, onUnmounted, reactive, watch } from 'vue'
|
||||||
import { NIcon, useThemeVars } from 'naive-ui'
|
import { NIcon, useThemeVars } from 'naive-ui'
|
||||||
import { timeout } from '@/utils/promise.js'
|
import { timeout } from '@/utils/promise.js'
|
||||||
import AutoRefreshForm from '@/components/common/AutoRefreshForm.vue'
|
import AutoRefreshForm from '@/components/common/AutoRefreshForm.vue'
|
||||||
import { toHumanReadable } from '@/utils/date.js'
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
server: String,
|
server: String,
|
||||||
|
@ -45,12 +45,6 @@ const autoRefresh = reactive({
|
||||||
interval: 2,
|
interval: 2,
|
||||||
})
|
})
|
||||||
|
|
||||||
const ttl = reactive({
|
|
||||||
value: 0,
|
|
||||||
expire: 0,
|
|
||||||
intervalID: 0,
|
|
||||||
})
|
|
||||||
|
|
||||||
const themeVars = useThemeVars()
|
const themeVars = useThemeVars()
|
||||||
const dialogStore = useDialog()
|
const dialogStore = useDialog()
|
||||||
const i18n = useI18n()
|
const i18n = useI18n()
|
||||||
|
@ -60,9 +54,15 @@ const binaryKey = computed(() => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const ttlString = computed(() => {
|
const ttlString = computed(() => {
|
||||||
if (ttl.value > 0) {
|
if (props.ttl > 0) {
|
||||||
return toHumanReadable(ttl.value)
|
const dur = dayjs.duration(props.ttl, 'seconds')
|
||||||
} else if (ttl.value < 0) {
|
const days = dur.days()
|
||||||
|
if (days > 0) {
|
||||||
|
return days + i18n.t('common.unit_day') + ' ' + dur.format('HH:mm:ss')
|
||||||
|
} else {
|
||||||
|
return dur.format('HH:mm:ss')
|
||||||
|
}
|
||||||
|
} else if (props.ttl < 0) {
|
||||||
return i18n.t('interface.forever')
|
return i18n.t('interface.forever')
|
||||||
} else {
|
} else {
|
||||||
return '00:00:00'
|
return '00:00:00'
|
||||||
|
@ -89,46 +89,15 @@ const stopAutoRefresh = () => {
|
||||||
autoRefresh.on = false
|
autoRefresh.on = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const syncTTL = (seconds) => {
|
|
||||||
ttl.value = seconds
|
|
||||||
if (seconds >= 0) {
|
|
||||||
ttl.expire = Math.floor(Date.now() / 1000 + seconds)
|
|
||||||
} else {
|
|
||||||
ttl.expire = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.keyPath,
|
() => props.keyPath,
|
||||||
() => {
|
() => {
|
||||||
stopAutoRefresh()
|
stopAutoRefresh()
|
||||||
|
autoRefresh.interval = props.interval
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
watch(
|
onUnmounted(() => stopAutoRefresh())
|
||||||
() => props.ttl,
|
|
||||||
(seconds) => syncTTL(seconds),
|
|
||||||
)
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
syncTTL(props.ttl)
|
|
||||||
ttl.intervalID = setInterval(() => {
|
|
||||||
if (ttl.expire > 0) {
|
|
||||||
const nowSeconds = Math.floor(Date.now() / 1000)
|
|
||||||
ttl.value = Math.max(0, ttl.expire - nowSeconds)
|
|
||||||
} else {
|
|
||||||
ttl.value = -1
|
|
||||||
}
|
|
||||||
}, 1000)
|
|
||||||
})
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
stopAutoRefresh()
|
|
||||||
if (ttl.intervalID > 0) {
|
|
||||||
clearInterval(ttl.intervalID)
|
|
||||||
ttl.intervalID = 0
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const onToggleRefresh = (on) => {
|
const onToggleRefresh = (on) => {
|
||||||
if (on) {
|
if (on) {
|
||||||
|
@ -199,7 +168,7 @@ const onTTL = () => {
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<n-icon :component="Timer" size="18" />
|
<n-icon :component="Timer" size="18" />
|
||||||
</template>
|
</template>
|
||||||
<span style="font-variant-numeric: tabular-nums">{{ ttlString }}</span>
|
{{ ttlString }}
|
||||||
</n-button>
|
</n-button>
|
||||||
</template>
|
</template>
|
||||||
TTL{{ `${ttl > 0 ? ': ' + ttl + $t('common.second') : ''}` }}
|
TTL{{ `${ttl > 0 ? ': ' + ttl + $t('common.second') : ''}` }}
|
||||||
|
|
|
@ -567,11 +567,7 @@ defineExpose({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div :style="{ backgroundColor }" class="flex-box-v browser-tree-wrapper" @contextmenu="(e) => e.preventDefault()">
|
||||||
:style="{ backgroundColor }"
|
|
||||||
class="flex-box-v browser-tree-wrapper"
|
|
||||||
@contextmenu="(e) => e.preventDefault()"
|
|
||||||
@keydown.esc="contextMenuParam.show = false">
|
|
||||||
<n-spin v-if="props.loading" class="fill-height" />
|
<n-spin v-if="props.loading" class="fill-height" />
|
||||||
<n-empty v-else-if="!props.loading && isEmpty(data)" class="empty-content" />
|
<n-empty v-else-if="!props.loading && isEmpty(data)" class="empty-content" />
|
||||||
<n-tree
|
<n-tree
|
||||||
|
|
|
@ -479,74 +479,67 @@ const onCancelOpen = () => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="connection-tree-wrapper" @keydown.esc="contextMenuParam.show = false">
|
<n-empty
|
||||||
<n-empty
|
v-if="isEmpty(connectionStore.connections)"
|
||||||
v-if="isEmpty(connectionStore.connections)"
|
:description="$t('interface.empty_server_list')"
|
||||||
:description="$t('interface.empty_server_list')"
|
class="empty-content" />
|
||||||
class="empty-content" />
|
<n-tree
|
||||||
<n-tree
|
v-else
|
||||||
v-else
|
:animated="false"
|
||||||
:animated="false"
|
:block-line="true"
|
||||||
:block-line="true"
|
:block-node="true"
|
||||||
:block-node="true"
|
:cancelable="false"
|
||||||
:cancelable="false"
|
:data="connectionStore.connections"
|
||||||
:data="connectionStore.connections"
|
:draggable="true"
|
||||||
:draggable="true"
|
:expanded-keys="expandedKeys"
|
||||||
:expanded-keys="expandedKeys"
|
:node-props="nodeProps"
|
||||||
:node-props="nodeProps"
|
:pattern="props.filterPattern"
|
||||||
:pattern="props.filterPattern"
|
:render-label="renderLabel"
|
||||||
:render-label="renderLabel"
|
:render-prefix="renderPrefix"
|
||||||
:render-prefix="renderPrefix"
|
:render-suffix="renderSuffix"
|
||||||
:render-suffix="renderSuffix"
|
:selected-keys="selectedKeys"
|
||||||
:selected-keys="selectedKeys"
|
class="fill-height"
|
||||||
class="fill-height"
|
virtual-scroll
|
||||||
virtual-scroll
|
@drop="handleDrop"
|
||||||
@drop="handleDrop"
|
@update:selected-keys="onUpdateSelectedKeys"
|
||||||
@update:selected-keys="onUpdateSelectedKeys"
|
@update:expanded-keys="onUpdateExpandedKeys" />
|
||||||
@update:expanded-keys="onUpdateExpandedKeys" />
|
|
||||||
|
|
||||||
<!-- status display modal -->
|
<!-- status display modal -->
|
||||||
<n-modal :show="connectingServer !== ''" transform-origin="center">
|
<n-modal :show="connectingServer !== ''" transform-origin="center">
|
||||||
<n-card
|
<n-card
|
||||||
:bordered="false"
|
:bordered="false"
|
||||||
:content-style="{ textAlign: 'center' }"
|
:content-style="{ textAlign: 'center' }"
|
||||||
aria-model="true"
|
aria-model="true"
|
||||||
role="dialog"
|
role="dialog"
|
||||||
style="width: 400px">
|
style="width: 400px">
|
||||||
<n-spin>
|
<n-spin>
|
||||||
<template #description>
|
<template #description>
|
||||||
<n-space vertical>
|
<n-space vertical>
|
||||||
<n-text strong>{{ $t('dialogue.opening_connection') }}</n-text>
|
<n-text strong>{{ $t('dialogue.opening_connection') }}</n-text>
|
||||||
<n-button :focusable="false" secondary size="small" @click="onCancelOpen">
|
<n-button :focusable="false" secondary size="small" @click="onCancelOpen">
|
||||||
{{ $t('dialogue.interrupt_connection') }}
|
{{ $t('dialogue.interrupt_connection') }}
|
||||||
</n-button>
|
</n-button>
|
||||||
</n-space>
|
</n-space>
|
||||||
</template>
|
</template>
|
||||||
</n-spin>
|
</n-spin>
|
||||||
</n-card>
|
</n-card>
|
||||||
</n-modal>
|
</n-modal>
|
||||||
|
|
||||||
<!-- context menu -->
|
<!-- context menu -->
|
||||||
<n-dropdown
|
<n-dropdown
|
||||||
:keyboard="true"
|
:keyboard="true"
|
||||||
:options="contextMenuParam.options"
|
:options="contextMenuParam.options"
|
||||||
:render-icon="({ icon }) => render.renderIcon(icon)"
|
:render-icon="({ icon }) => render.renderIcon(icon)"
|
||||||
:render-label="({ label }) => render.renderLabel($t(label), { class: 'context-menu-item' })"
|
:render-label="({ label }) => render.renderLabel($t(label), { class: 'context-menu-item' })"
|
||||||
:show="contextMenuParam.show"
|
:show="contextMenuParam.show"
|
||||||
:x="contextMenuParam.x"
|
:x="contextMenuParam.x"
|
||||||
:y="contextMenuParam.y"
|
:y="contextMenuParam.y"
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
trigger="manual"
|
trigger="manual"
|
||||||
@clickoutside="contextMenuParam.show = false"
|
@clickoutside="contextMenuParam.show = false"
|
||||||
@select="handleSelectContextMenu" />
|
@select="handleSelectContextMenu" />
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '@/styles/content';
|
@import '@/styles/content';
|
||||||
|
|
||||||
.connection-tree-wrapper {
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -379,14 +379,7 @@
|
||||||
"activity_status": "Activity",
|
"activity_status": "Activity",
|
||||||
"act_cmd": "Commands Per Second",
|
"act_cmd": "Commands Per Second",
|
||||||
"act_network_input": "Network Input",
|
"act_network_input": "Network Input",
|
||||||
"act_network_output": "Network Output",
|
"act_network_output": "Network Output"
|
||||||
"client": {
|
|
||||||
"title": "Connected Client List",
|
|
||||||
"addr": "Client Address",
|
|
||||||
"age": "Age (sec)",
|
|
||||||
"idle": "Idle (sec)",
|
|
||||||
"db": "Database"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"slog": {
|
"slog": {
|
||||||
"title": "Slow Log",
|
"title": "Slow Log",
|
||||||
|
|
|
@ -379,14 +379,7 @@
|
||||||
"activity_status": "活动状态",
|
"activity_status": "活动状态",
|
||||||
"act_cmd": "命令执行数/秒",
|
"act_cmd": "命令执行数/秒",
|
||||||
"act_network_input": "网络输入",
|
"act_network_input": "网络输入",
|
||||||
"act_network_output": "网络输出",
|
"act_network_output": "网络输出"
|
||||||
"client": {
|
|
||||||
"title": "所有客户端列表",
|
|
||||||
"addr": "客户端地址",
|
|
||||||
"age": "连接时长(秒)",
|
|
||||||
"idle": "空闲时长(秒)",
|
|
||||||
"db": "数据库"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"slog": {
|
"slog": {
|
||||||
"title": "慢日志",
|
"title": "慢日志",
|
||||||
|
|
|
@ -13,7 +13,6 @@ import {
|
||||||
DeleteKeys,
|
DeleteKeys,
|
||||||
ExportKey,
|
ExportKey,
|
||||||
FlushDB,
|
FlushDB,
|
||||||
GetClientList,
|
|
||||||
GetCmdHistory,
|
GetCmdHistory,
|
||||||
GetKeyDetail,
|
GetKeyDetail,
|
||||||
GetKeySummary,
|
GetKeySummary,
|
||||||
|
@ -1870,26 +1869,6 @@ const useBrowserStore = defineStore('browser', {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* get client list info
|
|
||||||
* @param {string} server
|
|
||||||
* @return {Promise<{idle: number, name: string, addr: string, age: number, db: number}[]>}
|
|
||||||
*/
|
|
||||||
async getClientList(server) {
|
|
||||||
const { success, msg, data } = await GetClientList(server)
|
|
||||||
if (success) {
|
|
||||||
const { list = [] } = data
|
|
||||||
return map(list, (item) => ({
|
|
||||||
addr: item['addr'],
|
|
||||||
name: item['name'],
|
|
||||||
age: item['age'] || 0,
|
|
||||||
idle: item['idle'] || 0,
|
|
||||||
db: item['db'] || 0,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
return []
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get slow log list
|
* get slow log list
|
||||||
* @param {string} server
|
* @param {string} server
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
import dayjs from 'dayjs'
|
|
||||||
import { i18nGlobal } from '@/utils/i18n.js'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* convert seconds number to human-readable string
|
|
||||||
* @param {number} duration duration in seconds
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
export const toHumanReadable = (duration) => {
|
|
||||||
const dur = dayjs.duration(duration, 'seconds')
|
|
||||||
const days = dur.days()
|
|
||||||
if (days > 0) {
|
|
||||||
return days + i18nGlobal.t('common.unit_day') + ' ' + dur.format('HH:mm:ss')
|
|
||||||
} else {
|
|
||||||
return dur.format('HH:mm:ss')
|
|
||||||
}
|
|
||||||
}
|
|
2
go.mod
2
go.mod
|
@ -5,7 +5,7 @@ go 1.21
|
||||||
require (
|
require (
|
||||||
github.com/adrg/sysfont v0.1.2
|
github.com/adrg/sysfont v0.1.2
|
||||||
github.com/andybalholm/brotli v1.1.0
|
github.com/andybalholm/brotli v1.1.0
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.5.0
|
||||||
github.com/klauspost/compress v1.17.4
|
github.com/klauspost/compress v1.17.4
|
||||||
github.com/redis/go-redis/v9 v9.4.0
|
github.com/redis/go-redis/v9 v9.4.0
|
||||||
github.com/vrischmann/userdir v0.0.0-20151206171402-20f291cebd68
|
github.com/vrischmann/userdir v0.0.0-20151206171402-20f291cebd68
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -26,8 +26,8 @@ github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||||
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||||
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck=
|
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck=
|
||||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
|
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
|
||||||
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
||||||
|
|
Loading…
Reference in New Issue