Compare commits

..

7 Commits

20 changed files with 471 additions and 166 deletions

View File

@ -94,11 +94,11 @@ jobs:
Set-Content -Path certificate\certificate.txt -Value '${{ secrets.WIN_SIGNING_CERT }}' Set-Content -Path certificate\certificate.txt -Value '${{ secrets.WIN_SIGNING_CERT }}'
certutil -decode certificate\certificate.txt certificate\certificate.pfx certutil -decode certificate\certificate.txt certificate\certificate.pfx
echo "Signing TinyRDM installer" echo "Signing TinyRDM installer"
& 'C:/Program Files (x86)/Windows Kits/10/bin/10.0.17763.0/x86/signtool.exe' sign /fd sha256 /tr http://ts.ssl.com /f certificate\certificate.pfx /p '${{ secrets.WIN_SIGNING_CERT_PASSWORD }}' TinyRDM-${{ steps.normalise_platform.outputs.tag }}-installer.exe & 'C:/Program Files (x86)/Windows Kits/10/bin/10.0.17763.0/x86/signtool.exe' sign /fd sha256 /tr http://ts.ssl.com /f certificate\certificate.pfx /p '${{ secrets.WIN_SIGNING_CERT_PASSWORD }}' 'Tiny RDM.exe'
- name: Rename installer - name: Rename installer
working-directory: ./build/bin working-directory: ./build/bin
run: Rename-Item -Path "TinyRDM-${{ steps.normalise_platform.outputs.tag }}-installer.exe" -NewName "TinyRDM_Setup_${{ steps.normalise_version.outputs.version }}_${{ steps.normalise_platform.outputs.tag }}.exe" run: Rename-Item -Path "Tiny RDM.exe" -NewName "TinyRDM_Setup_${{ steps.normalise_version.outputs.version }}_${{ steps.normalise_platform.outputs.tag }}.exe"
- name: Upload release asset (Installer) - name: Upload release asset (Installer)
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1

View File

