Compare commits
5 Commits
6538313da8
...
8948f76f16
Author | SHA1 | Date |
---|---|---|
Lykin | 8948f76f16 | |
Lykin | 6d3526c765 | |
Lykin | 42fa24debd | |
Lykin | 022ee20eed | |
Lykin | 9402af2433 |
|
@ -179,8 +179,13 @@ func (b *browserService) OpenConnection(name string) (resp types.JSResp) {
|
||||||
dbInfoStr := info["Keyspace"][dbName]
|
dbInfoStr := info["Keyspace"][dbName]
|
||||||
if len(dbInfoStr) > 0 {
|
if len(dbInfoStr) > 0 {
|
||||||
dbInfo := b.parseDBItemInfo(dbInfoStr)
|
dbInfo := b.parseDBItemInfo(dbInfoStr)
|
||||||
|
var alias string
|
||||||
|
if selConn.Alias != nil {
|
||||||
|
alias = selConn.Alias[idx]
|
||||||
|
}
|
||||||
return types.ConnectionDB{
|
return types.ConnectionDB{
|
||||||
Name: dbName,
|
Name: dbName,
|
||||||
|
Alias: alias,
|
||||||
Index: idx,
|
Index: idx,
|
||||||
MaxKeys: dbInfo["keys"],
|
MaxKeys: dbInfo["keys"],
|
||||||
Expires: dbInfo["expires"],
|
Expires: dbInfo["expires"],
|
||||||
|
|
|
@ -6,10 +6,15 @@ import (
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/klauspost/compress/zip"
|
||||||
"github.com/redis/go-redis/v9"
|
"github.com/redis/go-redis/v9"
|
||||||
|
"github.com/vrischmann/userdir"
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
@ -367,11 +372,143 @@ func (c *connectionService) SaveLastDB(name string, db int) (resp types.JSResp)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if param.LastDB != db {
|
||||||
param.LastDB = db
|
param.LastDB = db
|
||||||
if err := c.conns.UpdateConnection(name, param.ConnectionConfig); err != nil {
|
if err := c.conns.UpdateConnection(name, param.ConnectionConfig); err != nil {
|
||||||
resp.Msg = "save connection fail:" + err.Error()
|
resp.Msg = "save connection fail:" + err.Error()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
resp.Success = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveRefreshInterval save auto refresh interval
|
||||||
|
func (c *connectionService) SaveRefreshInterval(name string, interval int) (resp types.JSResp) {
|
||||||
|
param := c.conns.GetConnection(name)
|
||||||
|
if param == nil {
|
||||||
|
resp.Msg = "no connection named \"" + name + "\""
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if param.RefreshInterval != interval {
|
||||||
|
param.RefreshInterval = interval
|
||||||
|
if err := c.conns.UpdateConnection(name, param.ConnectionConfig); err != nil {
|
||||||
|
resp.Msg = "save connection fail:" + err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resp.Success = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportConnections export connections to zip file
|
||||||
|
func (c *connectionService) ExportConnections() (resp types.JSResp) {
|
||||||
|
defaultFileName := "connections_" + time.Now().Format("20060102150405") + ".zip"
|
||||||
|
filepath, err := runtime.SaveFileDialog(c.ctx, runtime.SaveDialogOptions{
|
||||||
|
ShowHiddenFiles: true,
|
||||||
|
DefaultFilename: defaultFileName,
|
||||||
|
Filters: []runtime.FileFilter{
|
||||||
|
{
|
||||||
|
Pattern: "*.zip",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
resp.Msg = err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// compress the connections profile with zip
|
||||||
|
const connectionFilename = "connections.yaml"
|
||||||
|
inputFile, err := os.Open(path.Join(userdir.GetConfigHome(), "TinyRDM", connectionFilename))
|
||||||
|
if err != nil {
|
||||||
|
resp.Msg = err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer inputFile.Close()
|
||||||
|
|
||||||
|
outputFile, err := os.Create(filepath)
|
||||||
|
if err != nil {
|
||||||
|
resp.Msg = err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer outputFile.Close()
|
||||||
|
|
||||||
|
zipWriter := zip.NewWriter(outputFile)
|
||||||
|
defer zipWriter.Close()
|
||||||
|
|
||||||
|
headerWriter, err := zipWriter.CreateHeader(&zip.FileHeader{
|
||||||
|
Name: connectionFilename,
|
||||||
|
Method: zip.Deflate,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
resp.Msg = err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = io.Copy(headerWriter, inputFile); err != nil {
|
||||||
|
resp.Msg = err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Success = true
|
||||||
|
resp.Data = struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
}{
|
||||||
|
Path: filepath,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImportConnections import connections from local zip file
|
||||||
|
func (c *connectionService) ImportConnections() (resp types.JSResp) {
|
||||||
|
filepath, err := runtime.OpenFileDialog(c.ctx, runtime.OpenDialogOptions{
|
||||||
|
ShowHiddenFiles: true,
|
||||||
|
Filters: []runtime.FileFilter{
|
||||||
|
{
|
||||||
|
Pattern: "*.zip",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
resp.Msg = err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const connectionFilename = "connections.yaml"
|
||||||
|
zipFile, err := zip.OpenReader(filepath)
|
||||||
|
if err != nil {
|
||||||
|
resp.Msg = err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var file *zip.File
|
||||||
|
for _, file = range zipFile.File {
|
||||||
|
if file.Name == connectionFilename {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if file != nil {
|
||||||
|
zippedFile, err := file.Open()
|
||||||
|
if err != nil {
|
||||||
|
resp.Msg = err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer zippedFile.Close()
|
||||||
|
|
||||||
|
outputFile, err := os.Create(path.Join(userdir.GetConfigHome(), "TinyRDM", connectionFilename))
|
||||||
|
if err != nil {
|
||||||
|
resp.Msg = err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer outputFile.Close()
|
||||||
|
|
||||||
|
if _, err = io.Copy(outputFile, zippedFile); err != nil {
|
||||||
|
resp.Msg = err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
resp.Success = true
|
resp.Success = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ func (c *ConnectionsStorage) defaultConnectionItem() types.ConnectionConfig {
|
||||||
DBFilterList: []int{},
|
DBFilterList: []int{},
|
||||||
LoadSize: consts.DEFAULT_LOAD_SIZE,
|
LoadSize: consts.DEFAULT_LOAD_SIZE,
|
||||||
MarkColor: "",
|
MarkColor: "",
|
||||||
|
RefreshInterval: 5,
|
||||||
Sentinel: types.ConnectionSentinel{
|
Sentinel: types.ConnectionSentinel{
|
||||||
Master: "mymaster",
|
Master: "mymaster",
|
||||||
},
|
},
|
||||||
|
|
|
@ -19,6 +19,8 @@ type ConnectionConfig struct {
|
||||||
KeyView int `json:"keyView,omitempty" yaml:"key_view,omitempty"`
|
KeyView int `json:"keyView,omitempty" yaml:"key_view,omitempty"`
|
||||||
LoadSize int `json:"loadSize,omitempty" yaml:"load_size,omitempty"`
|
LoadSize int `json:"loadSize,omitempty" yaml:"load_size,omitempty"`
|
||||||
MarkColor string `json:"markColor,omitempty" yaml:"mark_color,omitempty"`
|
MarkColor string `json:"markColor,omitempty" yaml:"mark_color,omitempty"`
|
||||||
|
RefreshInterval int `json:"refreshInterval,omitempty" yaml:"refreshInterval,omitempty"`
|
||||||
|
Alias map[int]string `json:"alias,omitempty" yaml:"alias,omitempty"`
|
||||||
SSL ConnectionSSL `json:"ssl,omitempty" yaml:"ssl,omitempty"`
|
SSL ConnectionSSL `json:"ssl,omitempty" yaml:"ssl,omitempty"`
|
||||||
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"`
|
||||||
|
@ -35,6 +37,7 @@ type Connections []Connection
|
||||||
|
|
||||||
type ConnectionDB struct {
|
type ConnectionDB struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
Alias string `json:"alias,omitempty"`
|
||||||
Index int `json:"index"`
|
Index int `json:"index"`
|
||||||
MaxKeys int `json:"maxKeys"`
|
MaxKeys int `json:"maxKeys"`
|
||||||
Expires int `json:"expires,omitempty"`
|
Expires int `json:"expires,omitempty"`
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,42 @@
|
||||||
|
#!/bin/bash
|
||||||
|
clear
|
||||||
|
BLACK="\033[0;30m"
|
||||||
|
DARK_GRAY="\033[1;30m"
|
||||||
|
BLUE="\033[0;34m"
|
||||||
|
LIGHT_BLUE="\033[1;34m"
|
||||||
|
GREEN="\033[0;32m"
|
||||||
|
LIGHT_GREEN="\033[1;32m"
|
||||||
|
CYAN="\033[0;36m"
|
||||||
|
LIGHT_CYAN="\033[1;36m"
|
||||||
|
RED="\033[0;31m"
|
||||||
|
LIGHT_RED="\033[1;31m"
|
||||||
|
PURPLE="\033[0;35m"
|
||||||
|
LIGHT_PURPLE="\033[1;35m"
|
||||||
|
BROWN="\033[0;33m"
|
||||||
|
YELLOW="\033[0;33m"
|
||||||
|
LIGHT_GRAY="\033[0;37m"
|
||||||
|
WHITE="\033[1;37m"
|
||||||
|
NC="\033[0m"
|
||||||
|
|
||||||
|
parentPath=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P )
|
||||||
|
cd "$parentPath"
|
||||||
|
appPath=$( find "$parentPath" -name '*.app' -maxdepth 1)
|
||||||
|
appName=${appPath##*/}
|
||||||
|
appBashName=${appName// /\ }
|
||||||
|
appDIR="/Applications/${appBashName}"
|
||||||
|
echo -e "『${appBashName} 已损坏,无法打开/ 来自身份不明的开发者』等问题修复工具"
|
||||||
|
echo ""
|
||||||
|
#未安装APP时提醒安装,已安装绕过公证
|
||||||
|
if [ ! -d "$appDIR" ];then
|
||||||
|
echo ""
|
||||||
|
echo -e "执行结果:${RED}您还未安装 ${appBashName} ,请先安装${NC}"
|
||||||
|
else
|
||||||
|
#绕过公证
|
||||||
|
echo -e "${YELLOW}请输入开机密码,输入完成后按下回车键(输入过程中密码是看不见的)${NC}"
|
||||||
|
sudo spctl --master-disable
|
||||||
|
sudo xattr -rd com.apple.quarantine /Applications/"$appBashName"
|
||||||
|
sudo xattr -rc /Applications/"$appBashName"
|
||||||
|
sudo codesign --sign - --force --deep /Applications/"$appBashName"
|
||||||
|
echo -e "执行结果:${GREEN}修复成功!${NC}您现在可以正常运行 ${appBashName} 了。${NC}"
|
||||||
|
fi
|
||||||
|
echo -e "本窗口可以关闭啦!"
|
|
@ -13,7 +13,7 @@
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"monaco-editor": "^0.45.0",
|
"monaco-editor": "^0.45.0",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"sass": "^1.69.7",
|
"sass": "^1.70.0",
|
||||||
"vue": "^3.4.14",
|
"vue": "^3.4.14",
|
||||||
"vue-chartjs": "^5.3.0",
|
"vue-chartjs": "^5.3.0",
|
||||||
"vue-i18n": "^9.9.0",
|
"vue-i18n": "^9.9.0",
|
||||||
|
@ -1890,9 +1890,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/sass": {
|
"node_modules/sass": {
|
||||||
"version": "1.69.7",
|
"version": "1.70.0",
|
||||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.69.7.tgz",
|
"resolved": "https://registry.npmjs.org/sass/-/sass-1.70.0.tgz",
|
||||||
"integrity": "sha512-rzj2soDeZ8wtE2egyLXgOOHQvaC2iosZrkF6v3EUG+tBwEvhqUCzm0VP3k9gHF9LXbSrRhT5SksoI56Iw8NPnQ==",
|
"integrity": "sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chokidar": ">=3.0.0 <4.0.0",
|
"chokidar": ">=3.0.0 <4.0.0",
|
||||||
"immutable": "^4.0.0",
|
"immutable": "^4.0.0",
|
||||||
|
@ -3678,9 +3678,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sass": {
|
"sass": {
|
||||||
"version": "1.69.7",
|
"version": "1.70.0",
|
||||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.69.7.tgz",
|
"resolved": "https://registry.npmjs.org/sass/-/sass-1.70.0.tgz",
|
||||||
"integrity": "sha512-rzj2soDeZ8wtE2egyLXgOOHQvaC2iosZrkF6v3EUG+tBwEvhqUCzm0VP3k9gHF9LXbSrRhT5SksoI56Iw8NPnQ==",
|
"integrity": "sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"chokidar": ">=3.0.0 <4.0.0",
|
"chokidar": ">=3.0.0 <4.0.0",
|
||||||
"immutable": "^4.0.0",
|
"immutable": "^4.0.0",
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"monaco-editor": "^0.45.0",
|
"monaco-editor": "^0.45.0",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"sass": "^1.69.7",
|
"sass": "^1.70.0",
|
||||||
"vue": "^3.4.14",
|
"vue": "^3.4.14",
|
||||||
"vue-chartjs": "^5.3.0",
|
"vue-chartjs": "^5.3.0",
|
||||||
"vue-i18n": "^9.9.0",
|
"vue-i18n": "^9.9.0",
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
0024a462d0c3d76d35aeb433518d8b5e
|
43b12c3794341237a522ac6457ab81da
|
|
@ -124,7 +124,9 @@ watch(
|
||||||
<span>{{ $t('interface.sub_tab.status') }}</span>
|
<span>{{ $t('interface.sub_tab.status') }}</span>
|
||||||
</n-space>
|
</n-space>
|
||||||
</template>
|
</template>
|
||||||
<content-server-status :server="props.server" />
|
<content-server-status
|
||||||
|
:pause="selectedSubTab !== BrowserTabType.Status.toString()"
|
||||||
|
:server="props.server" />
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
|
|
||||||
<!-- key detail pane -->
|
<!-- key detail pane -->
|
||||||
|
|
|
@ -13,13 +13,16 @@ import dayjs from 'dayjs'
|
||||||
import { convertBytes, formatBytes } from '@/utils/byte_convert.js'
|
import { convertBytes, formatBytes } from '@/utils/byte_convert.js'
|
||||||
import usePreferencesStore from 'stores/preferences.js'
|
import usePreferencesStore from 'stores/preferences.js'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import useConnectionStore from 'stores/connections.js'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
server: String,
|
server: String,
|
||||||
|
pause: Boolean,
|
||||||
})
|
})
|
||||||
|
|
||||||
const browserStore = useBrowserStore()
|
const browserStore = useBrowserStore()
|
||||||
const prefStore = usePreferencesStore()
|
const prefStore = usePreferencesStore()
|
||||||
|
const connectionStore = useConnectionStore()
|
||||||
const i18n = useI18n()
|
const i18n = useI18n()
|
||||||
const themeVars = useThemeVars()
|
const themeVars = useThemeVars()
|
||||||
const serverInfo = ref({})
|
const serverInfo = ref({})
|
||||||
|
@ -125,13 +128,19 @@ const isLoading = computed(() => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const startAutoRefresh = async () => {
|
const startAutoRefresh = async () => {
|
||||||
|
// connectionStore.getRefreshInterval()
|
||||||
let lastExec = Date.now()
|
let lastExec = Date.now()
|
||||||
do {
|
do {
|
||||||
if (!pageState.autoRefresh) {
|
if (!pageState.autoRefresh) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
await timeout(100)
|
await timeout(100)
|
||||||
if (pageState.loading || pageState.autoLoading || Date.now() - lastExec < pageState.refreshInterval * 1000) {
|
if (
|
||||||
|
props.pause ||
|
||||||
|
pageState.loading ||
|
||||||
|
pageState.autoLoading ||
|
||||||
|
Date.now() - lastExec < pageState.refreshInterval * 1000
|
||||||
|
) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
lastExec = Date.now()
|
lastExec = Date.now()
|
||||||
|
@ -147,13 +156,23 @@ const stopAutoRefresh = () => {
|
||||||
const onToggleRefresh = (on) => {
|
const onToggleRefresh = (on) => {
|
||||||
if (on) {
|
if (on) {
|
||||||
tabVal.value = 'activity'
|
tabVal.value = 'activity'
|
||||||
|
connectionStore.saveRefreshInterval(props.server, pageState.refreshInterval || 5)
|
||||||
startAutoRefresh()
|
startAutoRefresh()
|
||||||
} else {
|
} else {
|
||||||
|
connectionStore.saveRefreshInterval(props.server, -1)
|
||||||
stopAutoRefresh()
|
stopAutoRefresh()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
const interval = connectionStore.getRefreshInterval(props.server)
|
||||||
|
if (interval >= 0) {
|
||||||
|
pageState.autoRefresh = true
|
||||||
|
pageState.refreshInterval = interval === 0 ? 5 : interval
|
||||||
|
onToggleRefresh(true)
|
||||||
|
} else {
|
||||||
|
setTimeout(refreshInfo, 5000)
|
||||||
|
}
|
||||||
refreshInfo()
|
refreshInfo()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -208,15 +227,15 @@ const totalKeys = computed(() => {
|
||||||
return sum(toArray(nums))
|
return sum(toArray(nums))
|
||||||
})
|
})
|
||||||
|
|
||||||
const tabVal = ref('info')
|
const tabVal = ref('activity')
|
||||||
const envFilter = reactive({
|
const infoFilter = reactive({
|
||||||
keyword: '',
|
keyword: '',
|
||||||
group: 'CPU',
|
group: 'CPU',
|
||||||
})
|
})
|
||||||
|
|
||||||
const env = computed(() => {
|
const info = computed(() => {
|
||||||
if (!isEmpty(envFilter.group)) {
|
if (!isEmpty(infoFilter.group)) {
|
||||||
const val = serverInfo.value[envFilter.group]
|
const val = serverInfo.value[infoFilter.group]
|
||||||
if (!isEmpty(val)) {
|
if (!isEmpty(val)) {
|
||||||
return map(val, (v, k) => ({
|
return map(val, (v, k) => ({
|
||||||
key: k,
|
key: k,
|
||||||
|
@ -235,10 +254,10 @@ const env = computed(() => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const onFilterGroup = (group) => {
|
const onFilterGroup = (group) => {
|
||||||
if (group === envFilter.group) {
|
if (group === infoFilter.group) {
|
||||||
envFilter.group = ''
|
infoFilter.group = ''
|
||||||
} else {
|
} else {
|
||||||
envFilter.group = group
|
infoFilter.group = group
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,6 +364,7 @@ const chartOption = {
|
||||||
scales: {
|
scales: {
|
||||||
y: {
|
y: {
|
||||||
beginAtZero: true,
|
beginAtZero: true,
|
||||||
|
stepSize: 1024,
|
||||||
suggestedMin: 0,
|
suggestedMin: 0,
|
||||||
ticks: {
|
ticks: {
|
||||||
precision: 0,
|
precision: 0,
|
||||||
|
@ -365,7 +385,7 @@ const byteChartOption = {
|
||||||
precision: 0,
|
precision: 0,
|
||||||
// format display y axios tag
|
// format display y axios tag
|
||||||
callback: function (value, index, values) {
|
callback: function (value, index, values) {
|
||||||
return formatBytes(value, 0)
|
return formatBytes(value, 1)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -429,7 +449,6 @@ const byteChartOption = {
|
||||||
@toggle="onToggleRefresh" />
|
@toggle="onToggleRefresh" />
|
||||||
</n-popover>
|
</n-popover>
|
||||||
</template>
|
</template>
|
||||||
<n-spin :show="pageState.loading">
|
|
||||||
<n-grid style="min-width: 500px" x-gap="5">
|
<n-grid style="min-width: 500px" x-gap="5">
|
||||||
<n-gi :span="6">
|
<n-gi :span="6">
|
||||||
<n-statistic :label="$t('status.uptime')" :value="uptime.value">
|
<n-statistic :label="$t('status.uptime')" :value="uptime.value">
|
||||||
|
@ -454,7 +473,6 @@ const byteChartOption = {
|
||||||
</n-statistic>
|
</n-statistic>
|
||||||
</n-gi>
|
</n-gi>
|
||||||
</n-grid>
|
</n-grid>
|
||||||
</n-spin>
|
|
||||||
</n-card>
|
</n-card>
|
||||||
<n-card class="flex-item-expand" content-style="padding: 0; height: 100%;" embedded style="overflow: hidden">
|
<n-card class="flex-item-expand" content-style="padding: 0; height: 100%;" embedded style="overflow: hidden">
|
||||||
<n-tabs
|
<n-tabs
|
||||||
|
@ -466,7 +484,7 @@ const byteChartOption = {
|
||||||
type="line">
|
type="line">
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
<div v-if="tabVal === 'info'" style="padding-right: 10px">
|
<div v-if="tabVal === 'info'" style="padding-right: 10px">
|
||||||
<n-input v-model:value="envFilter.keyword" clearable placeholder="">
|
<n-input v-model:value="infoFilter.keyword" clearable placeholder="">
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<icon-button :icon="Filter" size="18" />
|
<icon-button :icon="Filter" size="18" />
|
||||||
</template>
|
</template>
|
||||||
|
@ -474,46 +492,6 @@ const byteChartOption = {
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- environment tab pane -->
|
|
||||||
<n-tab-pane :tab="$t('status.env_info')" name="info">
|
|
||||||
<n-space :wrap="false" :wrap-item="false" class="flex-item-expand">
|
|
||||||
<n-space align="end" item-style="padding: 0 5px;" vertical>
|
|
||||||
<n-button
|
|
||||||
v-for="(v, k) in serverInfo"
|
|
||||||
:key="k"
|
|
||||||
:disabled="isEmpty(v)"
|
|
||||||
:focusable="false"
|
|
||||||
:type="envFilter.group === k ? 'primary' : 'default'"
|
|
||||||
secondary
|
|
||||||
size="small"
|
|
||||||
@click="onFilterGroup(k)">
|
|
||||||
<span style="min-width: 80px">{{ k }}</span>
|
|
||||||
</n-button>
|
|
||||||
</n-space>
|
|
||||||
<n-data-table
|
|
||||||
:columns="[
|
|
||||||
{
|
|
||||||
title: $t('common.key'),
|
|
||||||
key: 'key',
|
|
||||||
defaultSortOrder: 'ascend',
|
|
||||||
minWidth: 80,
|
|
||||||
titleAlign: 'center',
|
|
||||||
filterOptionValue: envFilter.keyword,
|
|
||||||
filter(value, row) {
|
|
||||||
return !!~row.key.indexOf(value.toString())
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{ title: $t('common.value'), titleAlign: 'center', key: 'value' },
|
|
||||||
]"
|
|
||||||
:data="env"
|
|
||||||
:loading="pageState.loading"
|
|
||||||
:single-line="false"
|
|
||||||
class="flex-item-expand"
|
|
||||||
flex-height
|
|
||||||
striped />
|
|
||||||
</n-space>
|
|
||||||
</n-tab-pane>
|
|
||||||
|
|
||||||
<!-- activity tab pane -->
|
<!-- activity tab pane -->
|
||||||
<n-tab-pane
|
<n-tab-pane
|
||||||
:tab="$t('status.activity_status')"
|
:tab="$t('status.activity_status')"
|
||||||
|
@ -535,6 +513,46 @@ const byteChartOption = {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
|
|
||||||
|
<!-- info tab pane -->
|
||||||
|
<n-tab-pane :tab="$t('status.server_info')" name="info">
|
||||||
|
<n-space :wrap="false" :wrap-item="false" class="flex-item-expand">
|
||||||
|
<n-space align="end" item-style="padding: 0 5px;" vertical>
|
||||||
|
<n-button
|
||||||
|
v-for="(v, k) in serverInfo"
|
||||||
|
:key="k"
|
||||||
|
:disabled="isEmpty(v)"
|
||||||
|
:focusable="false"
|
||||||
|
:type="infoFilter.group === k ? 'primary' : 'default'"
|
||||||
|
secondary
|
||||||
|
size="small"
|
||||||
|
@click="onFilterGroup(k)">
|
||||||
|
<span style="min-width: 80px">{{ k }}</span>
|
||||||
|
</n-button>
|
||||||
|
</n-space>
|
||||||
|
<n-data-table
|
||||||
|
:columns="[
|
||||||
|
{
|
||||||
|
title: $t('common.key'),
|
||||||
|
key: 'key',
|
||||||
|
defaultSortOrder: 'ascend',
|
||||||
|
minWidth: 80,
|
||||||
|
titleAlign: 'center',
|
||||||
|
filterOptionValue: infoFilter.keyword,
|
||||||
|
filter(value, row) {
|
||||||
|
return !!~row.key.indexOf(value.toString())
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ title: $t('common.value'), titleAlign: 'center', key: 'value' },
|
||||||
|
]"
|
||||||
|
:data="info"
|
||||||
|
:loading="pageState.loading"
|
||||||
|
:single-line="false"
|
||||||
|
class="flex-item-expand"
|
||||||
|
flex-height
|
||||||
|
striped />
|
||||||
|
</n-space>
|
||||||
|
</n-tab-pane>
|
||||||
</n-tabs>
|
</n-tabs>
|
||||||
</n-card>
|
</n-card>
|
||||||
</n-space>
|
</n-space>
|
||||||
|
|
|
@ -11,10 +11,10 @@ import IconButton from '@/components/common/IconButton.vue'
|
||||||
import Copy from '@/components/icons/Copy.vue'
|
import Copy from '@/components/icons/Copy.vue'
|
||||||
import { ClipboardSetText } from 'wailsjs/runtime/runtime.js'
|
import { ClipboardSetText } from 'wailsjs/runtime/runtime.js'
|
||||||
import { computed, onUnmounted, reactive, watch } from 'vue'
|
import { computed, onUnmounted, reactive, watch } from 'vue'
|
||||||
import { padStart } from 'lodash'
|
|
||||||
import { NIcon, useThemeVars } from 'naive-ui'
|
import { NIcon, useThemeVars } from 'naive-ui'
|
||||||
import { timeout } from '@/utils/promise.js'
|
import { timeout } from '@/utils/promise.js'
|
||||||
import AutoRefreshForm from '@/components/common/AutoRefreshForm.vue'
|
import AutoRefreshForm from '@/components/common/AutoRefreshForm.vue'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
server: String,
|
server: String,
|
||||||
|
@ -54,20 +54,19 @@ const binaryKey = computed(() => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const ttlString = computed(() => {
|
const ttlString = computed(() => {
|
||||||
let s = ''
|
|
||||||
if (props.ttl > 0) {
|
if (props.ttl > 0) {
|
||||||
const hours = Math.floor(props.ttl / 3600)
|
const dur = dayjs.duration(props.ttl, 'seconds')
|
||||||
s += padStart(hours + ':', 3, '0')
|
const days = dur.days()
|
||||||
const minutes = Math.floor((props.ttl % 3600) / 60)
|
if (days > 0) {
|
||||||
s += padStart(minutes + ':', 3, '0')
|
return days + i18n.t('common.unit_day') + ' ' + dur.format('HH:mm:ss')
|
||||||
const seconds = Math.floor(props.ttl % 60)
|
|
||||||
s += padStart(seconds + '', 2, '0')
|
|
||||||
} else if (props.ttl < 0) {
|
|
||||||
s = '-1'
|
|
||||||
} else {
|
} else {
|
||||||
s = '00:00:00'
|
return dur.format('HH:mm:ss')
|
||||||
|
}
|
||||||
|
} else if (props.ttl < 0) {
|
||||||
|
return i18n.t('interface.forever')
|
||||||
|
} else {
|
||||||
|
return '00:00:00'
|
||||||
}
|
}
|
||||||
return s
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const startAutoRefresh = async () => {
|
const startAutoRefresh = async () => {
|
||||||
|
@ -165,7 +164,7 @@ const onTTL = () => {
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<n-icon :component="Timer" size="18" />
|
<n-icon :component="Timer" size="18" />
|
||||||
</template>
|
</template>
|
||||||
{{ ttlString === '-1' ? $t('interface.forever') : ttlString }}
|
{{ ttlString }}
|
||||||
</n-button>
|
</n-button>
|
||||||
</template>
|
</template>
|
||||||
TTL{{ `${ttl > 0 ? ': ' + ttl + $t('common.second') : ''}` }}
|
TTL{{ `${ttl > 0 ? ': ' + ttl + $t('common.second') : ''}` }}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { every, get, includes, isEmpty, map, sortBy, toNumber } from 'lodash'
|
import { every, get, includes, isEmpty, map, reject, sortBy, toNumber } from 'lodash'
|
||||||
import { computed, nextTick, ref, watch } from 'vue'
|
import { computed, nextTick, ref, watch } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { ListSentinelMasters, TestConnection } from 'wailsjs/go/services/connectionService.js'
|
import { ListSentinelMasters, TestConnection } from 'wailsjs/go/services/connectionService.js'
|
||||||
|
@ -10,6 +10,9 @@ import FileOpenInput from '@/components/common/FileOpenInput.vue'
|
||||||
import { KeyViewType } from '@/consts/key_view_type.js'
|
import { KeyViewType } from '@/consts/key_view_type.js'
|
||||||
import { useThemeVars } from 'naive-ui'
|
import { useThemeVars } from 'naive-ui'
|
||||||
import useBrowserStore from 'stores/browser.js'
|
import useBrowserStore from 'stores/browser.js'
|
||||||
|
import Delete from '@/components/icons/Delete.vue'
|
||||||
|
import Add from '@/components/icons/Add.vue'
|
||||||
|
import IconButton from '@/components/common/IconButton.vue'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dialog for new or edit connection
|
* Dialog for new or edit connection
|
||||||
|
@ -72,6 +75,24 @@ const onUpdateDBFilterType = (t) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const aliasPair = ref([
|
||||||
|
/*{ db: 0, alias: '' }*/
|
||||||
|
])
|
||||||
|
const onCreateAlias = () => {
|
||||||
|
return {
|
||||||
|
db: 0,
|
||||||
|
alias: '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const onUpdateAlias = () => {
|
||||||
|
const val = reject(aliasPair.value, (v) => v == null || isEmpty(v.alias))
|
||||||
|
const result = {}
|
||||||
|
for (const elem of val) {
|
||||||
|
result[elem.db] = elem.alias
|
||||||
|
}
|
||||||
|
generalForm.value.alias = result
|
||||||
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => dbFilterList.value,
|
() => dbFilterList.value,
|
||||||
(list) => {
|
(list) => {
|
||||||
|
@ -210,6 +231,13 @@ watch(
|
||||||
generalForm.value = dialogStore.connParam || connectionStore.newDefaultConnection()
|
generalForm.value = dialogStore.connParam || connectionStore.newDefaultConnection()
|
||||||
dbFilterList.value = map(generalForm.value.dbFilterList, (item) => item + '')
|
dbFilterList.value = map(generalForm.value.dbFilterList, (item) => item + '')
|
||||||
generalForm.value.ssh.loginType = generalForm.value.ssh.loginType || 'pwd'
|
generalForm.value.ssh.loginType = generalForm.value.ssh.loginType || 'pwd'
|
||||||
|
// update alias display
|
||||||
|
const alias = get(generalForm.value, 'alias', {})
|
||||||
|
const pairs = []
|
||||||
|
for (const db in alias) {
|
||||||
|
pairs.push({ db: parseInt(db), alias: alias[db] })
|
||||||
|
}
|
||||||
|
aliasPair.value = pairs
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -254,9 +282,15 @@ const onClose = () => {
|
||||||
style="width: 600px"
|
style="width: 600px"
|
||||||
transform-origin="center">
|
transform-origin="center">
|
||||||
<n-spin :show="closingConnection">
|
<n-spin :show="closingConnection">
|
||||||
<n-tabs v-model:value="tab" animated type="line">
|
<n-tabs
|
||||||
|
v-model:value="tab"
|
||||||
|
animated
|
||||||
|
pane-style="min-height: 50vh;"
|
||||||
|
placement="left"
|
||||||
|
tab-style="justify-content: right;"
|
||||||
|
type="line">
|
||||||
<!-- General pane -->
|
<!-- General pane -->
|
||||||
<n-tab-pane :tab="$t('dialogue.connection.general')" display-directive="show" name="general">
|
<n-tab-pane :tab="$t('dialogue.connection.general')" display-directive="show:lazy" name="general">
|
||||||
<n-form
|
<n-form
|
||||||
ref="generalFormRef"
|
ref="generalFormRef"
|
||||||
:model="generalForm"
|
:model="generalForm"
|
||||||
|
@ -434,8 +468,42 @@ const onClose = () => {
|
||||||
</n-form>
|
</n-form>
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
|
|
||||||
|
<n-tab-pane :tab="$t('dialogue.connection.alias.title')" display-directive="show:lazy" name="alias">
|
||||||
|
<n-form
|
||||||
|
:model="generalForm.alias"
|
||||||
|
:show-label="false"
|
||||||
|
:show-require-mark="false"
|
||||||
|
label-placement="top">
|
||||||
|
<n-form-item required>
|
||||||
|
<n-dynamic-input
|
||||||
|
v-model:value="aliasPair"
|
||||||
|
@create="onCreateAlias"
|
||||||
|
@update:value="onUpdateAlias">
|
||||||
|
<template #default="{ value }">
|
||||||
|
<n-input-number
|
||||||
|
v-model:value="value.db"
|
||||||
|
:min="0"
|
||||||
|
:placeholder="$t('dialogue.connection.alias.db')"
|
||||||
|
:show-button="false"
|
||||||
|
@update:value="onUpdateAlias" />
|
||||||
|
<n-text>:</n-text>
|
||||||
|
<n-input
|
||||||
|
v-model:value="value.alias"
|
||||||
|
:placeholder="$t('dialogue.connection.alias.value')"
|
||||||
|
type="text"
|
||||||
|
@update:value="onUpdateAlias" />
|
||||||
|
</template>
|
||||||
|
<template #action="{ index, create, remove, move }">
|
||||||
|
<icon-button :icon="Delete" size="18" @click="() => remove(index)" />
|
||||||
|
<icon-button :icon="Add" size="18" @click="() => create(index)" />
|
||||||
|
</template>
|
||||||
|
</n-dynamic-input>
|
||||||
|
</n-form-item>
|
||||||
|
</n-form>
|
||||||
|
</n-tab-pane>
|
||||||
|
|
||||||
<!-- SSL pane -->
|
<!-- SSL pane -->
|
||||||
<n-tab-pane :tab="$t('dialogue.connection.ssl.title')" display-directive="show" name="ssl">
|
<n-tab-pane :tab="$t('dialogue.connection.ssl.title')" display-directive="show:lazy" name="ssl">
|
||||||
<n-form-item label-placement="left">
|
<n-form-item label-placement="left">
|
||||||
<n-checkbox v-model:checked="generalForm.ssl.enable" size="medium">
|
<n-checkbox v-model:checked="generalForm.ssl.enable" size="medium">
|
||||||
{{ $t('dialogue.connection.ssl.enable') }}
|
{{ $t('dialogue.connection.ssl.enable') }}
|
||||||
|
@ -478,7 +546,7 @@ const onClose = () => {
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
|
|
||||||
<!-- SSH pane -->
|
<!-- SSH pane -->
|
||||||
<n-tab-pane :tab="$t('dialogue.connection.ssh.title')" display-directive="show" name="ssh">
|
<n-tab-pane :tab="$t('dialogue.connection.ssh.title')" display-directive="show:lazy" name="ssh">
|
||||||
<n-form-item label-placement="left">
|
<n-form-item label-placement="left">
|
||||||
<n-checkbox v-model:checked="generalForm.ssh.enable" size="medium">
|
<n-checkbox v-model:checked="generalForm.ssh.enable" size="medium">
|
||||||
{{ $t('dialogue.connection.ssh.enable') }}
|
{{ $t('dialogue.connection.ssh.enable') }}
|
||||||
|
@ -538,7 +606,10 @@ const onClose = () => {
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
|
|
||||||
<!-- Sentinel pane -->
|
<!-- Sentinel pane -->
|
||||||
<n-tab-pane :tab="$t('dialogue.connection.sentinel.title')" display-directive="show" name="sentinel">
|
<n-tab-pane
|
||||||
|
:tab="$t('dialogue.connection.sentinel.title')"
|
||||||
|
display-directive="show:lazy"
|
||||||
|
name="sentinel">
|
||||||
<n-form-item label-placement="left">
|
<n-form-item label-placement="left">
|
||||||
<n-checkbox v-model:checked="generalForm.sentinel.enable" size="medium">
|
<n-checkbox v-model:checked="generalForm.sentinel.enable" size="medium">
|
||||||
{{ $t('dialogue.connection.sentinel.enable') }}
|
{{ $t('dialogue.connection.sentinel.enable') }}
|
||||||
|
@ -580,7 +651,7 @@ const onClose = () => {
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
|
|
||||||
<!-- Cluster pane -->
|
<!-- Cluster pane -->
|
||||||
<n-tab-pane :tab="$t('dialogue.connection.cluster.title')" display-directive="show" name="cluster">
|
<n-tab-pane :tab="$t('dialogue.connection.cluster.title')" display-directive="show:lazy" name="cluster">
|
||||||
<n-form-item label-placement="left">
|
<n-form-item label-placement="left">
|
||||||
<n-checkbox v-model:checked="generalForm.cluster.enable" size="medium">
|
<n-checkbox v-model:checked="generalForm.cluster.enable" size="medium">
|
||||||
{{ $t('dialogue.connection.cluster.enable') }}
|
{{ $t('dialogue.connection.cluster.enable') }}
|
||||||
|
|
|
@ -25,11 +25,6 @@ const updateOption = [
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef ZSetItem
|
|
||||||
* @property {string} value
|
|
||||||
* @property {string} score
|
|
||||||
*/
|
|
||||||
const zset = ref([{ value: '', score: 0 }])
|
const zset = ref([{ value: '', score: 0 }])
|
||||||
const onCreate = () => {
|
const onCreate = () => {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import BrowserTree from './BrowserTree.vue'
|
||||||
import IconButton from '@/components/common/IconButton.vue'
|
import IconButton from '@/components/common/IconButton.vue'
|
||||||
import useTabStore from 'stores/tab.js'
|
import useTabStore from 'stores/tab.js'
|
||||||
import { computed, nextTick, onMounted, reactive, ref, unref, watch } from 'vue'
|
import { computed, nextTick, onMounted, reactive, ref, unref, watch } from 'vue'
|
||||||
import { find, get, map, size } from 'lodash'
|
import { find, get, isEmpty, map, size } from 'lodash'
|
||||||
import Refresh from '@/components/icons/Refresh.vue'
|
import Refresh from '@/components/icons/Refresh.vue'
|
||||||
import useDialogStore from 'stores/dialog.js'
|
import useDialogStore from 'stores/dialog.js'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
@ -50,16 +50,22 @@ const inCheckState = ref(false)
|
||||||
|
|
||||||
const dbSelectOptions = computed(() => {
|
const dbSelectOptions = computed(() => {
|
||||||
const dblist = browserStore.getDBList(props.server)
|
const dblist = browserStore.getDBList(props.server)
|
||||||
return map(dblist, (db) => {
|
return map(dblist, ({ db, alias, keyCount, maxKeys }) => {
|
||||||
if (props.db === db.db) {
|
let label
|
||||||
return {
|
if (!isEmpty(alias)) {
|
||||||
value: db.db,
|
// has alias
|
||||||
label: `db${db.db} (${db.keyCount}/${db.maxKeys})`,
|
label = `${alias}[${db}]`
|
||||||
|
} else {
|
||||||
|
label = `db${db}`
|
||||||
}
|
}
|
||||||
|
if (props.db === db) {
|
||||||
|
label += ` (${keyCount}/${maxKeys})`
|
||||||
|
} else {
|
||||||
|
label += ` (${maxKeys})`
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
value: db.db,
|
value: db,
|
||||||
label: `db${db.db} (${db.maxKeys})`,
|
label: label,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -7,10 +7,34 @@ import IconButton from '@/components/common/IconButton.vue'
|
||||||
import Filter from '@/components/icons/Filter.vue'
|
import Filter from '@/components/icons/Filter.vue'
|
||||||
import ConnectionTree from './ConnectionTree.vue'
|
import ConnectionTree from './ConnectionTree.vue'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
import More from '@/components/icons/More.vue'
|
||||||
|
import Import from '@/components/icons/Import.vue'
|
||||||
|
import { useRender } from '@/utils/render.js'
|
||||||
|
import Export from '@/components/icons/Export.vue'
|
||||||
|
import useConnectionStore from 'stores/connections.js'
|
||||||
|
|
||||||
const themeVars = useThemeVars()
|
const themeVars = useThemeVars()
|
||||||
const dialogStore = useDialogStore()
|
const dialogStore = useDialogStore()
|
||||||
|
const connectionStore = useConnectionStore()
|
||||||
|
const render = useRender()
|
||||||
const filterPattern = ref('')
|
const filterPattern = ref('')
|
||||||
|
|
||||||
|
const moreOptions = [
|
||||||
|
{ key: 'import', label: 'interface.import_conn', icon: Import },
|
||||||
|
{ key: 'export', label: 'interface.export_conn', icon: Export },
|
||||||
|
]
|
||||||
|
|
||||||
|
const onSelectOptions = async (select) => {
|
||||||
|
switch (select) {
|
||||||
|
case 'import':
|
||||||
|
await connectionStore.importConnections()
|
||||||
|
await connectionStore.initConnections(true)
|
||||||
|
break
|
||||||
|
case 'export':
|
||||||
|
await connectionStore.exportConnections()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -23,14 +47,14 @@ const filterPattern = ref('')
|
||||||
:button-class="['nav-pane-func-btn']"
|
:button-class="['nav-pane-func-btn']"
|
||||||
:icon="AddLink"
|
:icon="AddLink"
|
||||||
size="20"
|
size="20"
|
||||||
stroke-width="4"
|
:stroke-width="3.5"
|
||||||
t-tooltip="interface.new_conn"
|
t-tooltip="interface.new_conn"
|
||||||
@click="dialogStore.openNewDialog()" />
|
@click="dialogStore.openNewDialog()" />
|
||||||
<icon-button
|
<icon-button
|
||||||
:button-class="['nav-pane-func-btn']"
|
:button-class="['nav-pane-func-btn']"
|
||||||
:icon="AddGroup"
|
:icon="AddGroup"
|
||||||
size="20"
|
size="20"
|
||||||
stroke-width="4"
|
:stroke-width="3.5"
|
||||||
t-tooltip="interface.new_group"
|
t-tooltip="interface.new_group"
|
||||||
@click="dialogStore.openNewGroupDialog()" />
|
@click="dialogStore.openNewGroupDialog()" />
|
||||||
<n-divider vertical />
|
<n-divider vertical />
|
||||||
|
@ -39,6 +63,15 @@ const filterPattern = ref('')
|
||||||
<n-icon :component="Filter" size="20" />
|
<n-icon :component="Filter" size="20" />
|
||||||
</template>
|
</template>
|
||||||
</n-input>
|
</n-input>
|
||||||
|
<n-dropdown
|
||||||
|
:options="moreOptions"
|
||||||
|
:render-icon="({ icon }) => render.renderIcon(icon, { strokeWidth: 3.5 })"
|
||||||
|
:render-label="({ label }) => $t(label)"
|
||||||
|
placement="top-end"
|
||||||
|
style="min-width: 130px"
|
||||||
|
@select="onSelectOptions">
|
||||||
|
<icon-button :button-class="['nav-pane-func-btn']" :icon="More" :stroke-width="3.5" size="20" />
|
||||||
|
</n-dropdown>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -74,6 +74,8 @@
|
||||||
"edit_conn_group": "Edit Group",
|
"edit_conn_group": "Edit Group",
|
||||||
"rename_conn_group": "Rename Group",
|
"rename_conn_group": "Rename Group",
|
||||||
"remove_conn_group": "Delete Group",
|
"remove_conn_group": "Delete Group",
|
||||||
|
"import_conn": "Import Connections...",
|
||||||
|
"export_conn": "Export Connections...",
|
||||||
"ttl": "TTL",
|
"ttl": "TTL",
|
||||||
"forever": "Forever",
|
"forever": "Forever",
|
||||||
"rename_key": "Rename Key",
|
"rename_key": "Rename Key",
|
||||||
|
@ -199,6 +201,11 @@
|
||||||
"load_size": "Size of Keys Per Load",
|
"load_size": "Size of Keys Per Load",
|
||||||
"mark_color": "Mark Color"
|
"mark_color": "Mark Color"
|
||||||
},
|
},
|
||||||
|
"alias": {
|
||||||
|
"title": "Database Alias",
|
||||||
|
"db": "Input Database Index",
|
||||||
|
"value": "Input Database Alias"
|
||||||
|
},
|
||||||
"ssl": {
|
"ssl": {
|
||||||
"title": "SSL/TLS",
|
"title": "SSL/TLS",
|
||||||
"enable": "Enable SSL/TLS",
|
"enable": "Enable SSL/TLS",
|
||||||
|
@ -359,8 +366,7 @@
|
||||||
"connected_clients": "Clients",
|
"connected_clients": "Clients",
|
||||||
"total_keys": "Keys",
|
"total_keys": "Keys",
|
||||||
"memory_used": "Memory",
|
"memory_used": "Memory",
|
||||||
"all_info": "Information",
|
"server_info": "Server Info",
|
||||||
"env_info": "Environment",
|
|
||||||
"activity_status": "Activity",
|
"activity_status": "Activity",
|
||||||
"act_cmd": "Commands Per Second",
|
"act_cmd": "Commands Per Second",
|
||||||
"act_network_input": "Network Input",
|
"act_network_input": "Network Input",
|
||||||
|
|
|
@ -74,6 +74,8 @@
|
||||||
"edit_conn_group": "编辑分组",
|
"edit_conn_group": "编辑分组",
|
||||||
"rename_conn_group": "重命名分组",
|
"rename_conn_group": "重命名分组",
|
||||||
"remove_conn_group": "删除分组",
|
"remove_conn_group": "删除分组",
|
||||||
|
"import_conn": "导入连接...",
|
||||||
|
"export_conn": "导出连接...",
|
||||||
"ttl": "TTL",
|
"ttl": "TTL",
|
||||||
"forever": "永久",
|
"forever": "永久",
|
||||||
"rename_key": "重命名键",
|
"rename_key": "重命名键",
|
||||||
|
@ -199,6 +201,11 @@
|
||||||
"load_size": "单次加载键数量",
|
"load_size": "单次加载键数量",
|
||||||
"mark_color": "标记颜色"
|
"mark_color": "标记颜色"
|
||||||
},
|
},
|
||||||
|
"alias": {
|
||||||
|
"title": "数据库别名",
|
||||||
|
"db": "输入数据库索引",
|
||||||
|
"value": "输入别名"
|
||||||
|
},
|
||||||
"ssl": {
|
"ssl": {
|
||||||
"title": "SSL/TLS",
|
"title": "SSL/TLS",
|
||||||
"enable": "启用SSL",
|
"enable": "启用SSL",
|
||||||
|
@ -359,8 +366,7 @@
|
||||||
"connected_clients": "已连客户端",
|
"connected_clients": "已连客户端",
|
||||||
"total_keys": "键总数",
|
"total_keys": "键总数",
|
||||||
"memory_used": "内存使用",
|
"memory_used": "内存使用",
|
||||||
"all_info": "全部信息",
|
"server_info": "状态信息",
|
||||||
"env_info": "运行环境",
|
|
||||||
"activity_status": "活动状态",
|
"activity_status": "活动状态",
|
||||||
"act_cmd": "命令执行数/秒",
|
"act_cmd": "命令执行数/秒",
|
||||||
"act_network_input": "网络输入",
|
"act_network_input": "网络输入",
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
* redis database item
|
* redis database item
|
||||||
*/
|
*/
|
||||||
export class RedisDatabaseItem {
|
export class RedisDatabaseItem {
|
||||||
constructor({ db = 0, keyCount = 0, maxKeys = 0 }) {
|
constructor({ db = 0, alias = '', keyCount = 0, maxKeys = 0 }) {
|
||||||
this.db = db
|
this.db = db
|
||||||
|
this.alias = alias
|
||||||
this.keyCount = keyCount
|
this.keyCount = keyCount
|
||||||
this.maxKeys = maxKeys
|
this.maxKeys = maxKeys
|
||||||
}
|
}
|
||||||
|
|
|
@ -252,6 +252,7 @@ const useBrowserStore = defineStore('browser', {
|
||||||
for (const dbItem of db) {
|
for (const dbItem of db) {
|
||||||
databases[dbItem.index] = new RedisDatabaseItem({
|
databases[dbItem.index] = new RedisDatabaseItem({
|
||||||
db: dbItem.index,
|
db: dbItem.index,
|
||||||
|
alias: dbItem.alias,
|
||||||
maxKeys: dbItem.maxKeys,
|
maxKeys: dbItem.maxKeys,
|
||||||
})
|
})
|
||||||
if (dbItem.index === lastDB) {
|
if (dbItem.index === lastDB) {
|
||||||
|
|
|
@ -4,16 +4,20 @@ import {
|
||||||
CreateGroup,
|
CreateGroup,
|
||||||
DeleteConnection,
|
DeleteConnection,
|
||||||
DeleteGroup,
|
DeleteGroup,
|
||||||
|
ExportConnections,
|
||||||
GetConnection,
|
GetConnection,
|
||||||
|
ImportConnections,
|
||||||
ListConnection,
|
ListConnection,
|
||||||
RenameGroup,
|
RenameGroup,
|
||||||
SaveConnection,
|
SaveConnection,
|
||||||
SaveLastDB,
|
SaveLastDB,
|
||||||
|
SaveRefreshInterval,
|
||||||
SaveSortedConnection,
|
SaveSortedConnection,
|
||||||
} from 'wailsjs/go/services/connectionService.js'
|
} from 'wailsjs/go/services/connectionService.js'
|
||||||
import { ConnectionType } from '@/consts/connection_type.js'
|
import { ConnectionType } from '@/consts/connection_type.js'
|
||||||
import { KeyViewType } from '@/consts/key_view_type.js'
|
import { KeyViewType } from '@/consts/key_view_type.js'
|
||||||
import useBrowserStore from 'stores/browser.js'
|
import useBrowserStore from 'stores/browser.js'
|
||||||
|
import { i18nGlobal } from '@/utils/i18n.js'
|
||||||
|
|
||||||
const useConnectionStore = defineStore('connections', {
|
const useConnectionStore = defineStore('connections', {
|
||||||
/**
|
/**
|
||||||
|
@ -31,6 +35,7 @@ const useConnectionStore = defineStore('connections', {
|
||||||
* @property {string} defaultFilter
|
* @property {string} defaultFilter
|
||||||
* @property {string} keySeparator
|
* @property {string} keySeparator
|
||||||
* @property {string} markColor
|
* @property {string} markColor
|
||||||
|
* @property {number} refreshInterval
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -63,7 +68,7 @@ const useConnectionStore = defineStore('connections', {
|
||||||
const conns = []
|
const conns = []
|
||||||
const groups = []
|
const groups = []
|
||||||
const profiles = {}
|
const profiles = {}
|
||||||
const { data = [{ groupName: '', connections: [] }] } = await ListConnection()
|
const { data = [{ groupName: '', connections: [], refreshInterval: 5 }] } = await ListConnection()
|
||||||
for (const conn of data) {
|
for (const conn of data) {
|
||||||
if (conn.type !== 'group') {
|
if (conn.type !== 'group') {
|
||||||
// top level
|
// top level
|
||||||
|
@ -79,6 +84,7 @@ const useConnectionStore = defineStore('connections', {
|
||||||
defaultFilter: conn.defaultFilter,
|
defaultFilter: conn.defaultFilter,
|
||||||
keySeparator: conn.keySeparator,
|
keySeparator: conn.keySeparator,
|
||||||
markColor: conn.markColor,
|
markColor: conn.markColor,
|
||||||
|
refreshInterval: conn.refreshInterval,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// custom group
|
// custom group
|
||||||
|
@ -99,6 +105,7 @@ const useConnectionStore = defineStore('connections', {
|
||||||
defaultFilter: item.defaultFilter,
|
defaultFilter: item.defaultFilter,
|
||||||
keySeparator: item.keySeparator,
|
keySeparator: item.keySeparator,
|
||||||
markColor: item.markColor,
|
markColor: item.markColor,
|
||||||
|
refreshInterval: conn.refreshInterval,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
conns.push({
|
conns.push({
|
||||||
|
@ -157,6 +164,7 @@ const useConnectionStore = defineStore('connections', {
|
||||||
keyView: KeyViewType.Tree,
|
keyView: KeyViewType.Tree,
|
||||||
loadSize: 10000,
|
loadSize: 10000,
|
||||||
markColor: '',
|
markColor: '',
|
||||||
|
alias: {},
|
||||||
ssl: {
|
ssl: {
|
||||||
enable: false,
|
enable: false,
|
||||||
allowInsecure: true,
|
allowInsecure: true,
|
||||||
|
@ -369,6 +377,60 @@ const useConnectionStore = defineStore('connections', {
|
||||||
const { keySeparator = ':' } = this.serverProfile[name] || {}
|
const { keySeparator = ':' } = this.serverProfile[name] || {}
|
||||||
return keySeparator
|
return keySeparator
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get default status refresh interval by server name
|
||||||
|
* @param {string} name
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
getRefreshInterval(name) {
|
||||||
|
const { refreshInterval = 5 } = this.serverProfile[name] || {}
|
||||||
|
return refreshInterval
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set and save default refresh interval
|
||||||
|
* @param {string} name
|
||||||
|
* @param {number} interval
|
||||||
|
* @return {Promise<{success: boolean}|{msg: undefined, success: boolean}>}
|
||||||
|
*/
|
||||||
|
async saveRefreshInterval(name, interval) {
|
||||||
|
const profile = this.serverProfile[name] || {}
|
||||||
|
profile.refreshInterval = interval
|
||||||
|
const { success, msg } = await SaveRefreshInterval(name, interval)
|
||||||
|
if (!success) {
|
||||||
|
return { success: false, msg }
|
||||||
|
}
|
||||||
|
return { success: true }
|
||||||
|
},
|
||||||
|
|
||||||
|
async exportConnections() {
|
||||||
|
const {
|
||||||
|
success,
|
||||||
|
msg,
|
||||||
|
data: { path = '' },
|
||||||
|
} = await ExportConnections()
|
||||||
|
if (!success) {
|
||||||
|
if (!isEmpty(msg)) {
|
||||||
|
$message.error(msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$message.success(i18nGlobal.t('dialogue.handle_succ'))
|
||||||
|
},
|
||||||
|
|
||||||
|
async importConnections() {
|
||||||
|
const { success, msg } = await ImportConnections()
|
||||||
|
if (!success) {
|
||||||
|
if (!isEmpty(msg)) {
|
||||||
|
$message.error(msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$message.success(i18nGlobal.t('dialogue.handle_succ'))
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -277,10 +277,6 @@ const usePreferencesStore = defineStore('preferences', {
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
|
|
||||||
setAsideWidth(width) {
|
|
||||||
this.behavior.asideWidth = Math.max(width, 300)
|
|
||||||
},
|
|
||||||
|
|
||||||
async checkForUpdate(manual = false) {
|
async checkForUpdate(manual = false) {
|
||||||
let msgRef = null
|
let msgRef = null
|
||||||
if (manual) {
|
if (manual) {
|
||||||
|
|
|
@ -71,6 +71,9 @@ export const themeOverrides = {
|
||||||
optionTextColorHover: '#FFF',
|
optionTextColorHover: '#FFF',
|
||||||
optionHeightMedium: '28px',
|
optionHeightMedium: '28px',
|
||||||
},
|
},
|
||||||
|
Divider: {
|
||||||
|
color: '#AAAAAB',
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue