Skip to content

Commit

Permalink
feat: add filter database option #18
Browse files Browse the repository at this point in the history
refactor: tidy struct of translation file
  • Loading branch information
tiny-craft committed Oct 7, 2023
1 parent 7937b5b commit bb1d9f3
Show file tree
Hide file tree
Showing 8 changed files with 214 additions and 98 deletions.
44 changes: 33 additions & 11 deletions backend/services/connection_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"golang.org/x/crypto/ssh"
"net"
"os"
"slices"
"strconv"
"strings"
"sync"
Expand Down Expand Up @@ -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
}
}
}

Expand All @@ -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))
}
}
}

Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions backend/storage/connections.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ func (c *ConnectionsStorage) defaultConnectionItem() types.ConnectionConfig {
KeySeparator: ":",
ConnTimeout: 60,
ExecTimeout: 60,
DBFilterType: "none",
DBFilterList: []int{},
MarkColor: "",
}
}
Expand Down
2 changes: 2 additions & 0 deletions backend/types/connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
}
Expand Down
154 changes: 104 additions & 50 deletions frontend/src/components/dialogs/ConnectionDialog.vue
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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'
}
},
Expand Down Expand Up @@ -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
Expand All @@ -288,46 +342,46 @@ 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"
:max="65535"
: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
v-if="sshLoginType === 'pwd' || sshLoginType === 'pkfile'"
: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>
Expand Down
48 changes: 30 additions & 18 deletions frontend/src/langs/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading

0 comments on commit bb1d9f3

Please sign in to comment.