@ -37,7 +37,7 @@ Linux.</strong>
* Provides visually and user-friendly UI, light and dark themes (Thanks to [Naive UI](https://github.com/tusen-ai/naive-ui) * Provides visually and user-friendly UI, light and dark themes (Thanks to [Naive UI](https://github.com/tusen-ai/naive-ui)
and [IconPark](https://iconpark.oceanengine.com)). and [IconPark](https://iconpark.oceanengine.com)).
* Multi-language support ([Need more languages ? Click here to contribute](.github/CONTRIBUTING.md)). * Multi-language support ([Need more languages ? Click here to contribute](.github/CONTRIBUTING.md)).
* Better connection management: supports SSH Tunnel/SSL/Sentinel Mode/Cluster Mode. * Better connection management: supports SSH Tunnel/SSL/Sentinel Mode/Cluster Mode/HTTP proxy/SOCKS5 proxy.
* Visualize key value operations, CRUD support for Lists, Hashes, Strings, Sets, Sorted Sets, and Streams. * Visualize key value operations, CRUD support for Lists, Hashes, Strings, Sets, Sorted Sets, and Streams.
* Support multiple data viewing format and decode/decompression methods. * Support multiple data viewing format and decode/decompression methods.
* Use SCAN for segmented loading, making it easy to list millions of keys. * Use SCAN for segmented loading, making it easy to list millions of keys.
@ -50,11 +50,8 @@ Linux.</strong>
* Support real-time commands monitoring. * Support real-time commands monitoring.
* Support import/export data. * Support import/export data.
* Support publish/subscribe. * Support publish/subscribe.
* support import/export connection profile * Support import/export connection profile.
* Custom data encoder and decoder for value display.
## Roadmap
- [ ] Custom data encoder and decoder for value display
## Installation ## Installation

View File

@ -32,7 +32,7 @@
* 界面精美易用,提供浅色/深色主题(感谢[Naive UI](https://github.com/tusen-ai/naive-ui) * 界面精美易用,提供浅色/深色主题(感谢[Naive UI](https://github.com/tusen-ai/naive-ui)
和 [IconPark](https://iconpark.oceanengine.com) 和 [IconPark](https://iconpark.oceanengine.com)
* 多国语言支持:英文/中文([需要更多语言支持?点我贡献语言](.github/CONTRIBUTING_zh.md) * 多国语言支持:英文/中文([需要更多语言支持?点我贡献语言](.github/CONTRIBUTING_zh.md)
* 更好用的连接管理支持SSH隧道/SSL/哨兵模式/集群模式 * 更好用的连接管理支持SSH隧道/SSL/哨兵模式/集群模式/HTTP代理/SOCKS5代理
* 可视化键值操作,增删查改一应俱全 * 可视化键值操作,增删查改一应俱全
* 支持多种数据查看格式以及转码/解压方式 * 支持多种数据查看格式以及转码/解压方式
* 采用SCAN分段加载可轻松处理数百万键列表 * 采用SCAN分段加载可轻松处理数百万键列表
@ -46,10 +46,7 @@
* 支持导入/导出数据 * 支持导入/导出数据
* 支持发布订阅 * 支持发布订阅
* 支持导入/导出连接配置 * 支持导入/导出连接配置
* 自定义数据展示编码/解码
## 未来版本规划
- [ ] 自定义数据展示编码/解码
## 安装 ## 安装

View File

@ -11,8 +11,10 @@ import (
"github.com/vrischmann/userdir" "github.com/vrischmann/userdir"
"github.com/wailsapp/wails/v2/pkg/runtime" "github.com/wailsapp/wails/v2/pkg/runtime"
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
"golang.org/x/net/proxy"
"io" "io"
"net" "net"
"net/url"
"os" "os"
"path" "path"
"strconv" "strconv"
@ -21,6 +23,7 @@ import (
"time" "time"
. "tinyrdm/backend/storage" . "tinyrdm/backend/storage"
"tinyrdm/backend/types" "tinyrdm/backend/types"
_ "tinyrdm/backend/utils/proxy"
) )
type cmdHistoryItem struct { type cmdHistoryItem struct {
@ -54,9 +57,34 @@ func (c *connectionService) Start(ctx context.Context) {
} }
func (c *connectionService) buildOption(config types.ConnectionConfig) (*redis.Options, error) { func (c *connectionService) buildOption(config types.ConnectionConfig) (*redis.Options, error) {
var sshClient *ssh.Client var dialer proxy.Dialer
var dialerErr error
if config.Proxy.Type == 1 {
// use system proxy
dialer = proxy.FromEnvironment()
} else if config.Proxy.Type == 2 {
// use custom proxy
proxyUrl := url.URL{
Host: fmt.Sprintf("%s:%d", config.Proxy.Addr, config.Proxy.Port),
}
if len(config.Proxy.Username) > 0 {
proxyUrl.User = url.UserPassword(config.Proxy.Username, config.Proxy.Password)
}
switch config.Proxy.Schema {
case "socks5", "socks5h", "http", "https":
proxyUrl.Scheme = config.Proxy.Schema
default:
proxyUrl.Scheme = "http"
}
if dialer, dialerErr = proxy.FromURL(&proxyUrl, proxy.Direct); dialerErr != nil {
return nil, dialerErr
}
}
var sshConfig *ssh.ClientConfig
var sshAddr string
if config.SSH.Enable { if config.SSH.Enable {
sshConfig := &ssh.ClientConfig{ sshConfig = &ssh.ClientConfig{
User: config.SSH.Username, User: config.SSH.Username,
Auth: []ssh.AuthMethod{ssh.Password(config.SSH.Password)}, Auth: []ssh.AuthMethod{ssh.Password(config.SSH.Password)},
HostKeyCallback: ssh.InsecureIgnoreHostKey(), HostKeyCallback: ssh.InsecureIgnoreHostKey(),
@ -84,11 +112,7 @@ func (c *connectionService) buildOption(config types.ConnectionConfig) (*redis.O
return nil, errors.New("invalid login type") return nil, errors.New("invalid login type")
} }
var err error sshAddr = fmt.Sprintf("%s:%d", config.SSH.Addr, config.SSH.Port)
sshClient, err = ssh.Dial("tcp", fmt.Sprintf("%s:%d", config.SSH.Addr, config.SSH.Port), sshConfig)
if err != nil {
return nil, err
}
} }
var tlsConfig *tls.Config var tlsConfig *tls.Config
@ -150,9 +174,31 @@ func (c *connectionService) buildOption(config types.ConnectionConfig) (*redis.O
option.Addr = fmt.Sprintf("%s:%d", config.Addr, port) option.Addr = fmt.Sprintf("%s:%d", config.Addr, port)
} }
} }
if sshClient != nil {
if len(sshAddr) > 0 {
if dialer != nil {
// ssh with proxy
conn, err := dialer.Dial("tcp", sshAddr)
if err != nil {
return nil, err
}
sc, chans, reqs, err := ssh.NewClientConn(conn, sshAddr, sshConfig)
if err != nil {
return nil, err
}
dialer = ssh.NewClient(sc, chans, reqs)
} else {
// ssh without proxy
sshClient, err := ssh.Dial("tcp", sshAddr, sshConfig)
if err != nil {
return nil, err
}
dialer = sshClient
}
}
if dialer != nil {
option.Dialer = func(ctx context.Context, network, addr string) (net.Conn, error) { option.Dialer = func(ctx context.Context, network, addr string) (net.Conn, error) {
return sshClient.Dial(network, addr) return dialer.Dial(network, addr)
} }
option.ReadTimeout = -2 option.ReadTimeout = -2
option.WriteTimeout = -2 option.WriteTimeout = -2

View File

@ -27,6 +27,7 @@ type ConnectionConfig struct {
SSH ConnectionSSH `json:"ssh,omitempty" yaml:"ssh,omitempty"` SSH ConnectionSSH `json:"ssh,omitempty" yaml:"ssh,omitempty"`
Sentinel ConnectionSentinel `json:"sentinel,omitempty" yaml:"sentinel,omitempty"` Sentinel ConnectionSentinel `json:"sentinel,omitempty" yaml:"sentinel,omitempty"`
Cluster ConnectionCluster `json:"cluster,omitempty" yaml:"cluster,omitempty"` Cluster ConnectionCluster `json:"cluster,omitempty" yaml:"cluster,omitempty"`
Proxy ConnectionProxy `json:"proxy,omitempty" yaml:"proxy,omitempty"`
} }
type Connection struct { type Connection struct {
@ -76,3 +77,12 @@ type ConnectionSentinel struct {
type ConnectionCluster struct { type ConnectionCluster struct {
Enable bool `json:"enable,omitempty" yaml:"enable,omitempty"` Enable bool `json:"enable,omitempty" yaml:"enable,omitempty"`
} }
type ConnectionProxy struct {
Type int `json:"type,omitempty" yaml:"type,omitempty"`
Schema string `json:"schema,omitempty" yaml:"schema,omitempty"`
Addr string `json:"addr,omitempty" yaml:"addr,omitempty"`
Port int `json:"port,omitempty" yaml:"port,omitempty"`
Username string `json:"username,omitempty" yaml:"username,omitempty"`
Password string `json:"password,omitempty" yaml:"password,omitempty"`
}

View File

@ -0,0 +1,96 @@
package proxy
import (
"bufio"
"fmt"
"net"
"net/http"
"net/url"
"time"
"golang.org/x/net/proxy"
)
type HttpProxy struct {
scheme string // HTTP Proxy scheme
host string // HTTP Proxy host or host:port
auth *proxy.Auth // authentication
forward proxy.Dialer // forwarding Dialer
}
func (p *HttpProxy) Dial(network, addr string) (net.Conn, error) {
c, err := p.forward.Dial(network, p.host)
if err != nil {
return nil, err
}
err = c.SetDeadline(time.Now().Add(15 * time.Second))
if err != nil {
return nil, err
}
reqUrl := &url.URL{
Scheme: "",
Host: addr,
}
// create with CONNECT method
req, err := http.NewRequest("CONNECT", reqUrl.String(), nil)
if err != nil {
c.Close()
return nil, err
}
req.Close = false
// authentication
if p.auth != nil {
req.SetBasicAuth(p.auth.User, p.auth.Password)
req.Header.Add("Proxy-Authorization", req.Header.Get("Authorization"))
}
// send request
err = req.Write(c)
if err != nil {
c.Close()
return nil, err
}
res, err := http.ReadResponse(bufio.NewReader(c), req)
if err != nil {
res.Body.Close()
c.Close()
return nil, err
}
res.Body.Close()
if res.StatusCode != http.StatusOK {
c.Close()
return nil, fmt.Errorf("proxy connection error: StatusCode[%d]", res.StatusCode)
}
return c, nil
}
func NewHttpProxyDialer(u *url.URL, forward proxy.Dialer) (proxy.Dialer, error) {
var auth *proxy.Auth
if u.User != nil {
pwd, _ := u.User.Password()
auth = &proxy.Auth{
User: u.User.Username(),
Password: pwd,
}
}
hp := &HttpProxy{
scheme: u.Scheme,
host: u.Host,
auth: auth,
forward: forward,
}
return hp, nil
}
func init() {
proxy.RegisterDialerType("http", NewHttpProxyDialer)
proxy.RegisterDialerType("https", NewHttpProxyDialer)
}

View File

@ -13,21 +13,21 @@
"lodash": "^4.17.21", "lodash": "^4.17.21",
"monaco-editor": "^0.46.0", "monaco-editor": "^0.46.0",
"pinia": "^2.1.7", "pinia": "^2.1.7",
"sass": "^1.70.0", "sass": "^1.71.1",
"vue": "^3.4.18", "vue": "^3.4.19",
"vue-chartjs": "^5.3.0", "vue-chartjs": "^5.3.0",
"vue-i18n": "^9.9.1", "vue-i18n": "^9.9.1",
"xterm": "^5.3.0", "xterm": "^5.3.0",
"xterm-addon-fit": "^0.8.0" "xterm-addon-fit": "^0.8.0"
}, },
"devDependencies": { "devDependencies": {
"@vitejs/plugin-vue": "^5.0.3", "@vitejs/plugin-vue": "^5.0.4",
"naive-ui": "^2.37.3", "naive-ui": "^2.37.3",
"prettier": "^3.2.5", "prettier": "^3.2.5",
"unplugin-auto-import": "^0.17.5", "unplugin-auto-import": "^0.17.5",
"unplugin-icons": "^0.18.5", "unplugin-icons": "^0.18.5",
"unplugin-vue-components": "^0.26.0", "unplugin-vue-components": "^0.26.0",
"vite": "^5.1.0" "vite": "^5.1.4"
} }
}, },
"node_modules/@antfu/install-pkg": { "node_modules/@antfu/install-pkg": {
@ -902,9 +902,9 @@
} }
}, },
"node_modules/@vitejs/plugin-vue": { "node_modules/@vitejs/plugin-vue": {
"version": "5.0.3", "version": "5.0.4",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.3.tgz", "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.4.tgz",
"integrity": "sha512-b8S5dVS40rgHdDrw+DQi/xOM9ed+kSRZzfm1T74bMmBDCd8XO87NKlFYInzCtwvtWwXZvo1QxE2OSspTATWrbA==", "integrity": "sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": "^18.0.0 || >=20.0.0" "node": "^18.0.0 || >=20.0.0"
@ -915,36 +915,36 @@
} }
}, },
"node_modules/@vue/compiler-core": { "node_modules/@vue/compiler-core": {
"version": "3.4.18", "version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.18.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.19.tgz",
"integrity": "sha512-F7YK8lMK0iv6b9/Gdk15A67wM0KKZvxDxed0RR60C1z9tIJTKta+urs4j0RTN5XqHISzI3etN3mX0uHhjmoqjQ==", "integrity": "sha512-gj81785z0JNzRcU0Mq98E56e4ltO1yf8k5PQ+tV/7YHnbZkrM0fyFyuttnN8ngJZjbpofWE/m4qjKBiLl8Ju4w==",
"dependencies": { "dependencies": {
"@babel/parser": "^7.23.9", "@babel/parser": "^7.23.9",
"@vue/shared": "3.4.18", "@vue/shared": "3.4.19",
"entities": "^4.5.0", "entities": "^4.5.0",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"source-map-js": "^1.0.2" "source-map-js": "^1.0.2"
} }
}, },
"node_modules/@vue/compiler-dom": { "node_modules/@vue/compiler-dom": {
"version": "3.4.18", "version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.18.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.19.tgz",
"integrity": "sha512-24Eb8lcMfInefvQ6YlEVS18w5Q66f4+uXWVA+yb7praKbyjHRNuKVWGuinfSSjM0ZIiPi++QWukhkgznBaqpEA==", "integrity": "sha512-vm6+cogWrshjqEHTzIDCp72DKtea8Ry/QVpQRYoyTIg9k7QZDX6D8+HGURjtmatfgM8xgCFtJJaOlCaRYRK3QA==",
"dependencies": { "dependencies": {
"@vue/compiler-core": "3.4.18", "@vue/compiler-core": "3.4.19",
"@vue/shared": "3.4.18" "@vue/shared": "3.4.19"
} }
}, },
"node_modules/@vue/compiler-sfc": { "node_modules/@vue/compiler-sfc": {
"version": "3.4.18", "version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.18.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.19.tgz",
"integrity": "sha512-rG5tqtnzwrVpMqAQ7FHtvHaV70G6LLfJIWLYZB/jZ9m/hrnZmIQh+H3ewnC5onwe/ibljm9+ZupxeElzqCkTAw==", "integrity": "sha512-LQ3U4SN0DlvV0xhr1lUsgLCYlwQfUfetyPxkKYu7dkfvx7g3ojrGAkw0AERLOKYXuAGnqFsEuytkdcComei3Yg==",
"dependencies": { "dependencies": {
"@babel/parser": "^7.23.9", "@babel/parser": "^7.23.9",
"@vue/compiler-core": "3.4.18", "@vue/compiler-core": "3.4.19",
"@vue/compiler-dom": "3.4.18", "@vue/compiler-dom": "3.4.19",
"@vue/compiler-ssr": "3.4.18", "@vue/compiler-ssr": "3.4.19",
"@vue/shared": "3.4.18", "@vue/shared": "3.4.19",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"magic-string": "^0.30.6", "magic-string": "^0.30.6",
"postcss": "^8.4.33", "postcss": "^8.4.33",
@ -952,12 +952,12 @@
} }
}, },
"node_modules/@vue/compiler-ssr": { "node_modules/@vue/compiler-ssr": {
"version": "3.4.18", "version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.18.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.19.tgz",
"integrity": "sha512-hSlv20oUhPxo2UYUacHgGaxtqP0tvFo6ixxxD6JlXIkwzwoZ9eKK6PFQN4hNK/R13JlNyldwWt/fqGBKgWJ6nQ==", "integrity": "sha512-P0PLKC4+u4OMJ8sinba/5Z/iDT84uMRRlrWzadgLA69opCpI1gG4N55qDSC+dedwq2fJtzmGald05LWR5TFfLw==",
"dependencies": { "dependencies": {
"@vue/compiler-dom": "3.4.18", "@vue/compiler-dom": "3.4.19",
"@vue/shared": "3.4.18" "@vue/shared": "3.4.19"
} }
}, },
"node_modules/@vue/devtools-api": { "node_modules/@vue/devtools-api": {
@ -966,29 +966,29 @@
"integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==" "integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q=="
}, },
"node_modules/@vue/reactivity": { "node_modules/@vue/reactivity": {
"version": "3.4.18", "version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.18.tgz", "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.19.tgz",
"integrity": "sha512-7uda2/I0jpLiRygprDo5Jxs2HJkOVXcOMlyVlY54yRLxoycBpwGJRwJT9EdGB4adnoqJDXVT2BilUAYwI7qvmg==", "integrity": "sha512-+VcwrQvLZgEclGZRHx4O2XhyEEcKaBi50WbxdVItEezUf4fqRh838Ix6amWTdX0CNb/b6t3Gkz3eOebfcSt+UA==",
"dependencies": { "dependencies": {
"@vue/shared": "3.4.18" "@vue/shared": "3.4.19"
} }
}, },
"node_modules/@vue/runtime-core": { "node_modules/@vue/runtime-core": {
"version": "3.4.18", "version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.18.tgz", "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.19.tgz",
"integrity": "sha512-7mU9diCa+4e+8/wZ7Udw5pwTH10A11sZ1nldmHOUKJnzCwvZxfJqAtw31mIf4T5H2FsLCSBQT3xgioA9vIjyDQ==", "integrity": "sha512-/Z3tFwOrerJB/oyutmJGoYbuoadphDcJAd5jOuJE86THNZji9pYjZroQ2NFsZkTxOq0GJbb+s2kxTYToDiyZzw==",
"dependencies": { "dependencies": {
"@vue/reactivity": "3.4.18", "@vue/reactivity": "3.4.19",
"@vue/shared": "3.4.18" "@vue/shared": "3.4.19"
} }
}, },
"node_modules/@vue/runtime-dom": { "node_modules/@vue/runtime-dom": {
"version": "3.4.18", "version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.18.tgz", "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.19.tgz",
"integrity": "sha512-2y1Mkzcw1niSfG7z3Qx+2ir9Gb4hdTkZe5p/I8x1aTIKQE0vY0tPAEUPhZm5tx6183gG3D/KwHG728UR0sIufA==", "integrity": "sha512-IyZzIDqfNCF0OyZOauL+F4yzjMPN2rPd8nhqPP2N1lBn3kYqJpPHHru+83Rkvo2lHz5mW+rEeIMEF9qY3PB94g==",
"dependencies": { "dependencies": {
"@vue/runtime-core": "3.4.18", "@vue/runtime-core": "3.4.19",
"@vue/shared": "3.4.18", "@vue/shared": "3.4.19",
"csstype": "^3.1.3" "csstype": "^3.1.3"
} }
}, },
@ -998,21 +998,21 @@
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
}, },
"node_modules/@vue/server-renderer": { "node_modules/@vue/server-renderer": {
"version": "3.4.18", "version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.18.tgz", "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.19.tgz",
"integrity": "sha512-YJd1wa7mzUN3NRqLEsrwEYWyO+PUBSROIGlCc3J/cvn7Zu6CxhNLgXa8Z4zZ5ja5/nviYO79J1InoPeXgwBTZA==", "integrity": "sha512-eAj2p0c429RZyyhtMRnttjcSToch+kTWxFPHlzGMkR28ZbF1PDlTcmGmlDxccBuqNd9iOQ7xPRPAGgPVj+YpQw==",
"dependencies": { "dependencies": {
"@vue/compiler-ssr": "3.4.18", "@vue/compiler-ssr": "3.4.19",
"@vue/shared": "3.4.18" "@vue/shared": "3.4.19"
}, },
"peerDependencies": { "peerDependencies": {
"vue": "3.4.18" "vue": "3.4.19"
} }
}, },
"node_modules/@vue/shared": { "node_modules/@vue/shared": {
"version": "3.4.18", "version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.18.tgz", "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz",
"integrity": "sha512-CxouGFxxaW5r1WbrSmWwck3No58rApXgRSBxrqgnY1K+jk20F6DrXJkHdH9n4HVT+/B6G2CAn213Uq3npWiy8Q==" "integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw=="
}, },
"node_modules/acorn": { "node_modules/acorn": {
"version": "8.11.3", "version": "8.11.3",
@ -1934,9 +1934,9 @@
} }
}, },
"node_modules/sass": { "node_modules/sass": {
"version": "1.70.0", "version": "1.71.1",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.70.0.tgz", "resolved": "https://registry.npmjs.org/sass/-/sass-1.71.1.tgz",
"integrity": "sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==", "integrity": "sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==",
"dependencies": { "dependencies": {
"chokidar": ">=3.0.0 <4.0.0", "chokidar": ">=3.0.0 <4.0.0",
"immutable": "^4.0.0", "immutable": "^4.0.0",
@ -2259,9 +2259,9 @@
} }
}, },
"node_modules/vite": { "node_modules/vite": {
"version": "5.1.0", "version": "5.1.4",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.1.0.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.4.tgz",
"integrity": "sha512-STmSFzhY4ljuhz14bg9LkMTk3d98IO6DIArnTY6MeBwiD1Za2StcQtz7fzOUnRCqrHSD5+OS2reg4HOz1eoLnw==", "integrity": "sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"esbuild": "^0.19.3", "esbuild": "^0.19.3",
@ -2326,15 +2326,15 @@
} }
}, },
"node_modules/vue": { "node_modules/vue": {
"version": "3.4.18", "version": "3.4.19",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.4.18.tgz", "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.19.tgz",
"integrity": "sha512-0zLRYamFRe0wF4q2L3O24KQzLyLpL64ye1RUToOgOxuWZsb/FhaNRdGmeozdtVYLz6tl94OXLaK7/WQIrVCw1A==", "integrity": "sha512-W/7Fc9KUkajFU8dBeDluM4sRGc/aa4YJnOYck8dkjgZoXtVsn3OeTGni66FV1l3+nvPA7VBFYtPioaGKUmEADw==",
"dependencies": { "dependencies": {
"@vue/compiler-dom": "3.4.18", "@vue/compiler-dom": "3.4.19",
"@vue/compiler-sfc": "3.4.18", "@vue/compiler-sfc": "3.4.19",
"@vue/runtime-dom": "3.4.18", "@vue/runtime-dom": "3.4.19",
"@vue/server-renderer": "3.4.18", "@vue/server-renderer": "3.4.19",
"@vue/shared": "3.4.18" "@vue/shared": "3.4.19"
}, },
"peerDependencies": { "peerDependencies": {
"typescript": "*" "typescript": "*"
@ -2953,43 +2953,43 @@
} }
}, },
"@vitejs/plugin-vue": { "@vitejs/plugin-vue": {
"version": "5.0.3", "version": "5.0.4",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.3.tgz", "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.4.tgz",
"integrity": "sha512-b8S5dVS40rgHdDrw+DQi/xOM9ed+kSRZzfm1T74bMmBDCd8XO87NKlFYInzCtwvtWwXZvo1QxE2OSspTATWrbA==", "integrity": "sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==",
"dev": true, "dev": true,
"requires": {} "requires": {}
}, },
"@vue/compiler-core": { "@vue/compiler-core": {
"version": "3.4.18", "version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.18.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.19.tgz",
"integrity": "sha512-F7YK8lMK0iv6b9/Gdk15A67wM0KKZvxDxed0RR60C1z9tIJTKta+urs4j0RTN5XqHISzI3etN3mX0uHhjmoqjQ==", "integrity": "sha512-gj81785z0JNzRcU0Mq98E56e4ltO1yf8k5PQ+tV/7YHnbZkrM0fyFyuttnN8ngJZjbpofWE/m4qjKBiLl8Ju4w==",
"requires": { "requires": {
"@babel/parser": "^7.23.9", "@babel/parser": "^7.23.9",
"@vue/shared": "3.4.18", "@vue/shared": "3.4.19",
"entities": "^4.5.0", "entities": "^4.5.0",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"source-map-js": "^1.0.2" "source-map-js": "^1.0.2"
} }
}, },
"@vue/compiler-dom": { "@vue/compiler-dom": {
"version": "3.4.18", "version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.18.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.19.tgz",
"integrity": "sha512-24Eb8lcMfInefvQ6YlEVS18w5Q66f4+uXWVA+yb7praKbyjHRNuKVWGuinfSSjM0ZIiPi++QWukhkgznBaqpEA==", "integrity": "sha512-vm6+cogWrshjqEHTzIDCp72DKtea8Ry/QVpQRYoyTIg9k7QZDX6D8+HGURjtmatfgM8xgCFtJJaOlCaRYRK3QA==",
"requires": { "requires": {
"@vue/compiler-core": "3.4.18", "@vue/compiler-core": "3.4.19",
"@vue/shared": "3.4.18" "@vue/shared": "3.4.19"
} }
}, },
"@vue/compiler-sfc": { "@vue/compiler-sfc": {
"version": "3.4.18", "version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.18.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.19.tgz",
"integrity": "sha512-rG5tqtnzwrVpMqAQ7FHtvHaV70G6LLfJIWLYZB/jZ9m/hrnZmIQh+H3ewnC5onwe/ibljm9+ZupxeElzqCkTAw==", "integrity": "sha512-LQ3U4SN0DlvV0xhr1lUsgLCYlwQfUfetyPxkKYu7dkfvx7g3ojrGAkw0AERLOKYXuAGnqFsEuytkdcComei3Yg==",
"requires": { "requires": {
"@babel/parser": "^7.23.9", "@babel/parser": "^7.23.9",
"@vue/compiler-core": "3.4.18", "@vue/compiler-core": "3.4.19",
"@vue/compiler-dom": "3.4.18", "@vue/compiler-dom": "3.4.19",
"@vue/compiler-ssr": "3.4.18", "@vue/compiler-ssr": "3.4.19",
"@vue/shared": "3.4.18", "@vue/shared": "3.4.19",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"magic-string": "^0.30.6", "magic-string": "^0.30.6",
"postcss": "^8.4.33", "postcss": "^8.4.33",
@ -2997,12 +2997,12 @@
} }
}, },
"@vue/compiler-ssr": { "@vue/compiler-ssr": {
"version": "3.4.18", "version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.18.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.19.tgz",
"integrity": "sha512-hSlv20oUhPxo2UYUacHgGaxtqP0tvFo6ixxxD6JlXIkwzwoZ9eKK6PFQN4hNK/R13JlNyldwWt/fqGBKgWJ6nQ==", "integrity": "sha512-P0PLKC4+u4OMJ8sinba/5Z/iDT84uMRRlrWzadgLA69opCpI1gG4N55qDSC+dedwq2fJtzmGald05LWR5TFfLw==",
"requires": { "requires": {
"@vue/compiler-dom": "3.4.18", "@vue/compiler-dom": "3.4.19",
"@vue/shared": "3.4.18" "@vue/shared": "3.4.19"
} }
}, },
"@vue/devtools-api": { "@vue/devtools-api": {
@ -3011,29 +3011,29 @@
"integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==" "integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q=="
}, },
"@vue/reactivity": { "@vue/reactivity": {
"version": "3.4.18", "version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.18.tgz", "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.19.tgz",
"integrity": "sha512-7uda2/I0jpLiRygprDo5Jxs2HJkOVXcOMlyVlY54yRLxoycBpwGJRwJT9EdGB4adnoqJDXVT2BilUAYwI7qvmg==", "integrity": "sha512-+VcwrQvLZgEclGZRHx4O2XhyEEcKaBi50WbxdVItEezUf4fqRh838Ix6amWTdX0CNb/b6t3Gkz3eOebfcSt+UA==",
"requires": { "requires": {
"@vue/shared": "3.4.18" "@vue/shared": "3.4.19"
} }
}, },
"@vue/runtime-core": { "@vue/runtime-core": {
"version": "3.4.18", "version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.18.tgz", "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.19.tgz",
"integrity": "sha512-7mU9diCa+4e+8/wZ7Udw5pwTH10A11sZ1nldmHOUKJnzCwvZxfJqAtw31mIf4T5H2FsLCSBQT3xgioA9vIjyDQ==", "integrity": "sha512-/Z3tFwOrerJB/oyutmJGoYbuoadphDcJAd5jOuJE86THNZji9pYjZroQ2NFsZkTxOq0GJbb+s2kxTYToDiyZzw==",
"requires": { "requires": {
"@vue/reactivity": "3.4.18", "@vue/reactivity": "3.4.19",
"@vue/shared": "3.4.18" "@vue/shared": "3.4.19"
} }
}, },
"@vue/runtime-dom": { "@vue/runtime-dom": {
"version": "3.4.18", "version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.18.tgz", "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.19.tgz",
"integrity": "sha512-2y1Mkzcw1niSfG7z3Qx+2ir9Gb4hdTkZe5p/I8x1aTIKQE0vY0tPAEUPhZm5tx6183gG3D/KwHG728UR0sIufA==", "integrity": "sha512-IyZzIDqfNCF0OyZOauL+F4yzjMPN2rPd8nhqPP2N1lBn3kYqJpPHHru+83Rkvo2lHz5mW+rEeIMEF9qY3PB94g==",
"requires": { "requires": {
"@vue/runtime-core": "3.4.18", "@vue/runtime-core": "3.4.19",
"@vue/shared": "3.4.18", "@vue/shared": "3.4.19",
"csstype": "^3.1.3" "csstype": "^3.1.3"
}, },
"dependencies": { "dependencies": {
@ -3045,18 +3045,18 @@
} }
}, },
"@vue/server-renderer": { "@vue/server-renderer": {
"version": "3.4.18", "version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.18.tgz", "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.19.tgz",
"integrity": "sha512-YJd1wa7mzUN3NRqLEsrwEYWyO+PUBSROIGlCc3J/cvn7Zu6CxhNLgXa8Z4zZ5ja5/nviYO79J1InoPeXgwBTZA==", "integrity": "sha512-eAj2p0c429RZyyhtMRnttjcSToch+kTWxFPHlzGMkR28ZbF1PDlTcmGmlDxccBuqNd9iOQ7xPRPAGgPVj+YpQw==",
"requires": { "requires": {
"@vue/compiler-ssr": "3.4.18", "@vue/compiler-ssr": "3.4.19",
"@vue/shared": "3.4.18" "@vue/shared": "3.4.19"
} }
}, },
"@vue/shared": { "@vue/shared": {
"version": "3.4.18", "version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.18.tgz", "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz",
"integrity": "sha512-CxouGFxxaW5r1WbrSmWwck3No58rApXgRSBxrqgnY1K+jk20F6DrXJkHdH9n4HVT+/B6G2CAn213Uq3npWiy8Q==" "integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw=="
}, },
"acorn": { "acorn": {
"version": "8.11.3", "version": "8.11.3",
@ -3736,9 +3736,9 @@
} }
}, },
"sass": { "sass": {
"version": "1.70.0", "version": "1.71.1",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.70.0.tgz", "resolved": "https://registry.npmjs.org/sass/-/sass-1.71.1.tgz",
"integrity": "sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==", "integrity": "sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==",
"requires": { "requires": {
"chokidar": ">=3.0.0 <4.0.0", "chokidar": ">=3.0.0 <4.0.0",
"immutable": "^4.0.0", "immutable": "^4.0.0",
@ -3961,9 +3961,9 @@
} }
}, },
"vite": { "vite": {
"version": "5.1.0", "version": "5.1.4",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.1.0.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.4.tgz",
"integrity": "sha512-STmSFzhY4ljuhz14bg9LkMTk3d98IO6DIArnTY6MeBwiD1Za2StcQtz7fzOUnRCqrHSD5+OS2reg4HOz1eoLnw==", "integrity": "sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==",
"dev": true, "dev": true,
"requires": { "requires": {
"esbuild": "^0.19.3", "esbuild": "^0.19.3",
@ -3982,15 +3982,15 @@
} }
}, },
"vue": { "vue": {
"version": "3.4.18", "version": "3.4.19",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.4.18.tgz", "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.19.tgz",
"integrity": "sha512-0zLRYamFRe0wF4q2L3O24KQzLyLpL64ye1RUToOgOxuWZsb/FhaNRdGmeozdtVYLz6tl94OXLaK7/WQIrVCw1A==", "integrity": "sha512-W/7Fc9KUkajFU8dBeDluM4sRGc/aa4YJnOYck8dkjgZoXtVsn3OeTGni66FV1l3+nvPA7VBFYtPioaGKUmEADw==",
"requires": { "requires": {
"@vue/compiler-dom": "3.4.18", "@vue/compiler-dom": "3.4.19",
"@vue/compiler-sfc": "3.4.18", "@vue/compiler-sfc": "3.4.19",
"@vue/runtime-dom": "3.4.18", "@vue/runtime-dom": "3.4.19",
"@vue/server-renderer": "3.4.18", "@vue/server-renderer": "3.4.19",
"@vue/shared": "3.4.18" "@vue/shared": "3.4.19"
} }
}, },
"vue-chartjs": { "vue-chartjs": {

View File

@ -14,20 +14,20 @@
"lodash": "^4.17.21", "lodash": "^4.17.21",
"monaco-editor": "^0.46.0", "monaco-editor": "^0.46.0",
"pinia": "^2.1.7", "pinia": "^2.1.7",
"sass": "^1.70.0", "sass": "^1.71.1",
"vue": "^3.4.18", "vue": "^3.4.19",
"vue-chartjs": "^5.3.0", "vue-chartjs": "^5.3.0",
"vue-i18n": "^9.9.1", "vue-i18n": "^9.9.1",
"xterm": "^5.3.0", "xterm": "^5.3.0",
"xterm-addon-fit": "^0.8.0" "xterm-addon-fit": "^0.8.0"
}, },
"devDependencies": { "devDependencies": {
"@vitejs/plugin-vue": "^5.0.3", "@vitejs/plugin-vue": "^5.0.4",
"naive-ui": "^2.37.3", "naive-ui": "^2.37.3",
"prettier": "^3.2.5", "prettier": "^3.2.5",
"unplugin-auto-import": "^0.17.5", "unplugin-auto-import": "^0.17.5",
"unplugin-icons": "^0.18.5", "unplugin-icons": "^0.18.5",
"unplugin-vue-components": "^0.26.0", "unplugin-vue-components": "^0.26.0",
"vite": "^5.1.0" "vite": "^5.1.4"
} }
} }

