refactor: move the handling of String viewing methods to backend
feat: add view as gzip/deflate #30
This commit is contained in:
parent
7aba27e5f9
commit
ffc50a7fcc
|
@ -17,6 +17,7 @@ import (
|
|||
"tinyrdm/backend/types"
|
||||
maputil "tinyrdm/backend/utils/map"
|
||||
redis2 "tinyrdm/backend/utils/redis"
|
||||
strutil "tinyrdm/backend/utils/string"
|
||||
)
|
||||
|
||||
type cmdHistoryItem struct {
|
||||
|
@ -476,7 +477,7 @@ 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 string) (resp types.JSResp) {
|
||||
func (c *connectionService) GetKeyValue(connName string, db int, key, viewAs string) (resp types.JSResp) {
|
||||
rdb, ctx, err := c.getRedisClient(connName, db)
|
||||
if err != nil {
|
||||
resp.Msg = err.Error()
|
||||
|
@ -512,7 +513,9 @@ func (c *connectionService) GetKeyValue(connName string, db int, key string) (re
|
|||
var cursor uint64
|
||||
switch strings.ToLower(keyType) {
|
||||
case "string":
|
||||
value, err = rdb.Get(ctx, key).Result()
|
||||
var str string
|
||||
str, err = rdb.Get(ctx, key).Result()
|
||||
value, viewAs = strutil.ConvertTo(str, viewAs)
|
||||
size, _ = rdb.StrLen(ctx, key).Result()
|
||||
case "list":
|
||||
value, err = rdb.LRange(ctx, key, 0, -1).Result()
|
||||
|
@ -601,17 +604,18 @@ func (c *connectionService) GetKeyValue(connName string, db int, key string) (re
|
|||
}
|
||||
resp.Success = true
|
||||
resp.Data = map[string]any{
|
||||
"type": keyType,
|
||||
"ttl": ttl,
|
||||
"value": value,
|
||||
"size": size,
|
||||
"type": keyType,
|
||||
"ttl": ttl,
|
||||
"value": value,
|
||||
"size": size,
|
||||
"viewAs": viewAs,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 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) (resp types.JSResp) {
|
||||
func (c *connectionService) SetKeyValue(connName string, db int, key, 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()
|
||||
|
@ -632,7 +636,12 @@ func (c *connectionService) SetKeyValue(connName string, db int, key, keyType st
|
|||
resp.Msg = "invalid string value"
|
||||
return
|
||||
} else {
|
||||
_, err = rdb.Set(ctx, key, str, 0).Result()
|
||||
var saveStr string
|
||||
if saveStr, err = strutil.SaveAs(str, viewAs); err != nil {
|
||||
resp.Msg = fmt.Sprintf(`save to "%s" type fail: %s`, viewAs, err.Error())
|
||||
return
|
||||
}
|
||||
_, err = rdb.Set(ctx, key, saveStr, 0).Result()
|
||||
// set expiration lonely, not "keepttl"
|
||||
if err == nil && expiration > 0 {
|
||||
rdb.Expire(ctx, key, expiration)
|
||||
|
|
|
@ -104,10 +104,8 @@ func (p *preferencesService) GetAppVersion() (resp types.JSResp) {
|
|||
|
||||
func (p *preferencesService) SaveWindowSize(width, height int) {
|
||||
p.SetPreferences(map[string]any{
|
||||
"behavior": map[string]any{
|
||||
"window_width": width,
|
||||
"window_height": height,
|
||||
},
|
||||
"behavior.window_width": width,
|
||||
"behavior.window_height": height,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package storage
|
|||
import (
|
||||
"fmt"
|
||||
"gopkg.in/yaml.v3"
|
||||
"log"
|
||||
"strings"
|
||||
"sync"
|
||||
"tinyrdm/backend/consts"
|
||||
|
@ -158,10 +159,12 @@ func (p *PreferencesStorage) SetPreferencesN(values map[string]any) error {
|
|||
|
||||
pf := p.getPreferences()
|
||||
for path, v := range values {
|
||||
log.Println("path", path, v)
|
||||
if err := p.setPreferences(pf, path, v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
log.Println("after save", pf)
|
||||
|
||||
return p.savePreferences(pf)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package types
|
||||
|
||||
const PLAIN_TEXT = "Plain Text"
|
||||
const JSON = "JSON"
|
||||
const BASE64_TEXT = "Base64 Text"
|
||||
const BASE64_JSON = "Base64 JSON"
|
||||
const HEX = "Hex"
|
||||
const BINARY = "Binary"
|
||||
const GZIP = "GZip"
|
||||
const GZIP_JSON = "GZip JSON"
|
||||
const DEFLATE = "Deflate"
|
|
@ -0,0 +1,275 @@
|
|||
package strutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/flate"
|
||||
"compress/gzip"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"strings"
|
||||
"tinyrdm/backend/types"
|
||||
)
|
||||
|
||||
// ConvertTo convert string to specified type
|
||||
// @param targetType empty string indicates automatic detection of the string type
|
||||
func ConvertTo(str, targetType string) (value, resultType string) {
|
||||
if len(str) <= 0 {
|
||||
// empty content
|
||||
if len(targetType) <= 0 {
|
||||
resultType = types.PLAIN_TEXT
|
||||
} else {
|
||||
resultType = targetType
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
switch targetType {
|
||||
case types.PLAIN_TEXT:
|
||||
value = str
|
||||
resultType = targetType
|
||||
return
|
||||
|
||||
case types.JSON:
|
||||
value, _ = decodeJson(str)
|
||||
resultType = targetType
|
||||
return
|
||||
|
||||
case types.BASE64_TEXT, types.BASE64_JSON:
|
||||
if base64Str, ok := decodeBase64(str); ok {
|
||||
if targetType == types.BASE64_JSON {
|
||||
value, _ = decodeJson(base64Str)
|
||||
} else {
|
||||
value = base64Str
|
||||
}
|
||||
} else {
|
||||
value = str
|
||||
}
|
||||
resultType = targetType
|
||||
return
|
||||
|
||||
case types.HEX:
|
||||
if hexStr, ok := decodeHex(str); ok {
|
||||
log.Print(hexStr)
|
||||
value = hexStr
|
||||
} else {
|
||||
value = str
|
||||
}
|
||||
resultType = targetType
|
||||
return
|
||||
|
||||
case types.BINARY:
|
||||
var binary strings.Builder
|
||||
for _, char := range str {
|
||||
binary.WriteString(fmt.Sprintf("%08b", int(char)))
|
||||
}
|
||||
value = binary.String()
|
||||
resultType = targetType
|
||||
return
|
||||
|
||||
case types.GZIP, types.GZIP_JSON:
|
||||
if gzipStr, ok := decodeGZip(str); ok {
|
||||
if targetType == types.BASE64_JSON {
|
||||
value, _ = decodeJson(gzipStr)
|
||||
} else {
|
||||
value = gzipStr
|
||||
}
|
||||
} else {
|
||||
value = str
|
||||
}
|
||||
resultType = targetType
|
||||
return
|
||||
|
||||
case types.DEFLATE:
|
||||
value, _ = decodeDeflate(str)
|
||||
resultType = targetType
|
||||
return
|
||||
}
|
||||
|
||||
// type isn't specified or unknown, try to automatically detect and return converted value
|
||||
return autoToType(str)
|
||||
}
|
||||
|
||||
// attempt automatic convert to possible types
|
||||
// if no conversion is possible, it will return the origin string value and "plain text" type
|
||||
func autoToType(str string) (value, resultType string) {
|
||||
if len(str) > 0 {
|
||||
var ok bool
|
||||
if value, ok = decodeJson(str); ok {
|
||||
resultType = types.JSON
|
||||
return
|
||||
}
|
||||
|
||||
if value, ok = decodeBase64(str); ok {
|
||||
if value, ok = decodeJson(value); ok {
|
||||
resultType = types.BASE64_JSON
|
||||
return
|
||||
}
|
||||
resultType = types.BASE64_TEXT
|
||||
return
|
||||
}
|
||||
|
||||
if value, ok = decodeGZip(str); ok {
|
||||
resultType = types.GZIP
|
||||
return
|
||||
}
|
||||
|
||||
if value, ok = decodeDeflate(str); ok {
|
||||
resultType = types.DEFLATE
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
value = str
|
||||
resultType = types.PLAIN_TEXT
|
||||
return
|
||||
}
|
||||
|
||||
func decodeJson(str string) (string, bool) {
|
||||
var data any
|
||||
if (strings.HasPrefix(str, "{") && strings.HasSuffix(str, "}")) ||
|
||||
(strings.HasPrefix(str, "[") && strings.HasSuffix(str, "]")) {
|
||||
if err := json.Unmarshal([]byte(str), &data); err == nil {
|
||||
var jsonByte []byte
|
||||
if jsonByte, err = json.MarshalIndent(data, "", " "); err == nil {
|
||||
return string(jsonByte), true
|
||||
}
|
||||
}
|
||||
}
|
||||
return str, false
|
||||
}
|
||||
|
||||
func decodeBase64(str string) (string, bool) {
|
||||
if decodedStr, err := base64.StdEncoding.DecodeString(str); err == nil {
|
||||
return string(decodedStr), true
|
||||
}
|
||||
return str, false
|
||||
}
|
||||
|
||||
func decodeHex(str string) (string, bool) {
|
||||
encodeStr := hex.EncodeToString([]byte(str))
|
||||
var resultStr strings.Builder
|
||||
for i := 0; i < len(encodeStr); i += 2 {
|
||||
resultStr.WriteString("\\x")
|
||||
resultStr.WriteString(encodeStr[i : i+2])
|
||||
}
|
||||
return resultStr.String(), true
|
||||
}
|
||||
|
||||
func decodeGZip(str string) (string, bool) {
|
||||
if reader, err := gzip.NewReader(strings.NewReader(str)); err == nil {
|
||||
defer reader.Close()
|
||||
var decompressed []byte
|
||||
if decompressed, err = io.ReadAll(reader); err == nil {
|
||||
return string(decompressed), true
|
||||
}
|
||||
}
|
||||
return str, false
|
||||
}
|
||||
|
||||
func decodeDeflate(str string) (string, bool) {
|
||||
reader := flate.NewReader(strings.NewReader(str))
|
||||
defer reader.Close()
|
||||
if decompressed, err := io.ReadAll(reader); err == nil {
|
||||
return string(decompressed), true
|
||||
}
|
||||
return str, false
|
||||
}
|
||||
|
||||
func SaveAs(str, targetType string) (value string, err error) {
|
||||
switch targetType {
|
||||
case types.PLAIN_TEXT:
|
||||
return str, nil
|
||||
|
||||
case types.BASE64_TEXT:
|
||||
base64Str, _ := encodeBase64(str)
|
||||
return base64Str, nil
|
||||
|
||||
case types.JSON, types.BASE64_JSON, types.GZIP_JSON:
|
||||
if jsonStr, ok := encodeJson(str); ok {
|
||||
if targetType == types.BASE64_JSON {
|
||||
base64Str, _ := encodeBase64(jsonStr)
|
||||
return base64Str, nil
|
||||
} else {
|
||||
return jsonStr, nil
|
||||
}
|
||||
} else {
|
||||
return str, errors.New("invalid json")
|
||||
}
|
||||
|
||||
case types.GZIP:
|
||||
if gzipStr, ok := encodeGZip(str); ok {
|
||||
return gzipStr, nil
|
||||
} else {
|
||||
return str, errors.New("fail to build gzip data")
|
||||
}
|
||||
|
||||
case types.DEFLATE:
|
||||
if deflateStr, ok := encodeDeflate(str); ok {
|
||||
return deflateStr, nil
|
||||
} else {
|
||||
return str, errors.New("fail to build deflate data")
|
||||
}
|
||||
}
|
||||
return str, errors.New("fail to save with unknown error")
|
||||
}
|
||||
|
||||
func encodeJson(str string) (string, bool) {
|
||||
var data any
|
||||
if (strings.HasPrefix(str, "{") && strings.HasSuffix(str, "}")) ||
|
||||
(strings.HasPrefix(str, "[") && strings.HasSuffix(str, "]")) {
|
||||
if err := json.Unmarshal([]byte(str), &data); err == nil {
|
||||
var jsonByte []byte
|
||||
if jsonByte, err = json.Marshal(data); err == nil {
|
||||
return string(jsonByte), true
|
||||
}
|
||||
}
|
||||
}
|
||||
return str, false
|
||||
}
|
||||
|
||||
func encodeBase64(str string) (string, bool) {
|
||||
return base64.StdEncoding.EncodeToString([]byte(str)), true
|
||||
}
|
||||
|
||||
func encodeGZip(str string) (string, bool) {
|
||||
var compress = func(b []byte) (string, error) {
|
||||
var buf bytes.Buffer
|
||||
writer := gzip.NewWriter(&buf)
|
||||
if _, err := writer.Write([]byte(str)); err != nil {
|
||||
writer.Close()
|
||||
return "", err
|
||||
}
|
||||
writer.Close()
|
||||
return string(buf.Bytes()), nil
|
||||
}
|
||||
|
||||
if gzipStr, err := compress([]byte(str)); err == nil {
|
||||
return gzipStr, true
|
||||
}
|
||||
return str, false
|
||||
}
|
||||
|
||||
func encodeDeflate(str string) (string, bool) {
|
||||
var compress = func(b []byte) (string, error) {
|
||||
var buf bytes.Buffer
|
||||
writer, err := flate.NewWriter(&buf, flate.DefaultCompression)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if _, err = writer.Write([]byte(str)); err != nil {
|
||||
writer.Close()
|
||||
return "", err
|
||||
}
|
||||
writer.Close()
|
||||
return string(buf.Bytes()), nil
|
||||
}
|
||||
if deflateStr, err := compress([]byte(str)); err == nil {
|
||||
return deflateStr, true
|
||||
}
|
||||
return str, false
|
||||
}
|
|
@ -98,6 +98,7 @@ const tabContent = computed(() => {
|
|||
ttl: tab.ttl,
|
||||
value: tab.value,
|
||||
size: tab.size || 0,
|
||||
viewAs: tab.viewAs,
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -122,7 +123,7 @@ const onReloadKey = async () => {
|
|||
if (tab == null || isEmpty(tab.key)) {
|
||||
return null
|
||||
}
|
||||
await connectionStore.loadKeyValue(tab.name, tab.db, tab.key)
|
||||
await connectionStore.loadKeyValue(tab.name, tab.db, tab.key, tab.viewAs)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -153,7 +154,8 @@ const onReloadKey = async () => {
|
|||
:name="tabContent.name"
|
||||
:ttl="tabContent.ttl"
|
||||
:value="tabContent.value"
|
||||
:size="tabContent.size" />
|
||||
:size="tabContent.size"
|
||||
:view-as="tabContent.viewAs" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -8,12 +8,10 @@ import { useThemeVars } from 'naive-ui'
|
|||
import { types } from '@/consts/value_view_type.js'
|
||||
import Close from '@/components/icons/Close.vue'
|
||||
import Edit from '@/components/icons/Edit.vue'
|
||||
import { IsJson } from '@/utils/check_string_format.js'
|
||||
import { types as redisTypes } from '@/consts/support_redis_type.js'
|
||||
import { ClipboardSetText } from 'wailsjs/runtime/runtime.js'
|
||||
import { map, toLower } from 'lodash'
|
||||
import useConnectionStore from 'stores/connections.js'
|
||||
import { fromBase64, fromBase64Json, toBinary, toHex, toJsonText } from '@/utils/string_convert.js'
|
||||
|
||||
const i18n = useI18n()
|
||||
const themeVars = useThemeVars()
|
||||
|
@ -28,6 +26,10 @@ const props = defineProps({
|
|||
},
|
||||
value: String,
|
||||
size: Number,
|
||||
viewAs: {
|
||||
type: String,
|
||||
default: types.PLAIN_TEXT,
|
||||
},
|
||||
})
|
||||
|
||||
const viewOption = computed(() =>
|
||||
|
@ -38,15 +40,15 @@ const viewOption = computed(() =>
|
|||
}
|
||||
}),
|
||||
)
|
||||
const viewAs = ref(types.PLAIN_TEXT)
|
||||
// const viewAs = ref(types.PLAIN_TEXT)
|
||||
|
||||
const autoDetectFormat = () => {
|
||||
// auto check format when loaded
|
||||
if (IsJson(props.value)) {
|
||||
viewAs.value = types.JSON
|
||||
} else {
|
||||
viewAs.value = types.PLAIN_TEXT
|
||||
}
|
||||
// if (IsJson(props.value)) {
|
||||
// viewAs.value = types.JSON
|
||||
// } else {
|
||||
// viewAs.value = types.PLAIN_TEXT
|
||||
// }
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
@ -60,44 +62,25 @@ watch(
|
|||
)
|
||||
|
||||
const keyType = redisTypes.STRING
|
||||
/**
|
||||
* view value
|
||||
* @type {ComputedRef<string>}
|
||||
*/
|
||||
const viewValue = computed(() => {
|
||||
switch (viewAs.value) {
|
||||
case types.PLAIN_TEXT:
|
||||
return props.value
|
||||
case types.JSON:
|
||||
return toJsonText(props.value)
|
||||
case types.BASE64_TO_TEXT:
|
||||
return fromBase64(props.value)
|
||||
case types.BASE64_TO_JSON:
|
||||
return fromBase64Json(props.value)
|
||||
case types.HEX:
|
||||
return toHex(props.value)
|
||||
case types.BINARY:
|
||||
return toBinary(props.value)
|
||||
default:
|
||||
return props.value
|
||||
}
|
||||
})
|
||||
|
||||
const viewLanguage = computed(() => {
|
||||
switch (viewAs.value) {
|
||||
switch (props.viewAs) {
|
||||
case types.JSON:
|
||||
case types.BASE64_TO_JSON:
|
||||
case types.BASE64_JSON:
|
||||
return 'json'
|
||||
default:
|
||||
return 'plaintext'
|
||||
}
|
||||
})
|
||||
|
||||
const onViewTypeUpdate = (viewType) => {
|
||||
connectionStore.loadKeyValue(props.name, props.db, props.keyPath, viewType)
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy value
|
||||
*/
|
||||
const onCopyValue = () => {
|
||||
ClipboardSetText(viewValue.value)
|
||||
ClipboardSetText(props.value)
|
||||
.then((succ) => {
|
||||
if (succ) {
|
||||
$message.success(i18n.t('dialogue.copy_succ'))
|
||||
|
@ -111,7 +94,7 @@ const onCopyValue = () => {
|
|||
const editValue = ref('')
|
||||
const inEdit = ref(false)
|
||||
const onEditValue = () => {
|
||||
editValue.value = viewValue.value
|
||||
editValue.value = props.value
|
||||
inEdit.value = true
|
||||
}
|
||||
|
||||
|
@ -134,6 +117,7 @@ const onSaveValue = async () => {
|
|||
toLower(keyType),
|
||||
editValue.value,
|
||||
-1,
|
||||
props.viewAs,
|
||||
)
|
||||
if (success) {
|
||||
await connectionStore.loadKeyValue(props.name, props.db, props.keyPath)
|
||||
|
@ -155,7 +139,11 @@ const onSaveValue = async () => {
|
|||
<content-toolbar :db="props.db" :key-path="keyPath" :key-type="keyType" :server="props.name" :ttl="ttl" />
|
||||
<div class="tb2 flex-box-h">
|
||||
<n-text>{{ $t('interface.view_as') }}</n-text>
|
||||
<n-select v-model:value="viewAs" :options="viewOption" style="width: 200px" />
|
||||
<n-select
|
||||
:value="props.viewAs"
|
||||
:options="viewOption"
|
||||
style="width: 200px"
|
||||
@update:value="onViewTypeUpdate" />
|
||||
<div class="flex-item-expand"></div>
|
||||
<n-button-group v-if="!inEdit">
|
||||
<n-button :focusable="false" @click="onCopyValue">
|
||||
|
@ -188,7 +176,7 @@ const onSaveValue = async () => {
|
|||
</div>
|
||||
<div class="value-wrapper flex-item-expand flex-box-v">
|
||||
<n-scrollbar v-if="!inEdit" class="flex-item-expand">
|
||||
<n-code :code="viewValue" :language="viewLanguage" show-line-numbers style="cursor: text" word-wrap />
|
||||
<n-code :code="props.value" :language="viewLanguage" show-line-numbers style="cursor: text" word-wrap />
|
||||
</n-scrollbar>
|
||||
<n-input
|
||||
v-else
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<script setup>
|
||||
import { computed, h, reactive, ref, watch } from 'vue'
|
||||
import { types, typesColor } from '@/consts/support_redis_type.js'
|
||||
import { types as viewTypes } from '@/consts/value_view_type.js'
|
||||
import useDialog from 'stores/dialog'
|
||||
import { isEmpty, keys, map } from 'lodash'
|
||||
import NewStringValue from '@/components/new_value/NewStringValue.vue'
|
||||
|
@ -116,7 +117,15 @@ const onAdd = async () => {
|
|||
if (value == null) {
|
||||
value = defaultValue[type]
|
||||
}
|
||||
const { success, msg, nodeKey } = await connectionStore.setKey(server, db, key, type, value, ttl)
|
||||
const { success, msg, nodeKey } = await connectionStore.setKey(
|
||||
server,
|
||||
db,
|
||||
key,
|
||||
type,
|
||||
value,
|
||||
ttl,
|
||||
viewTypes.PLAIN_TEXT,
|
||||
)
|
||||
if (success) {
|
||||
// select current key
|
||||
tabStore.setSelectedKeys(server, nodeKey)
|
||||
|
|
|
@ -5,8 +5,11 @@
|
|||
export const types = {
|
||||
PLAIN_TEXT: 'Plain Text',
|
||||
JSON: 'JSON',
|
||||
BASE64_TO_TEXT: 'Base64 To Text',
|
||||
BASE64_TO_JSON: 'Base64 To JSON',
|
||||
BASE64_TEXT: 'Base64 Text',
|
||||
BASE64_JSON: 'Base64 JSON',
|
||||
HEX: 'Hex',
|
||||
BINARY: 'Binary',
|
||||
GZIP: 'GZip',
|
||||
GZIP_JSON: 'GZip JSON',
|
||||
DEFLATE: 'Deflate',
|
||||
}
|
||||
|
|
|
@ -525,14 +525,15 @@ const useConnectionStore = defineStore('connections', {
|
|||
* @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} [viewType]
|
||||
*/
|
||||
async loadKeyValue(server, db, key) {
|
||||
async loadKeyValue(server, db, key, viewType) {
|
||||
try {
|
||||
const tab = useTabStore()
|
||||
if (!isEmpty(key)) {
|
||||
const { data, success, msg } = await GetKeyValue(server, db, key)
|
||||
const { data, success, msg } = await GetKeyValue(server, db, key, viewType)
|
||||
if (success) {
|
||||
const { type, ttl, value, size } = data
|
||||
const { type, ttl, value, size, viewAs } = data
|
||||
tab.upsertTab({
|
||||
server,
|
||||
db,
|
||||
|
@ -541,11 +542,16 @@ const useConnectionStore = defineStore('connections', {
|
|||
key,
|
||||
value,
|
||||
size,
|
||||
viewAs,
|
||||
})
|
||||
return
|
||||
} else {
|
||||
if (!isEmpty(msg)) {
|
||||
$message.error('load key fail: ' + msg)
|
||||
}
|
||||
// its danger to delete "non-exists" key, just remove from tree view
|
||||
await this.deleteKey(server, db, key, true)
|
||||
// TODO: show key not found page?
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -852,11 +858,12 @@ const useConnectionStore = defineStore('connections', {
|
|||
* @param {string} keyType
|
||||
* @param {any} value
|
||||
* @param {number} ttl
|
||||
* @param {string} [viewAs]
|
||||
* @returns {Promise<{[msg]: string, success: boolean, [nodeKey]: {string}}>}
|
||||
*/
|
||||
async setKey(connName, db, key, keyType, value, ttl) {
|
||||
async setKey(connName, db, key, keyType, value, ttl, viewAs) {
|
||||
try {
|
||||
const { data, success, msg } = await SetKeyValue(connName, db, key, keyType, value, ttl)
|
||||
const { data, success, msg } = await SetKeyValue(connName, db, key, keyType, value, ttl, viewAs)
|
||||
if (success) {
|
||||
// update tree view data
|
||||
const { newKey = 0 } = this._addKeyNodes(connName, db, [key], true)
|
||||
|
|
|
@ -38,7 +38,7 @@ const usePreferencesStore = defineStore('preferences', {
|
|||
},
|
||||
general: {
|
||||
theme: 'auto',
|
||||
language: 'en',
|
||||
language: 'auto',
|
||||
font: '',
|
||||
fontSize: 14,
|
||||
useSysProxy: false,
|
||||
|
|
|
@ -85,8 +85,9 @@ const useTabStore = defineStore('tab', {
|
|||
* @param {string} [key]
|
||||
* @param {number} [size]
|
||||
* @param {*} [value]
|
||||
* @param {string} [viewAs]
|
||||
*/
|
||||
upsertTab({ server, db, type, ttl, key, size, value }) {
|
||||
upsertTab({ server, db, type, ttl, key, size, value, viewAs }) {
|
||||
let tabIndex = findIndex(this.tabList, { name: server })
|
||||
if (tabIndex === -1) {
|
||||
this.tabList.push({
|
||||
|
@ -98,6 +99,7 @@ const useTabStore = defineStore('tab', {
|
|||
key,
|
||||
size,
|
||||
value,
|
||||
viewAs,
|
||||
})
|
||||
tabIndex = this.tabList.length - 1
|
||||
}
|
||||
|
@ -112,6 +114,7 @@ const useTabStore = defineStore('tab', {
|
|||
tab.key = key
|
||||
tab.size = size
|
||||
tab.value = value
|
||||
tab.viewAs = viewAs
|
||||
this._setActivatedIndex(tabIndex, true)
|
||||
// this.activatedTab = tab.name
|
||||
},
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
import { endsWith, isEmpty, size, startsWith } from 'lodash'
|
||||
|
||||
export const IsRedisKey = (str, separator) => {
|
||||
if (isEmpty(separator)) {
|
||||
separator = ':'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* check string is json
|
||||
* @param str
|
||||
* @returns {boolean}
|
||||
* @constructor
|
||||
*/
|
||||
export const IsJson = (str) => {
|
||||
if (size(str) >= 2) {
|
||||
if (startsWith(str, '{') && endsWith(str, '}')) {
|
||||
return true
|
||||
}
|
||||
if (startsWith(str, '[') && endsWith(str, ']')) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
import { map, padStart } from 'lodash'
|
||||
|
||||
/**
|
||||
* convert string to json
|
||||
* @param str
|
||||
* @return {string}
|
||||
*/
|
||||
export const toJsonText = (str) => {
|
||||
try {
|
||||
const jsonObj = JSON.parse(str)
|
||||
return JSON.stringify(jsonObj, null, 2)
|
||||
} catch (e) {
|
||||
return str
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* convert string from base64
|
||||
* @param str
|
||||
* @return {string}
|
||||
*/
|
||||
export const fromBase64 = (str) => {
|
||||
try {
|
||||
return atob(str)
|
||||
} catch (e) {
|
||||
return str
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* convert string from base64 to json
|
||||
* @param str
|
||||
* @return {string}
|
||||
*/
|
||||
export const fromBase64Json = (str) => {
|
||||
try {
|
||||
const text = atob(str)
|
||||
const jsonObj = JSON.parse(text)
|
||||
return JSON.stringify(jsonObj, null, 2)
|
||||
} catch (e) {
|
||||
return str
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* convert string to hex string
|
||||
* @param str
|
||||
* @return {string}
|
||||
*/
|
||||
export const toHex = (str) => {
|
||||
const hexArr = map(str, (char) => {
|
||||
const charCode = char.charCodeAt(0)
|
||||
return charCode.toString(16)
|
||||
})
|
||||
return hexArr.join(' ')
|
||||
}
|
||||
|
||||
/**
|
||||
* convert string to binary string
|
||||
* @param str
|
||||
* @return {string}
|
||||
*/
|
||||
export const toBinary = (str) => {
|
||||
const codeUnits = map(str, (char) => {
|
||||
let code = char.charCodeAt(0).toString(2)
|
||||
code = padStart(code, 8, '0')
|
||||
return code
|
||||
})
|
||||
return codeUnits.join(' ')
|
||||
}
|
10
main.go
10
main.go
|
@ -57,6 +57,16 @@ func main() {
|
|||
app.startup(ctx)
|
||||
connSvc.Start(ctx)
|
||||
},
|
||||
//OnBeforeClose: func(ctx context.Context) (prevent bool) {
|
||||
// // save current window size
|
||||
// width, height := runtime2.WindowGetSize(ctx)
|
||||
// if width > 0 && height > 0 {
|
||||
// if w, h := prefSvc.GetWindowSize(); w != width || h != height {
|
||||
// prefSvc.SaveWindowSize(width, height)
|
||||
// }
|
||||
// }
|
||||
// return false
|
||||
//},
|
||||
OnShutdown: func(ctx context.Context) {
|
||||
// save current window size
|
||||
width, height := runtime2.WindowGetSize(ctx)
|
||||
|
|
Loading…
Reference in New Issue