Compare commits

...

4 Commits

21 changed files with 679 additions and 530 deletions

View File

@ -21,6 +21,7 @@ import (
"tinyrdm/backend/consts"
"tinyrdm/backend/types"
"tinyrdm/backend/utils/coll"
convutil "tinyrdm/backend/utils/convert"
redis2 "tinyrdm/backend/utils/redis"
sliceutil "tinyrdm/backend/utils/slice"
strutil "tinyrdm/backend/utils/string"
@ -781,7 +782,7 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS
Value: val,
})
if doConvert {
if dv, _, _ := strutil.ConvertTo(val, param.Decode, param.Format); dv != val {
if dv, _, _ := convutil.ConvertTo(val, param.Decode, param.Format); dv != val {
items[len(items)-1].DisplayValue = dv
}
}
@ -828,7 +829,7 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS
Value: strutil.EncodeRedisKey(loadedVal[i+1]),
})
if doConvert {
if dv, _, _ := strutil.ConvertTo(loadedVal[i+1], param.Decode, param.Format); dv != loadedVal[i+1] {
if dv, _, _ := convutil.ConvertTo(loadedVal[i+1], param.Decode, param.Format); dv != loadedVal[i+1] {
items[len(items)-1].DisplayValue = dv
}
}
@ -853,7 +854,7 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS
items[i/2].Key = loadedVal[i]
items[i/2].Value = strutil.EncodeRedisKey(loadedVal[i+1])
if doConvert {
if dv, _, _ := strutil.ConvertTo(loadedVal[i+1], param.Decode, param.Format); dv != loadedVal[i+1] {
if dv, _, _ := convutil.ConvertTo(loadedVal[i+1], param.Decode, param.Format); dv != loadedVal[i+1] {
items[i/2].DisplayValue = dv
}
}
@ -898,7 +899,7 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS
Value: val,
})
if doConvert {
if dv, _, _ := strutil.ConvertTo(val, param.Decode, param.Format); dv != val {
if dv, _, _ := convutil.ConvertTo(val, param.Decode, param.Format); dv != val {
items[len(items)-1].DisplayValue = dv
}
}
@ -918,7 +919,7 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS
for i, val := range loadedKey {
items[i].Value = val
if doConvert {
if dv, _, _ := strutil.ConvertTo(val, param.Decode, param.Format); dv != val {
if dv, _, _ := convutil.ConvertTo(val, param.Decode, param.Format); dv != val {
items[i].DisplayValue = dv
}
}
@ -966,7 +967,7 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS
Score: score,
})
if doConvert {
if dv, _, _ := strutil.ConvertTo(loadedVal[i], param.Decode, param.Format); dv != loadedVal[i] {
if dv, _, _ := convutil.ConvertTo(loadedVal[i], param.Decode, param.Format); dv != loadedVal[i] {
items[len(items)-1].DisplayValue = dv
}
}
@ -1000,7 +1001,7 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS
Value: val,
})
if doConvert {
if dv, _, _ := strutil.ConvertTo(val, param.Decode, param.Format); dv != val {
if dv, _, _ := convutil.ConvertTo(val, param.Decode, param.Format); dv != val {
items[len(items)-1].DisplayValue = dv
}
}
@ -1061,7 +1062,7 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS
if vb, merr := json.Marshal(msg.Values); merr != nil {
it.DisplayValue = "{}"
} else {
it.DisplayValue, _, _ = strutil.ConvertTo(string(vb), types.DECODE_NONE, types.FORMAT_JSON)
it.DisplayValue, _, _ = convutil.ConvertTo(string(vb), types.DECODE_NONE, types.FORMAT_JSON)
}
if doFilter && !strings.Contains(it.DisplayValue, param.MatchPattern) {
continue
@ -1095,7 +1096,7 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS
// blank format indicate auto format
func (b *browserService) ConvertValue(value any, decode, format string) (resp types.JSResp) {
str := strutil.DecodeRedisKey(value)
value, decode, format = strutil.ConvertTo(str, decode, format)
value, decode, format = convutil.ConvertTo(str, decode, format)
resp.Success = true
resp.Data = map[string]any{
"value": value,
@ -1138,7 +1139,7 @@ func (b *browserService) SetKeyValue(param types.SetKeyParam) (resp types.JSResp
return
} else {
var saveStr string
if saveStr, err = strutil.SaveAs(str, param.Format, param.Decode); err != nil {
if saveStr, err = convutil.SaveAs(str, param.Format, param.Decode); err != nil {
resp.Msg = fmt.Sprintf(`save to type "%s" fail: %s`, param.Format, err.Error())
return
}
@ -1249,12 +1250,12 @@ func (b *browserService) SetHashValue(param types.SetHashParam) (resp types.JSRe
key := strutil.DecodeRedisKey(param.Key)
str := strutil.DecodeRedisKey(param.Value)
var saveStr, displayStr string
if saveStr, err = strutil.SaveAs(str, param.Format, param.Decode); err != nil {
if saveStr, err = convutil.SaveAs(str, param.Format, param.Decode); err != nil {
resp.Msg = fmt.Sprintf(`save to type "%s" fail: %s`, param.Format, err.Error())
return
}
if len(param.RetDecode) > 0 && len(param.RetFormat) > 0 {
displayStr, _, _ = strutil.ConvertTo(saveStr, param.RetDecode, param.RetFormat)
displayStr, _, _ = convutil.ConvertTo(saveStr, param.RetDecode, param.RetFormat)
}
var updated, added, removed []types.HashEntryItem
var replaced []types.HashReplaceItem
@ -1472,7 +1473,7 @@ func (b *browserService) SetListItem(param types.SetListParam) (resp types.JSRes
} else {
// replace index value
var saveStr string
if saveStr, err = strutil.SaveAs(str, param.Format, param.Decode); err != nil {
if saveStr, err = convutil.SaveAs(str, param.Format, param.Decode); err != nil {
resp.Msg = fmt.Sprintf(`save to type "%s" fail: %s`, param.Format, err.Error())
return
}
@ -1483,7 +1484,7 @@ func (b *browserService) SetListItem(param types.SetListParam) (resp types.JSRes
}
var displayStr string
if len(param.RetDecode) > 0 && len(param.RetFormat) > 0 {
displayStr, _, _ = strutil.ConvertTo(saveStr, param.RetDecode, param.RetFormat)
displayStr, _, _ = convutil.ConvertTo(saveStr, param.RetDecode, param.RetFormat)
}
replaced = append(replaced, types.ListReplaceItem{
Index: param.Index,
@ -1574,7 +1575,7 @@ func (b *browserService) UpdateSetItem(param types.SetSetParam) (resp types.JSRe
// insert new value
str = strutil.DecodeRedisKey(param.NewValue)
var saveStr string
if saveStr, err = strutil.SaveAs(str, param.Format, param.Decode); err != nil {
if saveStr, err = convutil.SaveAs(str, param.Format, param.Decode); err != nil {
resp.Msg = fmt.Sprintf(`save to type "%s" fail: %s`, param.Format, err.Error())
return
}
@ -1582,7 +1583,7 @@ func (b *browserService) UpdateSetItem(param types.SetSetParam) (resp types.JSRe
// add new item
var displayStr string
if len(param.RetDecode) > 0 && len(param.RetFormat) > 0 {
displayStr, _, _ = strutil.ConvertTo(saveStr, param.RetDecode, param.RetFormat)
displayStr, _, _ = convutil.ConvertTo(saveStr, param.RetDecode, param.RetFormat)
}
added = append(added, types.SetEntryItem{
Value: saveStr,
@ -1629,7 +1630,7 @@ func (b *browserService) UpdateZSetValue(param types.SetZSetParam) (resp types.J
}
} else {
var saveVal string
if saveVal, err = strutil.SaveAs(newVal, param.Format, param.Decode); err != nil {
if saveVal, err = convutil.SaveAs(newVal, param.Format, param.Decode); err != nil {
resp.Msg = fmt.Sprintf(`save to type "%s" fail: %s`, param.Format, err.Error())
return
}
@ -1639,7 +1640,7 @@ func (b *browserService) UpdateZSetValue(param types.SetZSetParam) (resp types.J
Score: param.Score,
Member: saveVal,
}).Result()
displayValue, _, _ := strutil.ConvertTo(val, param.RetDecode, param.RetFormat)
displayValue, _, _ := convutil.ConvertTo(val, param.RetDecode, param.RetFormat)
if affect > 0 {
// add new item
added = append(added, types.ZSetEntryItem{
@ -1667,7 +1668,7 @@ func (b *browserService) UpdateZSetValue(param types.SetZSetParam) (resp types.J
Score: param.Score,
Member: saveVal,
}).Result()
displayValue, _, _ := strutil.ConvertTo(saveVal, param.RetDecode, param.RetFormat)
displayValue, _, _ := convutil.ConvertTo(saveVal, param.RetDecode, param.RetFormat)
if affect <= 0 {
// no new value added, just update exists item
removed = append(removed, types.ZSetEntryItem{
@ -1793,7 +1794,7 @@ func (b *browserService) AddStreamValue(server string, db int, k any, ID string,
updateValues[fieldItems[i].(string)] = fieldItems[i+1]
}
vb, _ := json.Marshal(updateValues)
displayValue, _, _ := strutil.ConvertTo(string(vb), types.DECODE_NONE, types.FORMAT_JSON)
displayValue, _, _ := convutil.ConvertTo(string(vb), types.DECODE_NONE, types.FORMAT_JSON)
resp.Success = true
resp.Data = struct {

View File

@ -0,0 +1,21 @@
package convutil
import (
"encoding/base64"
strutil "tinyrdm/backend/utils/string"
)
type Base64Convert struct{}
func (Base64Convert) Encode(str string) (string, bool) {
return base64.StdEncoding.EncodeToString([]byte(str)), true
}
func (Base64Convert) Decode(str string) (string, bool) {
if decodedStr, err := base64.StdEncoding.DecodeString(str); err == nil {
if s := string(decodedStr); !strutil.ContainsBinary(s) {
return s, true
}
}
return str, false
}

View File

@ -0,0 +1,27 @@
package convutil
import (
"fmt"
"strconv"
"strings"
)
type BinaryConvert struct{}
func (BinaryConvert) Encode(str string) (string, bool) {
var result strings.Builder
total := len(str)
for i := 0; i < total; i += 8 {
b, _ := strconv.ParseInt(str[i:i+8], 2, 8)
result.WriteByte(byte(b))
}
return result.String(), true
}
func (BinaryConvert) Decode(str string) (string, bool) {
var binary strings.Builder
for _, char := range str {
binary.WriteString(fmt.Sprintf("%08b", int(char)))
}
return binary.String(), true
}

View File

@ -0,0 +1,35 @@
package convutil
import (
"bytes"
"github.com/andybalholm/brotli"
"io"
"strings"
)
type BrotliConvert struct{}
func (BrotliConvert) Encode(str string) (string, bool) {
var compress = func(b []byte) (string, error) {
var buf bytes.Buffer
writer := brotli.NewWriter(&buf)
if _, err := writer.Write([]byte(str)); err != nil {
writer.Close()
return "", err
}
writer.Close()
return string(buf.Bytes()), nil
}
if brotliStr, err := compress([]byte(str)); err == nil {
return brotliStr, true
}
return str, false
}
func (BrotliConvert) Decode(str string) (string, bool) {
reader := brotli.NewReader(strings.NewReader(str))
if decompressed, err := io.ReadAll(reader); err == nil {
return string(decompressed), true
}
return str, false
}

View File

@ -0,0 +1,302 @@
package convutil
import (
"errors"
"regexp"
"tinyrdm/backend/types"
strutil "tinyrdm/backend/utils/string"
)
type DataConvert interface {
Encode(string) (string, bool)
Decode(string) (string, bool)
}
var (
jsonConv JsonConvert
yamlConv YamlConvert
xmlConv XmlConvert
base64Conv Base64Convert
binaryConv BinaryConvert
hexConv HexConvert
gzipConv GZipConvert
deflateConv DeflateConvert
zstdConv ZStdConvert
brotliConv BrotliConvert
msgpackConv MsgpackConvert
)
// ConvertTo convert string to specified type
// @param decodeType empty string indicates automatic detection
// @param formatType empty string indicates automatic detection
func ConvertTo(str, decodeType, formatType string) (value, resultDecode, resultFormat string) {
if len(str) <= 0 {
// empty content
if len(formatType) <= 0 {
resultFormat = types.FORMAT_RAW
} else {
resultFormat = formatType
}
if len(decodeType) <= 0 {
resultDecode = types.DECODE_NONE
} else {
resultDecode = decodeType
}
return
}
// decode first
value, resultDecode = decodeWith(str, decodeType)
// then format content
value, resultFormat = viewAs(value, formatType)
return
}
func decodeWith(str, decodeType string) (value, resultDecode string) {
if len(decodeType) > 0 {
switch decodeType {
case types.DECODE_NONE:
value = str
case types.DECODE_BASE64:
if base64Str, ok := base64Conv.Decode(str); ok {
value = base64Str
} else {
value = str
}
case types.DECODE_GZIP:
if gzipStr, ok := gzipConv.Decode(str); ok {
value = gzipStr
} else {
value = str
}
case types.DECODE_DEFLATE:
if falteStr, ok := deflateConv.Decode(str); ok {
value = falteStr
} else {
value = str
}
case types.DECODE_ZSTD:
if zstdStr, ok := zstdConv.Decode(str); ok {
value = zstdStr
} else {
value = str
}
case types.DECODE_BROTLI:
if brotliStr, ok := brotliConv.Decode(str); ok {
value = brotliStr
} else {
value = str
}
case types.DECODE_MSGPACK:
if msgpackStr, ok := msgpackConv.Decode(str); ok {
value = msgpackStr
} else {
value = str
}
}
resultDecode = decodeType
return
}
return autoDecode(str)
}
// attempt try possible decode method
// if no decode is possible, it will return the origin string value and "none" decode type
func autoDecode(str string) (value, resultDecode string) {
if len(str) > 0 {
// pure digit content may incorrect regard as some encoded type, skip decode
if match, _ := regexp.MatchString(`^\d+$`, str); !match {
var ok bool
if len(str)%4 == 0 && len(str) >= 12 && !strutil.IsSameChar(str) {
if value, ok = base64Conv.Decode(str); ok {
resultDecode = types.DECODE_BASE64
return
}
}
if value, ok = gzipConv.Decode(str); ok {
resultDecode = types.DECODE_GZIP
return
}
// FIXME: skip decompress with deflate due to incorrect format checking
//if value, ok = decodeDeflate(str); ok {
// resultDecode = types.DECODE_DEFLATE
// return
//}
if value, ok = zstdConv.Decode(str); ok {
resultDecode = types.DECODE_ZSTD
return
}
// FIXME: skip decompress with brotli due to incorrect format checking
//if value, ok = decodeBrotli(str); ok {
// resultDecode = types.DECODE_BROTLI
// return
//}
if value, ok = msgpackConv.Decode(str); ok {
resultDecode = types.DECODE_MSGPACK
return
}
}
}
value = str
resultDecode = types.DECODE_NONE
return
}
func viewAs(str, formatType string) (value, resultFormat string) {
if len(formatType) > 0 {
switch formatType {
case types.FORMAT_RAW, types.FORMAT_YAML, types.FORMAT_XML:
value = str
case types.FORMAT_JSON:
if jsonStr, ok := jsonConv.Decode(str); ok {
value = jsonStr
} else {
value = str
}
case types.FORMAT_HEX:
if hexStr, ok := hexConv.Decode(str); ok {
value = hexStr
} else {
value = str
}
case types.FORMAT_BINARY:
if binStr, ok := binaryConv.Decode(str); ok {
value = binStr
} else {
value = str
}
}
resultFormat = formatType
return
}
return autoViewAs(str)
}
// attempt automatic convert to possible types
// if no conversion is possible, it will return the origin string value and "plain text" type
func autoViewAs(str string) (value, resultFormat string) {
if len(str) > 0 {
var ok bool
if value, ok = jsonConv.Decode(str); ok {
resultFormat = types.FORMAT_JSON
return
}
if value, ok = yamlConv.Decode(str); ok {
resultFormat = types.FORMAT_YAML
return
}
if value, ok = xmlConv.Decode(str); ok {
resultFormat = types.FORMAT_XML
return
}
if strutil.ContainsBinary(str) {
if value, ok = hexConv.Decode(str); ok {
resultFormat = types.FORMAT_HEX
return
}
}
}
value = str
resultFormat = types.FORMAT_RAW
return
}
func SaveAs(str, format, decode string) (value string, err error) {
value = str
switch format {
case types.FORMAT_JSON:
if jsonStr, ok := jsonConv.Encode(str); ok {
value = jsonStr
} else {
err = errors.New("invalid json data")
return
}
case types.FORMAT_HEX:
if hexStr, ok := hexConv.Encode(str); ok {
value = hexStr
} else {
err = errors.New("invalid hex data")
return
}
case types.FORMAT_BINARY:
if binStr, ok := binaryConv.Encode(str); ok {
value = binStr
} else {
err = errors.New("invalid binary data")
return
}
}
switch decode {
case types.DECODE_NONE:
return
case types.DECODE_BASE64:
value, _ = base64Conv.Encode(value)
return
case types.DECODE_GZIP:
if gzipStr, ok := gzipConv.Encode(str); ok {
value = gzipStr
} else {
err = errors.New("fail to build gzip")
}
return
case types.DECODE_DEFLATE:
if deflateStr, ok := deflateConv.Encode(str); ok {
value = deflateStr
} else {
err = errors.New("fail to build deflate")
}
return
case types.DECODE_ZSTD:
if zstdStr, ok := zstdConv.Encode(str); ok {
value = zstdStr
} else {
err = errors.New("fail to build zstd")
}
return
case types.DECODE_BROTLI:
if brotliStr, ok := brotliConv.Encode(str); ok {
value = brotliStr
} else {
err = errors.New("fail to build brotli")
}
return
case types.DECODE_MSGPACK:
if msgpackStr, ok := msgpackConv.Encode(str); ok {
value = msgpackStr
} else {
err = errors.New("fail to build msgpack")
}
return
}
return str, nil
}

View File

@ -0,0 +1,39 @@
package convutil
import (
"bytes"
"github.com/klauspost/compress/flate"
"io"
"strings"
)
type DeflateConvert struct{}
func (d DeflateConvert) Encode(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
}
func (d DeflateConvert) Decode(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
}

View File

@ -0,0 +1,39 @@
package convutil
import (
"bytes"
"github.com/klauspost/compress/gzip"
"io"
"strings"
)
type GZipConvert struct{}
func (GZipConvert) Encode(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 (GZipConvert) Decode(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
}

View File

@ -0,0 +1,29 @@
package convutil
import (
"encoding/hex"
"strings"
)
type HexConvert struct{}
func (HexConvert) Encode(str string) (string, bool) {
hexStrArr := strings.Split(str, "\\x")
hexStr := strings.Join(hexStrArr, "")
if decodeStr, err := hex.DecodeString(hexStr); err == nil {
return string(decodeStr), true
}
return str, false
}
func (HexConvert) Decode(str string) (string, bool) {
decodeStr := hex.EncodeToString([]byte(str))
decodeStr = strings.ToUpper(decodeStr)
var resultStr strings.Builder
for i := 0; i < len(decodeStr); i += 2 {
resultStr.WriteString("\\x")
resultStr.WriteString(decodeStr[i : i+2])
}
return resultStr.String(), true
}

View File

@ -0,0 +1,29 @@
package convutil
import (
"bytes"
"encoding/json"
"strings"
)
type JsonConvert struct{}
func (JsonConvert) Decode(str string) (string, bool) {
trimedStr := strings.TrimSpace(str)
if (strings.HasPrefix(trimedStr, "{") && strings.HasSuffix(trimedStr, "}")) ||
(strings.HasPrefix(trimedStr, "[") && strings.HasSuffix(trimedStr, "]")) {
var out bytes.Buffer
if err := json.Indent(&out, []byte(trimedStr), "", " "); err == nil {
return out.String(), true
}
}
return str, false
}
func (JsonConvert) Encode(str string) (string, bool) {
var dst bytes.Buffer
if err := json.Compact(&dst, []byte(str)); err != nil {
return str, false
}
return dst.String(), true
}

View File

@ -0,0 +1,39 @@
package convutil
import (
"encoding/json"
"github.com/vmihailenco/msgpack/v5"
)
type MsgpackConvert struct{}
func (MsgpackConvert) Encode(str string) (string, bool) {
var obj map[string]any
if err := json.Unmarshal([]byte(str), &obj); err == nil {
if b, err := msgpack.Marshal(obj); err == nil {
return string(b), true
}
}
if b, err := msgpack.Marshal(str); err != nil {
return string(b), true
}
return str, false
}
func (MsgpackConvert) Decode(str string) (string, bool) {
var decodedStr string
if err := msgpack.Unmarshal([]byte(str), &decodedStr); err == nil {
return decodedStr, true
}
var obj map[string]any
if err := msgpack.Unmarshal([]byte(str), &obj); err == nil {
if b, err := json.Marshal(obj); err == nil {
return string(b), true
}
}
return str, false
}

View File

@ -0,0 +1,22 @@
package convutil
import (
"encoding/xml"
"strings"
)
type XmlConvert struct{}
func (XmlConvert) Encode(str string) (string, bool) {
return str, true
}
func (XmlConvert) Decode(str string) (string, bool) {
trimedStr := strings.TrimSpace(str)
if !strings.HasPrefix(trimedStr, "<") && !strings.HasSuffix(trimedStr, ">") {
return str, false
}
var obj any
err := xml.Unmarshal([]byte(trimedStr), &obj)
return str, err == nil
}

View File

@ -0,0 +1,23 @@
package convutil
import (
"gopkg.in/yaml.v3"
"log"
)
type YamlConvert struct{}
func (YamlConvert) Encode(str string) (string, bool) {
return str, true
}
func (YamlConvert) Decode(str string) (string, bool) {
var obj map[string]any
err := yaml.Unmarshal([]byte(str), &obj)
if err != nil {
log.Println(err.Error())
} else {
log.Println(obj)
}
return str, err == nil
}

View File

@ -0,0 +1,40 @@
package convutil
import (
"bytes"
"github.com/klauspost/compress/zstd"
"io"
"strings"
)
type ZStdConvert struct{}
func (ZStdConvert) Encode(str string) (string, bool) {
var compress = func(b []byte) (string, error) {
var buf bytes.Buffer
writer, err := zstd.NewWriter(&buf)
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 zstdStr, err := compress([]byte(str)); err == nil {
return zstdStr, true
}
return str, false
}
func (ZStdConvert) Decode(str string) (string, bool) {
if reader, err := zstd.NewReader(strings.NewReader(str)); err == nil {
defer reader.Close()
if decompressed, err := io.ReadAll(reader); err == nil {
return string(decompressed), true
}
}
return str, false
}

View File

@ -4,7 +4,7 @@ import (
"unicode"
)
func containsBinary(str string) bool {
func ContainsBinary(str string) bool {
//buf := []byte(str)
//size := 0
//for start := 0; start < len(buf); start += size {
@ -25,7 +25,7 @@ func containsBinary(str string) bool {
return false
}
func isSameChar(str string) bool {
func IsSameChar(str string) bool {
if len(str) <= 0 {
return false
}

View File

@ -1,505 +0,0 @@
package strutil
import (
"bytes"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"github.com/andybalholm/brotli"
"github.com/klauspost/compress/flate"
"github.com/klauspost/compress/gzip"
"github.com/klauspost/compress/zstd"
"github.com/vmihailenco/msgpack/v5"
"io"
"regexp"
"strconv"
"strings"
"tinyrdm/backend/types"
)
// ConvertTo convert string to specified type
// @param decodeType empty string indicates automatic detection
// @param formatType empty string indicates automatic detection
func ConvertTo(str, decodeType, formatType string) (value, resultDecode, resultFormat string) {
if len(str) <= 0 {
// empty content
if len(formatType) <= 0 {
resultFormat = types.FORMAT_RAW
} else {
resultFormat = formatType
}
if len(decodeType) <= 0 {
resultDecode = types.DECODE_NONE
} else {
resultDecode = decodeType
}
return
}
// decode first
value, resultDecode = decodeWith(str, decodeType)
// then format content
value, resultFormat = viewAs(value, formatType)
return
}
func decodeWith(str, decodeType string) (value, resultDecode string) {
if len(decodeType) > 0 {
switch decodeType {
case types.DECODE_NONE:
value = str
resultDecode = decodeType
return
case types.DECODE_BASE64:
if base64Str, ok := decodeBase64(str); ok {
value = base64Str
} else {
value = str
}
resultDecode = decodeType
return
case types.DECODE_GZIP:
if gzipStr, ok := decodeGZip(str); ok {
value = gzipStr
} else {
value = str
}
resultDecode = decodeType
return
case types.DECODE_DEFLATE:
if falteStr, ok := decodeDeflate(str); ok {
value = falteStr
} else {
value = str
}
resultDecode = decodeType
return
case types.DECODE_ZSTD:
if zstdStr, ok := decodeZStd(str); ok {
value = zstdStr
} else {
value = str
}
resultDecode = decodeType
return
case types.DECODE_BROTLI:
if brotliStr, ok := decodeBrotli(str); ok {
value = brotliStr
} else {
value = str
}
resultDecode = decodeType
return
}
}
return autoDecode(str)
}
// attempt try possible decode method
// if no decode is possible, it will return the origin string value and "none" decode type
func autoDecode(str string) (value, resultDecode string) {
if len(str) > 0 {
// pure digit content may incorrect regard as some encoded type, skip decode
if match, _ := regexp.MatchString(`^\d+$`, str); !match {
var ok bool
if len(str)%4 == 0 && len(str) >= 12 && !isSameChar(str) {
if value, ok = decodeBase64(str); ok {
resultDecode = types.DECODE_BASE64
return
}
}
if value, ok = decodeGZip(str); ok {
resultDecode = types.DECODE_GZIP
return
}
// FIXME: skip decompress with deflate due to incorrect format checking
//if value, ok = decodeDeflate(str); ok {
// resultDecode = types.DECODE_DEFLATE
// return
//}
if value, ok = decodeZStd(str); ok {
resultDecode = types.DECODE_ZSTD
return
}
// FIXME: skip decompress with brotli due to incorrect format checking
//if value, ok = decodeBrotli(str); ok {
// resultDecode = types.DECODE_BROTLI
// return
//}
if value, ok = decodeMsgpack(str); ok {
resultDecode = types.DECODE_MSGPACK
return
}
}
}
value = str
resultDecode = types.DECODE_NONE
return
}
func viewAs(str, formatType string) (value, resultFormat string) {
if len(formatType) > 0 {
switch formatType {
case types.FORMAT_RAW, types.FORMAT_YAML, types.FORMAT_XML:
value = str
resultFormat = formatType
return
case types.FORMAT_JSON:
if jsonStr, ok := decodeJson(str); ok {
value = jsonStr
} else {
value = str
}
resultFormat = formatType
return
case types.FORMAT_HEX:
if hexStr, ok := decodeToHex(str); ok {
value = hexStr
} else {
value = str
}
resultFormat = formatType
return
case types.FORMAT_BINARY:
if binStr, ok := decodeBinary(str); ok {
value = binStr
} else {
value = str
}
resultFormat = formatType
return
}
}
return autoViewAs(str)
}
// attempt automatic convert to possible types
// if no conversion is possible, it will return the origin string value and "plain text" type
func autoViewAs(str string) (value, resultFormat string) {
if len(str) > 0 {
var ok bool
if value, ok = decodeJson(str); ok {
resultFormat = types.FORMAT_JSON
return
}
if containsBinary(str) {
if value, ok = decodeToHex(str); ok {
resultFormat = types.FORMAT_HEX
return
}
}
}
value = str
resultFormat = types.FORMAT_RAW
return
}
func decodeJson(str string) (string, bool) {
trimedStr := strings.TrimSpace(str)
if (strings.HasPrefix(trimedStr, "{") && strings.HasSuffix(trimedStr, "}")) ||
(strings.HasPrefix(trimedStr, "[") && strings.HasSuffix(trimedStr, "]")) {
var out bytes.Buffer
if err := json.Indent(&out, []byte(trimedStr), "", " "); err == nil {
return out.String(), true
}
}
return str, false
}
func decodeBase64(str string) (string, bool) {
if decodedStr, err := base64.StdEncoding.DecodeString(str); err == nil {
if s := string(decodedStr); !containsBinary(s) {
return s, true
}
}
return str, false
}
func decodeBinary(str string) (string, bool) {
var binary strings.Builder
for _, char := range str {
binary.WriteString(fmt.Sprintf("%08b", int(char)))
}
return binary.String(), true
}
func decodeToHex(str string) (string, bool) {
decodeStr := hex.EncodeToString([]byte(str))
var resultStr strings.Builder
for i := 0; i < len(decodeStr); i += 2 {
resultStr.WriteString("\\x")
resultStr.WriteString(strings.ToUpper(decodeStr[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 decodeZStd(str string) (string, bool) {
if reader, err := zstd.NewReader(strings.NewReader(str)); err == nil {
defer reader.Close()
if decompressed, err := io.ReadAll(reader); err == nil {
return string(decompressed), true
}
}
return str, false
}
func decodeBrotli(str string) (string, bool) {
reader := brotli.NewReader(strings.NewReader(str))
if decompressed, err := io.ReadAll(reader); err == nil {
return string(decompressed), true
}
return str, false
}
func decodeMsgpack(str string) (string, bool) {
var decodedStr string
if err := msgpack.Unmarshal([]byte(str), &decodedStr); err == nil {
return decodedStr, true
}
var decodedObj map[string]any
if err := msgpack.Unmarshal([]byte(str), &decodedObj); err == nil {
if b, err := json.Marshal(decodedObj); err == nil {
return string(b), true
}
}
return str, false
}
func SaveAs(str, format, decode string) (value string, err error) {
value = str
switch format {
case types.FORMAT_JSON:
if jsonStr, ok := encodeJson(str); ok {
value = jsonStr
} else {
err = errors.New("invalid json data")
return
}
case types.FORMAT_HEX:
if hexStr, ok := encodeHex(str); ok {
value = hexStr
} else {
err = errors.New("invalid hex data")
return
}
case types.FORMAT_BINARY:
if binStr, ok := encodeBinary(str); ok {
value = binStr
} else {
err = errors.New("invalid binary data")
return
}
}
switch decode {
case types.DECODE_NONE:
return
case types.DECODE_BASE64:
value, _ = encodeBase64(value)
return
case types.DECODE_GZIP:
if gzipStr, ok := encodeGZip(str); ok {
value = gzipStr
} else {
err = errors.New("fail to build gzip")
}
return
case types.DECODE_DEFLATE:
if deflateStr, ok := encodeDeflate(str); ok {
value = deflateStr
} else {
err = errors.New("fail to build deflate")
}
return
case types.DECODE_ZSTD:
if zstdStr, ok := encodeZStd(str); ok {
value = zstdStr
} else {
err = errors.New("fail to build zstd")
}
return
case types.DECODE_BROTLI:
if brotliStr, ok := encodeBrotli(str); ok {
value = brotliStr
} else {
err = errors.New("fail to build brotli")
}
return
case types.DECODE_MSGPACK:
if msgpackStr, ok := encodeMsgpack(str); ok {
value = msgpackStr
} else {
err = errors.New("fail to build msgpack")
}
return
}
return str, nil
}
func encodeJson(str string) (string, bool) {
var dst bytes.Buffer
if err := json.Compact(&dst, []byte(str)); err != nil {
return str, false
}
return dst.String(), true
}
func encodeBase64(str string) (string, bool) {
return base64.StdEncoding.EncodeToString([]byte(str)), true
}
func encodeBinary(str string) (string, bool) {
var result strings.Builder
total := len(str)
for i := 0; i < total; i += 8 {
b, _ := strconv.ParseInt(str[i:i+8], 2, 8)
result.WriteByte(byte(b))
}
return result.String(), true
}
func encodeHex(str string) (string, bool) {
hexStrArr := strings.Split(str, "\\x")
hexStr := strings.Join(hexStrArr, "")
if decodeStr, err := hex.DecodeString(hexStr); err == nil {
return string(decodeStr), true
}
return str, false
}
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
}
func encodeZStd(str string) (string, bool) {
var compress = func(b []byte) (string, error) {
var buf bytes.Buffer
writer, err := zstd.NewWriter(&buf)
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 zstdStr, err := compress([]byte(str)); err == nil {
return zstdStr, true
}
return str, false
}
func encodeBrotli(str string) (string, bool) {
var compress = func(b []byte) (string, error) {
var buf bytes.Buffer
writer := brotli.NewWriter(&buf)
if _, err := writer.Write([]byte(str)); err != nil {
writer.Close()
return "", err
}
writer.Close()
return string(buf.Bytes()), nil
}
if brotliStr, err := compress([]byte(str)); err == nil {
return brotliStr, true
}
return str, false
}
func encodeMsgpack(str string) (string, bool) {
var err error
var buf bytes.Buffer
enc := msgpack.NewEncoder(&buf)
if err = enc.EncodeString(str); err == nil {
return buf.String(), true
}
return str, false
}

View File

@ -8,7 +8,7 @@ import (
// 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) {
if ContainsBinary(key) {
b := []byte(key)
arr := make([]int, len(b))
for i, bb := range b {

View File

@ -498,6 +498,7 @@ const pasteFromClipboard = async () => {
</n-form>
</n-tab-pane>
<!-- Alias pane -->
<n-tab-pane :tab="$t('dialogue.connection.alias.title')" display-directive="show:lazy" name="alias">
<n-form
:model="generalForm.alias"

View File

@ -108,6 +108,7 @@ const onClose = () => {
<n-select
v-model:value="prefStore.general.fontFamily"
:options="prefStore.fontOption"
:placeholder="$t('preferences.general.font_tip')"
:render-label="({ label, value }) => (value === '' ? $t(label) : label)"
filterable
multiple
@ -166,6 +167,7 @@ const onClose = () => {
<n-select
v-model:value="prefStore.editor.fontFamily"
:options="prefStore.fontOption"
:placeholder="$t('preferences.general.font_tip')"
:render-label="({ label, value }) => value || $t(label)"
filterable
multiple
@ -206,6 +208,7 @@ const onClose = () => {
<n-select
v-model:value="prefStore.cli.fontFamily"
:options="prefStore.fontOption"
:placeholder="$t('preferences.general.font_tip')"
:render-label="({ label, value }) => value || $t(label)"
filterable
multiple

View File

@ -37,6 +37,7 @@
"language": "Language",
"system_lang": "Use System Language",
"font": "Font",
"font_tip": "Please Select or Input Font Name",
"font_size": "Font Size",
"scan_size": "Default Size for SCAN Command",
"key_icon_style": "Key Icon Style",
@ -139,6 +140,7 @@
"action": "Action",
"type": "Type",
"cli_welcome": "Welcome to Tiny RDM Redis Console",
"retrieving_version": "Retrieving for new version",
"sub_tab": {
"status": "Status",
"key_detail": "Key Detail",

View File

@ -37,6 +37,7 @@
"language": "语言",
"system_lang": "使用系统语言",
"font": "字体",
"font_tip": "请选择或手动输入字体名",
"font_size": "字体尺寸",
"scan_size": "SCAN命令默认数量",
"key_icon_style": "键图标样式",
@ -139,6 +140,7 @@
"action": "操作",
"type": "类型",
"cli_welcome": "欢迎使用Tiny RDM的Redis命令行控制台",
"retrieving_version": "正在检索新版本",
"sub_tab": {
"status": "状态",
"key_detail": "键详情",
@ -153,7 +155,7 @@
"browser": "数据浏览",
"log": "日志",
"wechat_official": "微信公众号",
"follow_x": "关注我\uD835\uDD4F",
"follow_x": "关注\uD835\uDD4F",
"github": "Github"
},
"dialogue": {

View File

@ -338,7 +338,7 @@ const usePreferencesStore = defineStore('preferences', {
async checkForUpdate(manual = false) {
let msgRef = null
if (manual) {
msgRef = $message.loading('Retrieving for new version', { duration: 0 })
msgRef = $message.loading(i18nGlobal.t('interface.retrieving_version'), { duration: 0 })
}
try {
const { success, data = {} } = await CheckForUpdate()