diff --git a/backend/services/connection_service.go b/backend/services/connection_service.go index 651e4a9..8ac8dbd 100644 --- a/backend/services/connection_service.go +++ b/backend/services/connection_service.go @@ -545,7 +545,7 @@ func (c *connectionService) ScanKeys(connName string, db int, match, keyType str filterType := len(keyType) > 0 - var keys []string + var keys []any //keys := map[string]keyItem{} var cursor uint64 for { @@ -559,7 +559,9 @@ func (c *connectionService) ScanKeys(connName string, db int, match, keyType str resp.Msg = err.Error() return } - keys = append(keys, loadedKey...) + for _, k := range loadedKey { + keys = append(keys, strutil.EncodeRedisKey(k)) + } //for _, k := range loadedKey { // //t, _ := rdb.Type(ctx, k).Result() // keys[k] = keyItem{Type: "t"} @@ -579,13 +581,14 @@ func (c *connectionService) ScanKeys(connName string, db int, match, keyType str } // GetKeyValue get value by key -func (c *connectionService) GetKeyValue(connName string, db int, key, viewAs string) (resp types.JSResp) { +func (c *connectionService) GetKeyValue(connName string, db int, k any, viewAs string) (resp types.JSResp) { rdb, ctx, err := c.getRedisClient(connName, db) if err != nil { resp.Msg = err.Error() return } + key := strutil.DecodeRedisKey(k) var keyType string var dur time.Duration keyType, err = rdb.Type(ctx, key).Result() @@ -717,13 +720,14 @@ func (c *connectionService) GetKeyValue(connName string, db int, key, viewAs str // SetKeyValue set value by key // @param ttl <= 0 means keep current ttl -func (c *connectionService) SetKeyValue(connName string, db int, key, keyType string, value any, ttl int64, viewAs string) (resp types.JSResp) { +func (c *connectionService) SetKeyValue(connName string, db int, k any, keyType string, value any, ttl int64, viewAs string) (resp types.JSResp) { rdb, ctx, err := c.getRedisClient(connName, db) if err != nil { resp.Msg = err.Error() return } + key := strutil.DecodeRedisKey(k) var expiration time.Duration if ttl < 0 { if expiration, err = rdb.PTTL(ctx, key).Result(); err != nil { @@ -839,13 +843,14 @@ func (c *connectionService) SetKeyValue(connName string, db int, key, keyType st } // SetHashValue set hash field -func (c *connectionService) SetHashValue(connName string, db int, key, field, newField, value string) (resp types.JSResp) { +func (c *connectionService) SetHashValue(connName string, db int, k any, field, newField, value string) (resp types.JSResp) { rdb, ctx, err := c.getRedisClient(connName, db) if err != nil { resp.Msg = err.Error() return } + key := strutil.DecodeRedisKey(k) var removedField []string updatedField := map[string]string{} if len(field) <= 0 { @@ -884,13 +889,14 @@ func (c *connectionService) SetHashValue(connName string, db int, key, field, ne } // AddHashField add or update hash field -func (c *connectionService) AddHashField(connName string, db int, key string, action int, fieldItems []any) (resp types.JSResp) { +func (c *connectionService) AddHashField(connName string, db int, k any, action int, fieldItems []any) (resp types.JSResp) { rdb, ctx, err := c.getRedisClient(connName, db) if err != nil { resp.Msg = err.Error() return } + key := strutil.DecodeRedisKey(k) updated := map[string]any{} switch action { case 1: @@ -929,13 +935,14 @@ func (c *connectionService) AddHashField(connName string, db int, key string, ac } // AddListItem add item to list or remove from it -func (c *connectionService) AddListItem(connName string, db int, key string, action int, items []any) (resp types.JSResp) { +func (c *connectionService) AddListItem(connName string, db int, k any, action int, items []any) (resp types.JSResp) { rdb, ctx, err := c.getRedisClient(connName, db) if err != nil { resp.Msg = err.Error() return } + key := strutil.DecodeRedisKey(k) var leftPush, rightPush []any switch action { case 0: @@ -961,13 +968,14 @@ func (c *connectionService) AddListItem(connName string, db int, key string, act } // SetListItem update or remove list item by index -func (c *connectionService) SetListItem(connName string, db int, key string, index int64, value string) (resp types.JSResp) { +func (c *connectionService) SetListItem(connName string, db int, k any, index int64, value string) (resp types.JSResp) { rdb, ctx, err := c.getRedisClient(connName, db) if err != nil { resp.Msg = err.Error() return } + key := strutil.DecodeRedisKey(k) var removed []int64 updated := map[int64]string{} if len(value) <= 0 { @@ -1003,13 +1011,14 @@ func (c *connectionService) SetListItem(connName string, db int, key string, ind } // SetSetItem add members to set or remove from set -func (c *connectionService) SetSetItem(connName string, db int, key string, remove bool, members []any) (resp types.JSResp) { +func (c *connectionService) SetSetItem(connName string, db int, k any, remove bool, members []any) (resp types.JSResp) { rdb, ctx, err := c.getRedisClient(connName, db) if err != nil { resp.Msg = err.Error() return } + key := strutil.DecodeRedisKey(k) if remove { _, err = rdb.SRem(ctx, key, members...).Result() } else { @@ -1025,13 +1034,14 @@ func (c *connectionService) SetSetItem(connName string, db int, key string, remo } // UpdateSetItem replace member of set -func (c *connectionService) UpdateSetItem(connName string, db int, key, value, newValue string) (resp types.JSResp) { +func (c *connectionService) UpdateSetItem(connName string, db int, k any, value, newValue string) (resp types.JSResp) { rdb, ctx, err := c.getRedisClient(connName, db) if err != nil { resp.Msg = err.Error() return } + key := strutil.DecodeRedisKey(k) _, _ = rdb.SRem(ctx, key, value).Result() _, err = rdb.SAdd(ctx, key, newValue).Result() if err != nil { @@ -1044,13 +1054,14 @@ func (c *connectionService) UpdateSetItem(connName string, db int, key, value, n } // UpdateZSetValue update value of sorted set member -func (c *connectionService) UpdateZSetValue(connName string, db int, key, value, newValue string, score float64) (resp types.JSResp) { +func (c *connectionService) UpdateZSetValue(connName string, db int, k any, value, newValue string, score float64) (resp types.JSResp) { rdb, ctx, err := c.getRedisClient(connName, db) if err != nil { resp.Msg = err.Error() return } + key := strutil.DecodeRedisKey(k) updated := map[string]any{} var removed []string if len(newValue) <= 0 { @@ -1094,13 +1105,14 @@ func (c *connectionService) UpdateZSetValue(connName string, db int, key, value, } // AddZSetValue add item to sorted set -func (c *connectionService) AddZSetValue(connName string, db int, key string, action int, valueScore map[string]float64) (resp types.JSResp) { +func (c *connectionService) AddZSetValue(connName string, db int, k any, action int, valueScore map[string]float64) (resp types.JSResp) { rdb, ctx, err := c.getRedisClient(connName, db) if err != nil { resp.Msg = err.Error() return } + key := strutil.DecodeRedisKey(k) members := maputil.ToSlice(valueScore, func(k string) redis.Z { return redis.Z{ Score: valueScore[k], @@ -1126,13 +1138,14 @@ func (c *connectionService) AddZSetValue(connName string, db int, key string, ac } // AddStreamValue add stream field -func (c *connectionService) AddStreamValue(connName string, db int, key, ID string, fieldItems []any) (resp types.JSResp) { +func (c *connectionService) AddStreamValue(connName string, db int, k any, ID string, fieldItems []any) (resp types.JSResp) { rdb, ctx, err := c.getRedisClient(connName, db) if err != nil { resp.Msg = err.Error() return } + key := strutil.DecodeRedisKey(k) _, err = rdb.XAdd(ctx, &redis.XAddArgs{ Stream: key, ID: ID, @@ -1148,26 +1161,28 @@ func (c *connectionService) AddStreamValue(connName string, db int, key, ID stri } // RemoveStreamValues remove stream values by id -func (c *connectionService) RemoveStreamValues(connName string, db int, key string, IDs []string) (resp types.JSResp) { +func (c *connectionService) RemoveStreamValues(connName string, db int, k any, IDs []string) (resp types.JSResp) { rdb, ctx, err := c.getRedisClient(connName, db) if err != nil { resp.Msg = err.Error() return } + key := strutil.DecodeRedisKey(k) _, err = rdb.XDel(ctx, key, IDs...).Result() resp.Success = true return } // SetKeyTTL set ttl of key -func (c *connectionService) SetKeyTTL(connName string, db int, key string, ttl int64) (resp types.JSResp) { +func (c *connectionService) SetKeyTTL(connName string, db int, k any, ttl int64) (resp types.JSResp) { rdb, ctx, err := c.getRedisClient(connName, db) if err != nil { resp.Msg = err.Error() return } + key := strutil.DecodeRedisKey(k) var expiration time.Duration if ttl < 0 { if err = rdb.Persist(ctx, key).Err(); err != nil { @@ -1187,14 +1202,15 @@ func (c *connectionService) SetKeyTTL(connName string, db int, key string, ttl i } // DeleteKey remove redis key -func (c *connectionService) DeleteKey(connName string, db int, key string) (resp types.JSResp) { +func (c *connectionService) DeleteKey(connName string, db int, k any) (resp types.JSResp) { rdb, ctx, err := c.getRedisClient(connName, db) if err != nil { resp.Msg = err.Error() return } - var deletedKeys []string + key := strutil.DecodeRedisKey(k) + var deletedKeys []string if strings.HasSuffix(key, "*") { // delete by prefix var cursor uint64 diff --git a/backend/utils/string/common.go b/backend/utils/string/common.go new file mode 100644 index 0000000..c93d177 --- /dev/null +++ b/backend/utils/string/common.go @@ -0,0 +1,19 @@ +package strutil + +import "unicode/utf8" + +func containsBinary(str string) bool { + //buf := []byte(str) + //size := 0 + //for start := 0; start < len(buf); start += size { + // var r rune + // if r, size = utf8.DecodeRune(buf[start:]); r == utf8.RuneError { + // return true + // } + //} + + if !utf8.ValidString(str) { + return true + } + return false +} diff --git a/backend/utils/string/convert.go b/backend/utils/string/convert.go index 72e5caf..cae1d18 100644 --- a/backend/utils/string/convert.go +++ b/backend/utils/string/convert.go @@ -14,7 +14,6 @@ import ( "strconv" "strings" "tinyrdm/backend/types" - "unicode/utf8" ) // ConvertTo convert string to specified type @@ -55,7 +54,7 @@ func ConvertTo(str, targetType string) (value, resultType string) { return case types.HEX: - if hexStr, ok := decodeHex(str); ok { + if hexStr, ok := decodeToHex(str); ok { value = hexStr } else { value = str @@ -162,8 +161,8 @@ func autoToType(str string) (value, resultType string) { return } - if isBinary(str) { - if value, ok = decodeHex(str); ok { + if containsBinary(str) { + if value, ok = decodeToHex(str); ok { resultType = types.HEX return } @@ -175,22 +174,6 @@ func autoToType(str string) (value, resultType string) { return } -func isBinary(str string) bool { - //buf := []byte(str) - //size := 0 - //for start := 0; start < len(buf); start += size { - // var r rune - // if r, size = utf8.DecodeRune(buf[start:]); r == utf8.RuneError { - // return true - // } - //} - - if !utf8.ValidString(str) { - return true - } - return false -} - func decodeJson(str string) (string, bool) { var data any if (strings.HasPrefix(str, "{") && strings.HasSuffix(str, "}")) || @@ -220,7 +203,7 @@ func decodeBinary(str string) (string, bool) { return binary.String(), true } -func decodeHex(str string) (string, bool) { +func decodeToHex(str string) (string, bool) { decodeStr := hex.EncodeToString([]byte(str)) var resultStr strings.Builder for i := 0; i < len(decodeStr); i += 2 { diff --git a/backend/utils/string/key_convert.go b/backend/utils/string/key_convert.go new file mode 100644 index 0000000..ef5b3a5 --- /dev/null +++ b/backend/utils/string/key_convert.go @@ -0,0 +1,76 @@ +package strutil + +import ( + "strconv" + sliceutil "tinyrdm/backend/utils/slice" +) + +// EncodeRedisKey encode the redis key to integer array +// if key contains binary which could not display on ui, convert the key to char array +func EncodeRedisKey(key string) any { + if containsBinary(key) { + b := []byte(key) + arr := make([]int, len(b)) + for i, bb := range b { + arr[i] = int(bb) + } + return arr + } + return key +} + +// DecodeRedisKey decode redis key to readable string +func DecodeRedisKey(key any) string { + switch key.(type) { + case string: + return key.(string) + + case []any: + arr := key.([]any) + bytes := sliceutil.Map(arr, func(i int) byte { + if c, ok := AnyToInt(arr[i]); ok { + return byte(c) + } + return '0' + }) + return string(bytes) + + case []int: + arr := key.([]int) + b := make([]byte, len(arr)) + for i, bb := range arr { + b[i] = byte(bb) + } + return string(b) + } + return "" +} + +// AnyToInt convert any value to int +func AnyToInt(val any) (int, bool) { + switch val.(type) { + case string: + num, err := strconv.Atoi(val.(string)) + if err != nil { + return 0, false + } + return num, true + case float64: + return int(val.(float64)), true + case float32: + return int(val.(float32)), true + case int64: + return int(val.(int64)), true + case int32: + return int(val.(int32)), true + case int: + return val.(int), true + case bool: + if val.(bool) { + return 1, true + } else { + return 0, true + } + } + return 0, false +} diff --git a/frontend/src/components/common/RedisTypeTag.vue b/frontend/src/components/common/RedisTypeTag.vue index 5ab4390..3bba2de 100644 --- a/frontend/src/components/common/RedisTypeTag.vue +++ b/frontend/src/components/common/RedisTypeTag.vue @@ -1,6 +1,7 @@ + + + + diff --git a/frontend/src/components/sidebar/BrowserTree.vue b/frontend/src/components/sidebar/BrowserTree.vue index 57bd23a..baf7366 100644 --- a/frontend/src/components/sidebar/BrowserTree.vue +++ b/frontend/src/components/sidebar/BrowserTree.vue @@ -3,6 +3,7 @@ import { computed, h, nextTick, onMounted, reactive, ref } from 'vue' import { ConnectionType } from '@/consts/connection_type.js' import { NIcon, NSpace, NTag } from 'naive-ui' import Key from '@/components/icons/Key.vue' +import Binary from '@/components/icons/Binary.vue' import ToggleDb from '@/components/icons/ToggleDb.vue' import { find, get, includes, indexOf, isEmpty, pull, remove, size } from 'lodash' import { useI18n } from 'vue-i18n' @@ -223,7 +224,9 @@ const handleSelectContextMenu = (key) => { return } const node = connectionStore.getNode(selectedKey) - const { db, key: nodeKey, redisKey } = node || {} + const { db, key: nodeKey } = node || {} + const redisKey = node.redisKeyCode || node.redisKey + const redisKeyName = !!node.redisKeyCode ? node.label : redisKey switch (key) { case 'server_info': tabStore.setSelectedKeys(props.server) @@ -267,10 +270,10 @@ const handleSelectContextMenu = (key) => { dialogStore.openDeleteKeyDialog(props.server, db, isEmpty(redisKey) ? '*' : redisKey + ':*') break case 'value_remove': - $dialog.warning(i18n.t('dialogue.remove_tip', { name: redisKey }), () => { + $dialog.warning(i18n.t('dialogue.remove_tip', { name: redisKeyName }), () => { connectionStore.deleteKey(props.server, db, redisKey).then((success) => { if (success) { - $message.success(i18n.t('dialogue.delete_key_succ', { key: redisKey })) + $message.success(i18n.t('dialogue.delete_key_succ', { key: redisKeyName })) } }) }) @@ -327,7 +330,8 @@ const onUpdateSelectedKeys = (keys, options) => { // prevent load duplicate key for (const node of options) { if (node.type === ConnectionType.RedisValue) { - const { key, db, redisKey } = node + const { key, db } = node + const redisKey = node.redisKeyCode || node.redisKey if (!includes(selectedKeys.value, key)) { connectionStore.loadKeyValue(props.server, db, redisKey) } @@ -373,7 +377,7 @@ const renderPrefix = ({ option }) => { NIcon, { size: 20 }, { - default: () => h(Key), + default: () => h(!!option.redisKeyCode ? Binary : Key), }, ) } diff --git a/frontend/src/langs/en.json b/frontend/src/langs/en.json index 342927b..89496eb 100644 --- a/frontend/src/langs/en.json +++ b/frontend/src/langs/en.json @@ -80,6 +80,7 @@ "batch_delete": "Batch Delete", "copy_path": "Copy Path", "copy_key": "Copy Key", + "binary_key": "Binary Key Name", "remove_key": "Remove Key", "new_key": "Add New Key", "nonexist_tab_content": "Selected key does not exist. Please retry", @@ -105,6 +106,7 @@ "delete_key_succ": "\"{key}\" has been deleted", "save_value_succ": "Value Saved !", "copy_succ": "Value Copied !", + "rename_binary_key_fail": "Rename binary key name is unsupported", "handle_succ": "Success!", "reload_succ": "Reloaded!", "field_required": "This item should not be blank", diff --git a/frontend/src/langs/zh-cn.json b/frontend/src/langs/zh-cn.json index 243db65..4daa08e 100644 --- a/frontend/src/langs/zh-cn.json +++ b/frontend/src/langs/zh-cn.json @@ -80,6 +80,7 @@ "batch_delete": "批量删除键", "copy_path": "复制路径", "copy_key": "复制键名", + "binary_key": "二进制键名", "remove_key": "删除键", "new_key": "添加新键", "nonexist_tab_content": "所选键不存在,请尝试刷新重试", @@ -105,6 +106,7 @@ "delete_key_succ": "{key} 已被删除", "save_value_succ": "已保存值", "copy_succ": "已复制到剪切板", + "rename_binary_key_fail": "不支持重命名二进制键名", "handle_succ": "操作成功", "reload_succ": "已重新载入", "field_required": "此项不能为空", diff --git a/frontend/src/stores/connections.js b/frontend/src/stores/connections.js index d591356..a731a13 100644 --- a/frontend/src/stores/connections.js +++ b/frontend/src/stores/connections.js @@ -5,6 +5,7 @@ import { get, isEmpty, join, + map, remove, size, slice, @@ -49,6 +50,7 @@ import { import { ConnectionType } from '@/consts/connection_type.js' import useTabStore from './tab.js' import { types } from '@/consts/support_redis_type.js' +import { decodeRedisKey, nativeRedisKey } from '@/utils/key_convert.js' const useConnectionStore = defineStore('connections', { /** @@ -68,6 +70,7 @@ const useConnectionStore = defineStore('connections', { * @property {number} type * @property {number} [db] - database index, type == ConnectionType.RedisDB only * @property {string} [redisKey] - redis key, type == ConnectionType.RedisKey || type == ConnectionType.RedisValue only + * @property {string} [redisKeyCode] - redis key char code array, optional for redis key which contains binary data * @property {number} [keys] - children key count * @property {boolean} [isLeaf] * @property {boolean} [opened] - redis db is opened, type == ConnectionType.RedisDB only @@ -592,7 +595,7 @@ const useConnectionStore = defineStore('connections', { * load redis key * @param {string} server * @param {number} db - * @param {string} [key] when key is null or blank, update tab to display normal content (blank content or server status) + * @param {string|number[]} [key] when key is null or blank, update tab to display normal content (blank content or server status) * @param {string} [viewType] */ async loadKeyValue(server, db, key, viewType) { @@ -602,12 +605,15 @@ const useConnectionStore = defineStore('connections', { const { data, success, msg } = await GetKeyValue(server, db, key, viewType) if (success) { const { type, ttl, value, size, viewAs } = data + const k = decodeRedisKey(key) + const binaryKey = k !== key tab.upsertTab({ server, db, type, ttl, - key, + keyCode: binaryKey ? key : undefined, + key: k, value, size, viewAs, @@ -629,6 +635,7 @@ const useConnectionStore = defineStore('connections', { type: 'none', ttl: -1, key: null, + keyCode: null, value: null, size: 0, }) @@ -713,7 +720,7 @@ const useConnectionStore = defineStore('connections', { * remove keys in db * @param {string} connName * @param {number} db - * @param {string[]} keys + * @param {Array} keys * @param {boolean} [sortInsert] * @return {{success: boolean, newKey: number, newLayer: number, replaceKey: number}} * @private @@ -735,7 +742,9 @@ const useConnectionStore = defineStore('connections', { const nodeMap = this._getNodeMap(connName, db) const rootChildren = selDB.children for (const key of keys) { - const keyParts = split(key, separator) + const k = decodeRedisKey(key) + const binaryKey = k !== key + const keyParts = binaryKey ? [nativeRedisKey(key)] : split(k, separator) const len = size(keyParts) const lastIdx = len - 1 let handlePath = '' @@ -776,10 +785,11 @@ const useConnectionStore = defineStore('connections', { const replaceKey = nodeMap.has(nodeKey) const selectedNode = { key: `${connName}/db${db}#${nodeKey}`, - label: keyParts[i], + label: binaryKey ? k : keyParts[i], db, keys: 0, redisKey: handlePath, + redisKeyCode: binaryKey ? key : undefined, type: ConnectionType.RedisValue, isLeaf: true, } @@ -926,7 +936,7 @@ const useConnectionStore = defineStore('connections', { * set redis key * @param {string} connName * @param {number} db - * @param {string} key + * @param {string|number[]} key * @param {string} keyType * @param {any} value * @param {number} ttl @@ -959,7 +969,7 @@ const useConnectionStore = defineStore('connections', { * when both field and newField are set, and field !== newField, delete field and add newField * @param {string} connName * @param {number} db - * @param {string} key + * @param {string|number[]} key * @param {string} field * @param {string} newField * @param {string} value @@ -983,7 +993,7 @@ const useConnectionStore = defineStore('connections', { * insert or update hash field item * @param {string} connName * @param {number} db - * @param {string} key + * @param {string|number[]} key * @param {number }action 0:ignore duplicated fields 1:overwrite duplicated fields * @param {string[]} fieldItems field1, value1, filed2, value2... * @returns {Promise<{[msg]: string, success: boolean, [updated]: {}}>} @@ -1028,7 +1038,7 @@ const useConnectionStore = defineStore('connections', { * insert list item * @param {string} connName * @param {number} db - * @param {string} key + * @param {string|number[]} key * @param {int} action 0: push to head, 1: push to tail * @param {string[]}values * @returns {Promise<*|{msg, success: boolean}>} @@ -1089,7 +1099,7 @@ const useConnectionStore = defineStore('connections', { * update value of list item by index * @param {string} connName * @param {number} db - * @param {string} key + * @param {string|number[]} key * @param {number} index * @param {string} value * @returns {Promise<{[msg]: string, success: boolean, [updated]: {}}>} @@ -1112,7 +1122,7 @@ const useConnectionStore = defineStore('connections', { * remove list item * @param {string} connName * @param {number} db - * @param {string} key + * @param {string|number[]} key * @param {number} index * @returns {Promise<{[msg]: string, success: boolean, [removed]: string[]}>} */ @@ -1134,7 +1144,7 @@ const useConnectionStore = defineStore('connections', { * add item to set * @param {string} connName * @param {number} db - * @param {string} key + * @param {string|number} key * @param {string} value * @returns {Promise<{[msg]: string, success: boolean}>} */ @@ -1155,7 +1165,7 @@ const useConnectionStore = defineStore('connections', { * update value of set item * @param {string} connName * @param {number} db - * @param {string} key + * @param {string|number[]} key * @param {string} value * @param {string} newValue * @returns {Promise<{[msg]: string, success: boolean}>} @@ -1175,10 +1185,10 @@ const useConnectionStore = defineStore('connections', { /** * remove item from set - * @param connName - * @param db - * @param key - * @param value + * @param {string} connName + * @param {number} db + * @param {string|number[]} key + * @param {string} value * @returns {Promise<{[msg]: string, success: boolean}>} */ async removeSetItem(connName, db, key, value) { @@ -1198,7 +1208,7 @@ const useConnectionStore = defineStore('connections', { * add item to sorted set * @param {string} connName * @param {number} db - * @param {string} key + * @param {string|number[]} key * @param {number} action * @param {Object.} vs value: score * @returns {Promise<{[msg]: string, success: boolean}>} @@ -1220,7 +1230,7 @@ const useConnectionStore = defineStore('connections', { * update item of sorted set * @param {string} connName * @param {number} db - * @param {string} key + * @param {string|number[]} key * @param {string} value * @param {string} newValue * @param {number} score @@ -1244,7 +1254,7 @@ const useConnectionStore = defineStore('connections', { * remove item from sorted set * @param {string} connName * @param {number} db - * @param key + * @param {string|number[]} key * @param {string} value * @returns {Promise<{[msg]: string, success: boolean, [removed]: []}>} */ @@ -1266,7 +1276,7 @@ const useConnectionStore = defineStore('connections', { * insert new stream field item * @param {string} connName * @param {number} db - * @param {string} key + * @param {string|number[]} key * @param {string} id * @param {string[]} values field1, value1, filed2, value2... * @returns {Promise<{[msg]: string, success: boolean, [updated]: {}}>} @@ -1289,7 +1299,7 @@ const useConnectionStore = defineStore('connections', { * remove stream field * @param {string} connName * @param {number} db - * @param {string} key + * @param {string|number[]} key * @param {string[]|string} ids * @returns {Promise<{[msg]: {}, success: boolean, [removed]: string[]}>} */ @@ -1427,7 +1437,7 @@ const useConnectionStore = defineStore('connections', { * delete redis key * @param {string} connName * @param {number} db - * @param {string} key + * @param {string|number[]} key * @param {boolean} [soft] do not try to remove from redis if true, just remove from tree data * @returns {Promise} */ @@ -1437,9 +1447,10 @@ const useConnectionStore = defineStore('connections', { await DeleteKey(connName, db, key) } + const k = nativeRedisKey(key) // update tree view data - this._deleteKeyNode(connName, db, key) - this._tidyNode(connName, db, key, true) + this._deleteKeyNode(connName, db, k) + this._tidyNode(connName, db, k, true) // set tab content empty const tab = useTabStore() diff --git a/frontend/src/stores/dialog.js b/frontend/src/stores/dialog.js index 7d5d4b4..4652e85 100644 --- a/frontend/src/stores/dialog.js +++ b/frontend/src/stores/dialog.js @@ -45,6 +45,7 @@ const useDialogStore = defineStore('dialog', { server: '', db: 0, key: '', + keyCode: null, type: null, }, addFieldsDialogVisible: false, @@ -185,12 +186,14 @@ const useDialogStore = defineStore('dialog', { * @param {string} server * @param {number} db * @param {string} key + * @param {number[]|null} keyCode * @param {string} type */ - openAddFieldsDialog(server, db, key, type) { + openAddFieldsDialog(server, db, key, keyCode, type) { this.addFieldParam.server = server this.addFieldParam.db = db this.addFieldParam.key = key + this.addFieldParam.keyCode = keyCode this.addFieldParam.type = type this.addFieldsDialogVisible = true }, diff --git a/frontend/src/stores/tab.js b/frontend/src/stores/tab.js index 8ed3d9b..ebc8bf2 100644 --- a/frontend/src/stores/tab.js +++ b/frontend/src/stores/tab.js @@ -14,6 +14,7 @@ const useTabStore = defineStore('tab', { * @property {string} [server] server name * @property {int} [db] database index * @property {string} [key] current key name + * @property {number[]|null|undefined} [keyCode] current key name as char array * @property {int} [ttl] ttl of current key */ @@ -83,11 +84,12 @@ const useTabStore = defineStore('tab', { * @param {number} [type] * @param {number} [ttl] * @param {string} [key] + * @param {string} [keyCode] * @param {number} [size] * @param {*} [value] * @param {string} [viewAs] */ - upsertTab({ server, db, type, ttl, key, size, value, viewAs }) { + upsertTab({ server, db, type, ttl, key, keyCode, size, value, viewAs }) { let tabIndex = findIndex(this.tabList, { name: server }) if (tabIndex === -1) { this.tabList.push({ @@ -97,6 +99,7 @@ const useTabStore = defineStore('tab', { type, ttl, key, + keyCode, size, value, viewAs, @@ -112,6 +115,7 @@ const useTabStore = defineStore('tab', { tab.type = type tab.ttl = ttl tab.key = key + tab.keyCode = keyCode tab.size = size tab.value = value tab.viewAs = viewAs @@ -123,7 +127,7 @@ const useTabStore = defineStore('tab', { * update ttl by tag * @param {string} server * @param {number} db - * @param {string} key + * @param {string|number[]} key * @param {number} ttl */ updateTTL({ server, db, key, ttl }) { diff --git a/frontend/src/utils/key_convert.js b/frontend/src/utils/key_convert.js new file mode 100644 index 0000000..7aef0f8 --- /dev/null +++ b/frontend/src/utils/key_convert.js @@ -0,0 +1,35 @@ +import { join, map } from 'lodash' + +/** + * converted binary data in strings to hex format + * @param {string|number[]} key + * @return {string} + */ +export function decodeRedisKey(key) { + if (key instanceof Array) { + // char array, convert to hex string + return join( + map(key, (k) => { + if (k >= 32 && k <= 126) { + return String.fromCharCode(k) + } + return '\\x' + k.toString(16).toUpperCase().padStart(2, '0') + }), + '', + ) + } + + return key +} + +/** + * convert char code array to string + * @param {string|number[]} key + * @return {string} + */ +export function nativeRedisKey(key) { + if (key instanceof Array) { + return map(key, (c) => String.fromCharCode(c)).join('') + } + return key +} diff --git a/wails.json b/wails.json index 59fe550..6ffc825 100644 --- a/wails.json +++ b/wails.json @@ -13,7 +13,7 @@ "info": { "companyName": "Tiny Craft", "productName": "Tiny RDM", - "productVersion": "0.9.0", + "productVersion": "1.0.0", "copyright": "Copyright © 2023", "comments": "Tiny Redis Desktop Manager" }