View File

@ -1 +1 @@
0e7759f741b2d18b129ae154ce334ae9 0d716149856646a7939a157fbfa27913

View File

@ -1,8 +1,9 @@
<script setup> <script setup>
import { computed, h, ref } from 'vue' import { computed, h, ref } from 'vue'
import { get, isEmpty } from 'lodash' import { get, isEmpty, some } from 'lodash'
import { NIcon, NText } from 'naive-ui' import { NIcon, NText } from 'naive-ui'
import { useRender } from '@/utils/render.js' import { useRender } from '@/utils/render.js'
import { useI18n } from 'vue-i18n'
const props = defineProps({ const props = defineProps({
value: { value: {
@ -13,6 +14,10 @@ const props = defineProps({
type: Array, type: Array,
value: () => [], value: () => [],
}, },
menuOption: {
type: Array,
value: () => [],
},
tooltip: { tooltip: {
type: String, type: String,
}, },
@ -21,7 +26,8 @@ const props = defineProps({
disabled: Boolean, disabled: Boolean,
}) })
const emit = defineEmits(['update:value']) const emit = defineEmits(['update:value', 'menu'])
const i18n = useI18n()
const render = useRender() const render = useRender()
const renderHeader = () => { const renderHeader = () => {
@ -65,12 +71,29 @@ const dropdownOption = computed(() => {
}) })
} }
} }
if (!isEmpty(props.menuOption)) {
options.push({
key: 'header-divider',
type: 'divider',
})
for (const { key, label } of props.menuOption) {
options.push({
key,
label: i18n.t(label),
})
}
}
return options return options
}) })
const onDropdownSelect = (key) => { const onDropdownSelect = (key) => {
if (some(props.menuOption, { key })) {
emit('menu', key)
} else {
emit('update:value', key) emit('update:value', key)
} }
}
const buttonText = computed(() => { const buttonText = computed(() => {
return props.value || get(dropdownOption.value, [1, 'label'], props.default) return props.value || get(dropdownOption.value, [1, 'label'], props.default)
@ -88,7 +111,6 @@ const onDropdownShow = (show) => {
:options="dropdownOption" :options="dropdownOption"
:render-label="({ label }) => render.renderLabel(label, { class: 'type-selector-item' })" :render-label="({ label }) => render.renderLabel(label, { class: 'type-selector-item' })"
:show-arrow="true" :show-arrow="true"
:title="props.tooltip"
:value="props.value" :value="props.value"
trigger="click" trigger="click"
@select="onDropdownSelect" @select="onDropdownSelect"

View File

@ -6,6 +6,7 @@ import DropdownSelector from '@/components/common/DropdownSelector.vue'
import { includes, isEmpty, map, pull, some, values } from 'lodash' import { includes, isEmpty, map, pull, some, values } from 'lodash'
import { computed } from 'vue' import { computed } from 'vue'
import usePreferencesStore from 'stores/preferences.js' import usePreferencesStore from 'stores/preferences.js'
import useDialogStore from 'stores/dialog.js'
const props = defineProps({ const props = defineProps({
decode: { decode: {
@ -20,6 +21,7 @@ const props = defineProps({
}) })
const prefStore = usePreferencesStore() const prefStore = usePreferencesStore()
const dialogStore = useDialogStore()
const formatTypeOption = computed(() => { const formatTypeOption = computed(() => {
return map(formatTypes, (t) => t) return map(formatTypes, (t) => t)
@ -46,6 +48,15 @@ const decodeTypeOption = computed(() => {
return [buildinTypes, customTypes] return [buildinTypes, customTypes]
}) })
const decodeMenuOption = computed(() => {
return [
{
key: 'new_rdm_decoder',
label: 'interface.custom_decoder',
},
]
})
const emit = defineEmits(['formatChanged', 'update:decode', 'update:format']) const emit = defineEmits(['formatChanged', 'update:decode', 'update:format'])
const onFormatChanged = (selDecode, selFormat) => { const onFormatChanged = (selDecode, selFormat) => {
const [buildin, external] = decodeTypeOption.value const [buildin, external] = decodeTypeOption.value
@ -64,6 +75,14 @@ const onFormatChanged = (selDecode, selFormat) => {
emit('update:format', selFormat) emit('update:format', selFormat)
} }
} }
const onDecodeMenu = (key) => {
switch (key) {
case 'new_rdm_decoder':
dialogStore.openPreferencesDialog('decoder')
break
}
}
</script> </script>
<template> <template>
@ -81,9 +100,11 @@ const onFormatChanged = (selDecode, selFormat) => {
:default="decodeTypes.NONE" :default="decodeTypes.NONE"
:disabled="props.disabled" :disabled="props.disabled"
:icon="Conversion" :icon="Conversion"
:menu-option="decodeMenuOption"
:options="decodeTypeOption" :options="decodeTypeOption"
:tooltip="$t('interface.decode_with')" :tooltip="$t('interface.decode_with')"
:value="props.decode" :value="props.decode"
@menu="onDecodeMenu"
@update:value="(d) => onFormatChanged(d, '')" /> @update:value="(d) => onFormatChanged(d, '')" />
</n-space> </n-space>
</template> </template>

View File

@ -205,10 +205,24 @@ const onSaveConnection = async () => {
generalForm.value.sentinel = {} generalForm.value.sentinel = {}
} }
// trim cluster data
if (!!!generalForm.value.cluster.enable) { if (!!!generalForm.value.cluster.enable) {
generalForm.value.cluster = {} generalForm.value.cluster = {}
} }
// trim proxy data
if (generalForm.value.proxy.type !== 2) {
generalForm.value.proxy.schema = ''
generalForm.value.proxy.addr = ''
generalForm.value.proxy.port = 0
generalForm.value.proxy.auth = false
generalForm.value.proxy.username = ''
generalForm.value.proxy.password = ''
} else if (!generalForm.value.proxy.auth) {
generalForm.value.proxy.username = ''
generalForm.value.proxy.password = ''
}
// store new connection // store new connection
const { success, msg } = await connectionStore.saveConnection( const { success, msg } = await connectionStore.saveConnection(
isEditMode.value ? editName.value : null, isEditMode.value ? editName.value : null,
@ -248,6 +262,7 @@ watch(
pairs.push({ db: parseInt(db), alias: alias[db] }) pairs.push({ db: parseInt(db), alias: alias[db] })
} }
aliasPair.value = pairs aliasPair.value = pairs
generalForm.value.proxy.auth = !isEmpty(generalForm.value.proxy.username)
} }
}, },
) )
@ -723,6 +738,69 @@ const pasteFromClipboard = async () => {
<!-- label-placement="top">--> <!-- label-placement="top">-->
<!-- </n-form>--> <!-- </n-form>-->
</n-tab-pane> </n-tab-pane>
<!-- Proxy pane -->
<n-tab-pane :tab="$t('dialogue.connection.proxy.title')" display-directive="show:lazy" name="proxy">
<n-radio-group v-model:value="generalForm.proxy.type" name="radiogroup">
<n-space size="large" vertical>
<n-radio :label="$t('dialogue.connection.proxy.type_none')" :value="0" />
<n-radio :label="$t('dialogue.connection.proxy.type_system')" :value="1" />
<n-radio :label="$t('dialogue.connection.proxy.type_custom')" :value="2" />
<n-form
:disabled="generalForm.proxy.type !== 2"
:model="generalForm.proxy"
:show-require-mark="false"
label-placement="top">
<n-grid :x-gap="10">
<n-form-item-gi :show-label="false" :span="24" path="addr" required>
<n-input-group>
<n-select
v-model:value="generalForm.proxy.schema"
:consistent-menu-width="false"
:options="[
{ value: 'http', label: 'HTTP' },
{ value: 'https', label: 'HTTPS' },
{ value: 'socks5', label: 'SOCKS5' },
{ value: 'socks5h', label: 'SOCKS5H' },
]"
default-value="http"
style="max-width: 100px" />
<n-input
v-model:value="generalForm.proxy.addr"
:placeholder="$t('dialogue.connection.proxy.host')" />
<n-text style="width: 40px; text-align: center">:</n-text>
<n-input-number
v-model:value="generalForm.proxy.port"
:max="65535"
:min="0"
:show-button="false"
style="width: 200px" />
</n-input-group>
</n-form-item-gi>
<n-form-item-gi :show-label="false" :span="24" path="auth">
<n-checkbox v-model:checked="generalForm.proxy.auth" size="medium">
{{ $t('dialogue.connection.proxy.auth') }}
</n-checkbox>
</n-form-item-gi>
<n-form-item-gi :label="$t('dialogue.connection.usr')" :span="12" path="username">
<n-input
v-model:value="generalForm.proxy.username"
:disabled="!!!generalForm.proxy.auth"
:placeholder="$t('dialogue.connection.proxy.usr_tip')" />
</n-form-item-gi>
<n-form-item-gi :label="$t('dialogue.connection.pwd')" :span="12" path="password">
<n-input
v-model:value="generalForm.proxy.password"
:disabled="!!!generalForm.proxy.auth"
:placeholder="$t('dialogue.connection.proxy.pwd_tip')"
show-password-on="click"
type="password" />
</n-form-item-gi>
</n-grid>
</n-form>
</n-space>
</n-radio-group>
</n-tab-pane>
</n-tabs> </n-tabs>
<!-- test result alert--> <!-- test result alert-->

View File

@ -25,7 +25,7 @@ const loading = ref(false)
const initPreferences = async () => { const initPreferences = async () => {
try { try {
loading.value = true loading.value = true
tab.value = 'general' tab.value = dialogStore.preferencesTag || 'general'
await prefStore.loadPreferences() await prefStore.loadPreferences()
prevPreferences.value = { prevPreferences.value = {
general: prefStore.general, general: prefStore.general,

View File

@ -169,20 +169,23 @@ const exThemeVars = computed(() => {
v-if="prefStore.currentLanguage === 'zh'" v-if="prefStore.currentLanguage === 'zh'"
:icon="QRCode" :icon="QRCode"
:size="iconSize" :size="iconSize"
:tooltip-delay="100"
class="nav-menu-button" class="nav-menu-button"
t-tooltip="ribbon.wechat_official" t-tooltip="ribbon.wechat_official"
@click="openWechatOfficial" /> @click="openWechatOfficial" />
<icon-button <icon-button
v-else v-else
:icon="Twitter"
:border="false" :border="false"
:icon="Twitter"
:size="iconSize" :size="iconSize"
:tooltip-delay="100"
class="nav-menu-button" class="nav-menu-button"
t-tooltip="ribbon.follow_x" t-tooltip="ribbon.follow_x"
@click="openX" /> @click="openX" />
<icon-button <icon-button
:icon="Github" :icon="Github"
:size="iconSize" :size="iconSize"
:tooltip-delay="100"
class="nav-menu-button" class="nav-menu-button"
t-tooltip="ribbon.github" t-tooltip="ribbon.github"
@click="openGithub" /> @click="openGithub" />

View File

@ -127,6 +127,7 @@
"memory_usage": "Memory Usage", "memory_usage": "Memory Usage",
"view_as": "View As", "view_as": "View As",
"decode_with": "Decode / Decompression", "decode_with": "Decode / Decompression",
"custom_decoder": "New Custom Decoder",
"reload": "Reload", "reload": "Reload",
"reload_disable": "Reload will enable after full loaded", "reload_disable": "Reload will enable after full loaded",
"auto_refresh": "Auto Refresh", "auto_refresh": "Auto Refresh",
@ -268,6 +269,16 @@
"cluster": { "cluster": {
"title": "Cluster", "title": "Cluster",
"enable": "Serve as Cluster Node" "enable": "Serve as Cluster Node"
},
"proxy": {
"title": "Proxy",
"type_none": "No Proxy",
"type_system": "System Proxy Configuration",
"type_custom": "Manual Proxy Configuration",
"host": "Host name",
"auth": "Proxy authentication",
"usr_tip": "Username for proxy authentication",
"pwd_tip": "Password for proxy authentication"
} }
}, },
"group": { "group": {

View File

@ -127,6 +127,7 @@
"memory_usage": "内存占用", "memory_usage": "内存占用",
"view_as": "查看方式", "view_as": "查看方式",
"decode_with": "解码/解压方式", "decode_with": "解码/解压方式",
"custom_decoder": "添加自定义解码",
"reload": "重新载入", "reload": "重新载入",
"reload_disable": "全量加载后可重新载入", "reload_disable": "全量加载后可重新载入",
"auto_refresh": "自动刷新", "auto_refresh": "自动刷新",
@ -268,6 +269,16 @@
"cluster": { "cluster": {
"title": "集群模式", "title": "集群模式",
"enable": "当前为集群节点" "enable": "当前为集群节点"
},
"proxy": {
"title": "网络代理",
"type_none": "不使用代理",
"type_system": "使用系统代理设置",
"type_custom": "手动配置代理",
"host": "主机名",
"auth": "使用身份验证",
"usr_tip": "代理授权用户名",
"pwd_tip": "代理授权密码"
} }
}, },
"group": { "group": {

View File

@ -196,6 +196,15 @@ const useConnectionStore = defineStore('connections', {
cluster: { cluster: {
enable: false, enable: false,
}, },
proxy: {
type: 0,
schema: 'http',
addr: '',
port: 0,
auth: false,
username: '',
password: '',
},
} }
}, },

View File

@ -102,6 +102,8 @@ const useDialogStore = defineStore('dialog', {
}, },
preferencesDialogVisible: false, preferencesDialogVisible: false,
preferencesTag: '',
aboutDialogVisible: false, aboutDialogVisible: false,
}), }),
actions: { actions: {
@ -346,11 +348,13 @@ const useDialogStore = defineStore('dialog', {
this.decodeDialogVisible = false this.decodeDialogVisible = false
}, },
openPreferencesDialog() { openPreferencesDialog(tag = '') {
this.preferencesDialogVisible = true this.preferencesDialogVisible = true
this.preferencesTag = tag
}, },
closePreferencesDialog() { closePreferencesDialog() {
this.preferencesDialogVisible = false this.preferencesDialogVisible = false
this.preferencesTag = ''
}, },
openAboutDialog() { openAboutDialog() {

6
go.mod
View File

@ -6,12 +6,13 @@ require (
github.com/adrg/sysfont v0.1.2 github.com/adrg/sysfont v0.1.2
github.com/andybalholm/brotli v1.1.0 github.com/andybalholm/brotli v1.1.0
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/klauspost/compress v1.17.6 github.com/klauspost/compress v1.17.7
github.com/redis/go-redis/v9 v9.4.0 github.com/redis/go-redis/v9 v9.5.1
github.com/vmihailenco/msgpack/v5 v5.4.1 github.com/vmihailenco/msgpack/v5 v5.4.1
github.com/vrischmann/userdir v0.0.0-20151206171402-20f291cebd68 github.com/vrischmann/userdir v0.0.0-20151206171402-20f291cebd68
github.com/wailsapp/wails/v2 v2.8.0 github.com/wailsapp/wails/v2 v2.8.0
golang.org/x/crypto v0.19.0 golang.org/x/crypto v0.19.0
golang.org/x/net v0.21.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
@ -44,7 +45,6 @@ require (
github.com/wailsapp/go-webview2 v1.0.10 // indirect github.com/wailsapp/go-webview2 v1.0.10 // indirect
github.com/wailsapp/mimetype v1.4.1 // indirect github.com/wailsapp/mimetype v1.4.1 // indirect
golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 // indirect golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.17.0 // indirect golang.org/x/sys v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect golang.org/x/text v0.14.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect

8
go.sum
View File

@ -30,8 +30,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck= github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck=
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs= github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI= github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@ -66,8 +66,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/redis/go-redis/v9 v9.4.0 h1:Yzoz33UZw9I/mFhx4MNrB6Fk+XHO1VukNcCa1+lwyKk= github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8=
github.com/redis/go-redis/v9 v9.4.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=