feat: add filter database option #18

refactor: tidy struct of translation file
This commit is contained in:
tiny-craft 2023-10-07 02:08:41 +08:00
parent 7937b5b0f8
commit bb1d9f316b
8 changed files with 214 additions and 98 deletions

View File

@ -9,6 +9,7 @@ import (
"golang.org/x/crypto/ssh"
"net"
"os"
"slices"
"strconv"
"strings"
"sync"
@ -261,11 +262,16 @@ func (c *connectionService) OpenConnection(name string) (resp types.JSResp) {
return
}
// get total databases
// get connection config
selConn := c.conns.GetConnection(name)
totaldb := 16
if config, err := rdb.ConfigGet(ctx, "databases").Result(); err == nil {
if total, err := strconv.Atoi(config["databases"]); err == nil {
totaldb = total
if selConn.DBFilterType == "none" {
// get total databases
if config, err := rdb.ConfigGet(ctx, "databases").Result(); err == nil {
if total, err := strconv.Atoi(config["databases"]); err == nil {
totaldb = total
}
}
}
@ -278,21 +284,37 @@ func (c *connectionService) OpenConnection(name string) (resp types.JSResp) {
// Parse all db, response content like below
var dbs []types.ConnectionDB
info := c.parseInfo(res)
for i := 0; i < totaldb; i++ {
dbName := "db" + strconv.Itoa(i)
queryDB := func(idx int) types.ConnectionDB {
dbName := "db" + strconv.Itoa(idx)
dbInfoStr := info["Keyspace"][dbName]
if len(dbInfoStr) > 0 {
dbInfo := c.parseDBItemInfo(dbInfoStr)
dbs = append(dbs, types.ConnectionDB{
return types.ConnectionDB{
Name: dbName,
Keys: dbInfo["keys"],
Expires: dbInfo["expires"],
AvgTTL: dbInfo["avg_ttl"],
})
}
} else {
dbs = append(dbs, types.ConnectionDB{
return types.ConnectionDB{
Name: dbName,
})
}
}
}
switch selConn.DBFilterType {
case "none":
for idx := 0; idx < totaldb; idx++ {
dbs = append(dbs, queryDB(idx))
}
case "show":
for _, idx := range selConn.DBFilterList {
dbs = append(dbs, queryDB(idx))
}
case "hide":
for idx := 0; idx < totaldb; idx++ {
if !slices.Contains(selConn.DBFilterList, idx) {
dbs = append(dbs, queryDB(idx))
}
}
}
@ -350,7 +372,7 @@ func (c *connectionService) getRedisClient(connName string, db int) (*redis.Clie
})
}))
if _, err := rdb.Ping(c.ctx).Result(); err != nil && err != redis.Nil {
if _, err = rdb.Ping(c.ctx).Result(); err != nil && err != redis.Nil {
return nil, nil, errors.New("can not connect to redis server:" + err.Error())
}
var cancelFunc context.CancelFunc

View File

@ -34,6 +34,8 @@ func (c *ConnectionsStorage) defaultConnectionItem() types.ConnectionConfig {
KeySeparator: ":",
ConnTimeout: 60,
ExecTimeout: 60,
DBFilterType: "none",
DBFilterList: []int{},
MarkColor: "",
}
}

View File

@ -13,6 +13,8 @@ type ConnectionConfig struct {
KeySeparator string `json:"keySeparator,omitempty" yaml:"key_separator,omitempty"`
ConnTimeout int `json:"connTimeout,omitempty" yaml:"conn_timeout,omitempty"`
ExecTimeout int `json:"execTimeout,omitempty" yaml:"exec_timeout,omitempty"`
DBFilterType string `json:"dbFilterType" yaml:"db_filter_type,omitempty"`
DBFilterList []int `json:"dbFilterList" yaml:"db_filter_list,omitempty"`
MarkColor string `json:"markColor,omitempty" yaml:"mark_color,omitempty"`
SSH ConnectionSSH `json:"ssh,omitempty" yaml:"ssh,omitempty"`
}

View File

@ -1,5 +1,5 @@
<script setup>
import { every, get, includes, isEmpty, map } from 'lodash'
import { every, get, includes, isEmpty, map, sortBy, toNumber } from 'lodash'
import { computed, nextTick, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { SelectKeyFile, TestConnection } from 'wailsjs/go/services/connectionService.js'
@ -56,12 +56,24 @@ const groupOptions = computed(() => {
return options
})
const dbFilterList = ref([])
const onUpdateDBFilterList = (list) => {
const dbList = []
for (const item of list) {
const idx = toNumber(item)
if (!isNaN(idx)) {
dbList.push(idx)
}
}
generalForm.value.dbFilterList = sortBy(dbList)
}
const sshLoginType = computed(() => {
return get(generalForm.value, 'ssh.loginType', 'pwd')
})
const onChoosePKFile = async () => {
const { success, data } = await SelectKeyFile(i18n.t('dialogue.connection.pkfile_selection_title'))
const { success, data } = await SelectKeyFile(i18n.t('dialogue.connection.ssh.pkfile_selection_title'))
if (!success) {
generalForm.value.ssh.pkFile = ''
} else {
@ -139,6 +151,7 @@ watch(
resetForm()
editName.value = get(dialogStore.connParam, 'name', '')
generalForm.value = dialogStore.connParam || connectionStore.newDefaultConnection()
dbFilterList.value = map(generalForm.value.dbFilterList, (item) => item + '')
generalForm.value.ssh.loginType = generalForm.value.ssh.loginType || 'pwd'
}
},
@ -233,50 +246,91 @@ const onClose = () => {
:rules="generalFormRules()"
:show-require-mark="false"
label-placement="top">
<n-form-item :label="$t('dialogue.connection.advn_filter')" path="defaultFilter">
<n-input
v-model:value="generalForm.defaultFilter"
:placeholder="$t('dialogue.connection.advn_filter_tip')" />
</n-form-item>
<n-form-item :label="$t('dialogue.connection.advn_separator')" path="keySeparator">
<n-input
v-model:value="generalForm.keySeparator"
:placeholder="$t('dialogue.connection.advn_separator_tip')" />
</n-form-item>
<n-form-item :label="$t('dialogue.connection.advn_conn_timeout')" path="connTimeout">
<n-input-number v-model:value="generalForm.connTimeout" :max="999999" :min="1">
<template #suffix>
{{ $t('common.second') }}
</template>
</n-input-number>
</n-form-item>
<n-form-item :label="$t('dialogue.connection.advn_exec_timeout')" path="execTimeout">
<n-input-number v-model:value="generalForm.execTimeout" :max="999999" :min="1">
<template #suffix>
{{ $t('common.second') }}
</template>
</n-input-number>
</n-form-item>
<n-form-item :label="$t('dialogue.connection.advn_mark_color')" path="markColor">
<div
v-for="color in predefineColors"
:key="color"
:class="{
'color-preset-item_selected': generalForm.markColor === color,
}"
:style="{ backgroundColor: color }"
class="color-preset-item"
@click="generalForm.markColor = color">
<n-icon v-if="isEmpty(color)" :component="Close" size="24" />
</div>
</n-form-item>
<n-grid :x-gap="10">
<n-form-item-gi
:span="12"
:label="$t('dialogue.connection.advn.filter')"
path="defaultFilter">
<n-input
v-model:value="generalForm.defaultFilter"
:placeholder="$t('dialogue.connection.advn.filter_tip')" />
</n-form-item-gi>
<n-form-item-gi
:span="12"
:label="$t('dialogue.connection.advn.separator')"
path="keySeparator">
<n-input
v-model:value="generalForm.keySeparator"
:placeholder="$t('dialogue.connection.advn_separator_tip')" />
</n-form-item-gi>
<n-form-item-gi
:span="12"
:label="$t('dialogue.connection.advn.conn_timeout')"
path="connTimeout">
<n-input-number v-model:value="generalForm.connTimeout" :max="999999" :min="1">
<template #suffix>
{{ $t('common.second') }}
</template>
</n-input-number>
</n-form-item-gi>
<n-form-item-gi
:span="12"
:label="$t('dialogue.connection.advn.exec_timeout')"
path="execTimeout">
<n-input-number v-model:value="generalForm.execTimeout" :max="999999" :min="1">
<template #suffix>
{{ $t('common.second') }}
</template>
</n-input-number>
</n-form-item-gi>
<n-form-item-gi :span="24" :label="$t('dialogue.connection.advn.dbfilter_type')">
<n-radio-group v-model:value="generalForm.dbFilterType">
<n-radio-button :label="$t('dialogue.connection.advn.dbfilter_all')" value="none" />
<n-radio-button
:label="$t('dialogue.connection.advn.dbfilter_show')"
value="show" />
<n-radio-button
:label="$t('dialogue.connection.advn.dbfilter_hide')"
value="hide" />
</n-radio-group>
</n-form-item-gi>
<n-form-item-gi :span="24" :label="$t('dialogue.connection.advn.dbfilter_input')">
<n-select
v-model:value="dbFilterList"
:disabled="generalForm.dbFilterType === 'none'"
filterable
multiple
tag
:placeholder="$t('dialogue.connection.advn.dbfilter_input_tip')"
:show-arrow="false"
:show="false"
:clearable="true"
@update:value="onUpdateDBFilterList" />
</n-form-item-gi>
<n-form-item-gi
:span="24"
:label="$t('dialogue.connection.advn.mark_color')"
path="markColor">
<div
v-for="color in predefineColors"
:key="color"
:class="{
'color-preset-item_selected': generalForm.markColor === color,
}"
:style="{ backgroundColor: color }"
class="color-preset-item"
@click="generalForm.markColor = color">
<n-icon v-if="isEmpty(color)" :component="Close" size="24" />
</div>
</n-form-item-gi>
</n-grid>
</n-form>
</n-tab-pane>
<n-tab-pane :tab="$t('dialogue.connection.ssh_tunnel')" display-directive="show" name="ssh">
<n-tab-pane :tab="$t('dialogue.connection.ssh.tunnel')" display-directive="show" name="ssh">
<n-form-item label-placement="left">
<n-checkbox v-model:checked="generalForm.ssh.enable" size="medium">
{{ $t('dialogue.connection.ssh_enable') }}
{{ $t('dialogue.connection.ssh.enable') }}
</n-checkbox>
</n-form-item>
<n-form
@ -288,7 +342,7 @@ const onClose = () => {
<n-form-item :label="$t('dialogue.connection.addr')" required>
<n-input
v-model:value="generalForm.ssh.addr"
:placeholder="$t('dialogue.connection.ssh_addr_tip')" />
:placeholder="$t('dialogue.connection.ssh.addr_tip')" />
<n-text style="width: 40px; text-align: center">:</n-text>
<n-input-number
v-model:value="generalForm.ssh.port"
@ -296,10 +350,10 @@ const onClose = () => {
:min="1"
style="width: 200px" />
</n-form-item>
<n-form-item :label="$t('dialogue.connection.login_type')">
<n-form-item :label="$t('dialogue.connection.ssh.login_type')">
<n-radio-group v-model:value="generalForm.ssh.loginType">
<n-radio-button :label="$t('dialogue.connection.pwd')" value="pwd" />
<n-radio-button :label="$t('dialogue.connection.pkfile')" value="pkfile" />
<n-radio-button :label="$t('dialogue.connection.ssh.pkfile')" value="pkfile" />
</n-radio-group>
</n-form-item>
<n-form-item
@ -307,27 +361,27 @@ const onClose = () => {
:label="$t('dialogue.connection.usr')">
<n-input
v-model:value="generalForm.ssh.username"
:placeholder="$t('dialogue.connection.ssh_usr_tip')" />
:placeholder="$t('dialogue.connection.ssh.usr_tip')" />
</n-form-item>
<n-form-item v-if="sshLoginType === 'pwd'" :label="$t('dialogue.connection.pwd')">
<n-input
v-model:value="generalForm.ssh.password"
:placeholder="$t('dialogue.connection.ssh_pwd_tip')"
:placeholder="$t('dialogue.connection.ssh.pwd_tip')"
show-password-on="click"
type="password" />
</n-form-item>
<n-form-item v-if="sshLoginType === 'pkfile'" :label="$t('dialogue.connection.pkfile')">
<n-form-item v-if="sshLoginType === 'pkfile'" :label="$t('dialogue.connection.ssh.pkfile')">
<n-input-group>
<n-input
v-model:value="generalForm.ssh.pkFile"
:placeholder="$t('dialogue.connection.pkfile_tip')" />
:placeholder="$t('dialogue.connection.ssh.pkfile_tip')" />
<n-button :focusable="false" @click="onChoosePKFile">...</n-button>
</n-input-group>
</n-form-item>
<n-form-item v-if="sshLoginType === 'pkfile'" :label="$t('dialogue.connection.passphrase')">
<n-form-item v-if="sshLoginType === 'pkfile'" :label="$t('dialogue.connection.ssh.passphrase')">
<n-input
v-model:value="generalForm.ssh.passphrase"
:placeholder="$t('dialogue.connection.passphrase_tip')"
:placeholder="$t('dialogue.connection.ssh.passphrase_tip')"
show-password-on="click"
type="password" />
</n-form-item>

View File

@ -127,24 +127,36 @@
"test": "Test Connection",
"test_succ": "Successful connection to redis-server",
"test_fail": "Fail Connection",
"advn_filter": "Default Filter",
"advn_filter_tip": "Pattern which defines loaded keys from redis server",
"advn_separator": "Key Separator",
"advn_separator_tip": "Separator used for key path item",
"advn_conn_timeout": "Connection Timeout",
"advn_exec_timeout": "Execution Timeout",
"advn_mark_color": "Mark Color",
"ssh_enable": "Enable SSH Tuntel",
"ssh_tunnel": "SSH Tunnel",
"login_type": "Login Type",
"pkfile": "Private Key File",
"passphrase": "Passphrase",
"ssh_addr_tip": "SSH Server Host",
"ssh_usr_tip": "SSH Username",
"ssh_pwd_tip": "SSH Password",
"pkfile_tip": "SSH Private Key File Path",
"passphrase_tip": "(Optional) Passphrase for Private Key",
"pkfile_selection_title": "Please Select Private Key File"
"advn": {
"filter": "Default Key Filter Pattern",
"filter_tip": "Pattern which defines loaded keys from redis server",
"separator": "Key Separator",
"separator_tip":"Separator for key path item",
"conn_timeout": "Connection Timeout",
"exec_timeout": "Execution Timeout",
"dbfilter_type": "Database Filter",
"dbfilter_all": "Show All",
"dbfilter_show": "Show Selected",
"dbfilter_hide": "Hide Selected",
"dbfilter_show_title": "Select the Databases to Show",
"dbfilter_hide_title": "Select the Databases to Hide",
"dbfilter_input": "Input Database Index",
"dbfilter_input_tip": "Press Enter to confirm",
"mark_color": "Mark Color"
},
"ssh": {
"enable": "Enable SSH Tuntel",
"tunnel": "SSH Tunnel",
"login_type": "Login Type",
"pkfile": "Private Key File",
"passphrase": "Passphrase",
"addr_tip": "SSH Server Host",
"usr_tip": "SSH Username",
"pwd_tip": "SSH Password",
"pkfile_tip": "SSH Private Key File Path",
"passphrase_tip": "(Optional) Passphrase for Private Key",
"pkfile_selection_title": "Please Select Private Key File"
}
},
"group": {
"name": "Group Name",

View File

@ -127,24 +127,36 @@
"test": "测试连接",
"test_succ": "成功连接到Redis服务器",
"test_fail": "连接失败",
"advn_filter": "默认过滤",
"advn_filter_tip": "需要加载的键名表达式",
"advn_separator": "键分隔符",
"advn_separator_tip": "键名路径分隔符",
"advn_conn_timeout": "连接超时",
"advn_exec_timeout": "执行超时",
"advn_mark_color": "标记颜色",
"ssh_enable": "启用SSH隧道",
"ssh_tunnel": "SSH隧道",
"login_type": "登录类型",
"pkfile": "私钥文件",
"passphrase": "私钥密码",
"ssh_addr_tip": "SSH地址",
"ssh_usr_tip": "SSH登录用户名",
"ssh_pwd_tip": "SSH登录密码",
"pkfile_tip": "SSH私钥文件路径",
"passphrase_tip": "(可选)SSH私钥密码",
"pkfile_selection_title": "请选择私钥文件"
"advn": {
"filter": "默认键过滤表达式",
"filter_tip": "需要加载的键名表达式",
"separator": "键分隔符",
"separator_tip":"键名路径分隔符",
"conn_timeout": "连接超时",
"exec_timeout": "执行超时",
"dbfilter_type": "数据库过滤方式",
"dbfilter_all": "显示所有",
"dbfilter_show": "显示指定",
"dbfilter_hide": "隐藏指定",
"dbfilter_show_title": "需要显示的数据库",
"dbfilter_hide_title": "需要隐藏的数据库",
"dbfilter_input": "输入数据库索引",
"dbfilter_input_tip": "按回车确认",
"mark_color": "标记颜色"
},
"ssh": {
"enable": "启用SSH隧道",
"tunnel": "SSH隧道",
"login_type": "登录类型",
"pkfile": "私钥文件",
"passphrase": "私钥密码",
"addr_tip": "SSH地址",
"usr_tip": "SSH登录用户名",
"pwd_tip": "SSH登录密码",
"pkfile_tip": "SSH私钥文件路径",
"passphrase_tip": "(可选)SSH私钥密码",
"pkfile_selection_title": "请选择私钥文件"
}
},
"group": {
"name": "分组名",

View File

@ -208,6 +208,8 @@ const useConnectionStore = defineStore('connections', {
keySeparator: ':',
connTimeout: 60,
execTimeout: 60,
dbFilterType: 'none',
dbFilterList: [],
markColor: '',
ssh: {
enable: false,

View File

@ -1,5 +1,6 @@
import { defineStore } from 'pinia'
import useConnectionStore from './connections.js'
import { assignWith, isEmpty } from 'lodash'
/**
* connection dialog type
@ -81,7 +82,16 @@ const useDialogStore = defineStore('dialog', {
async openEditDialog(name) {
const connStore = useConnectionStore()
const profile = await connStore.getConnectionProfile(name)
this.connParam = profile || connStore.newDefaultConnection(name)
const assignCustomizer = (objVal, srcVal, key) => {
if (isEmpty(objVal)) {
return srcVal
}
if (isEmpty(srcVal)) {
return objVal
}
return undefined
}
this.connParam = assignWith({}, connStore.newDefaultConnection(name), profile, assignCustomizer)
this.connType = ConnDialogType.EDIT
this.connDialogVisible = true
},