From f6a4ab8de0540102baa4828e04eb400a2ddd95f0 Mon Sep 17 00:00:00 2001 From: tiny-craft <137850705+tiny-craft@users.noreply.github.com> Date: Sun, 2 Jul 2023 16:52:07 +0800 Subject: [PATCH] Add common confirm dialog function Add connection group handle logic Refactor local connection profile struct --- backend/services/connection_service.go | 48 +++- backend/storage/connections.go | 262 +++++++++++------- backend/types/connection.go | 30 +- frontend/src/App.vue | 2 + .../src/components/content/ContentPane.vue | 22 +- .../components/dialogs/ConnectionDialog.vue | 12 +- .../src/components/dialogs/GroupDialog.vue | 97 +++++++ .../src/components/sidebar/ConnectionPane.vue | 8 +- .../src/components/sidebar/ConnectionTree.vue | 49 ++-- .../src/components/sidebar/DatabaseTree.vue | 25 +- frontend/src/langs/en.json | 7 +- frontend/src/langs/zh-cn.json | 9 +- frontend/src/stores/connections.js | 105 +++++-- frontend/src/stores/dialog.js | 18 ++ frontend/src/stores/tab.js | 2 +- frontend/src/utils/confirm_dialog.js | 28 ++ 16 files changed, 506 insertions(+), 218 deletions(-) create mode 100644 frontend/src/components/dialogs/GroupDialog.vue create mode 100644 frontend/src/utils/confirm_dialog.js diff --git a/backend/services/connection_service.go b/backend/services/connection_service.go index fd5771c..e5d38e7 100644 --- a/backend/services/connection_service.go +++ b/backend/services/connection_service.go @@ -92,7 +92,7 @@ func (c *connectionService) GetConnection(name string) (resp types.JSResp) { } // SaveConnection save connection config to local profile -func (c *connectionService) SaveConnection(name string, param types.Connection) (resp types.JSResp) { +func (c *connectionService) SaveConnection(name string, param types.ConnectionConfig) (resp types.JSResp) { var err error if len(name) > 0 { // update connection @@ -119,6 +119,39 @@ func (c *connectionService) RemoveConnection(name string) (resp types.JSResp) { return } +// CreateGroup create new group +func (c *connectionService) CreateGroup(name string) (resp types.JSResp) { + err := c.conns.CreateGroup(name) + if err != nil { + resp.Msg = err.Error() + return + } + resp.Success = true + return +} + +// RenameGroup rename group +func (c *connectionService) RenameGroup(name, newName string) (resp types.JSResp) { + err := c.conns.RenameGroup(name, newName) + if err != nil { + resp.Msg = err.Error() + return + } + resp.Success = true + return +} + +// RemoveGroup remove group by name +func (c *connectionService) RemoveGroup(name string, includeConn bool) (resp types.JSResp) { + err := c.conns.RemoveGroup(name, includeConn) + if err != nil { + resp.Msg = err.Error() + return + } + resp.Success = true + return +} + // OpenConnection open redis server connection func (c *connectionService) OpenConnection(name string) (resp types.JSResp) { rdb, ctx, err := c.getRedisClient(name, 0) @@ -195,18 +228,9 @@ func (c *connectionService) getRedisClient(connName string, db int) (*redis.Clie if ok { rdb, ctx = item.rdb, item.ctx } else { - connGroups := c.conns.GetConnections() - var selConn *types.Connection - for _, connGroup := range connGroups { - for _, conn := range connGroup.Connections { - if conn.Name == connName { - selConn = &conn - break - } - } - } + selConn := c.conns.GetConnection(connName) if selConn == nil { - return nil, nil, errors.New("no match connection connName") + return nil, nil, fmt.Errorf("no match connection \"%s\"", connName) } rdb = redis.NewClient(&redis.Options{ diff --git a/backend/storage/connections.go b/backend/storage/connections.go index ceda763..b67dae3 100644 --- a/backend/storage/connections.go +++ b/backend/storage/connections.go @@ -5,7 +5,6 @@ import ( "gopkg.in/yaml.v3" "sync" "tinyrdm/backend/types" - sliceutil "tinyrdm/backend/utils/slice" ) type ConnectionsStorage struct { @@ -19,17 +18,12 @@ func NewConnections() *ConnectionsStorage { } } -func (c *ConnectionsStorage) defaultConnections() []types.ConnectionGroup { - return []types.ConnectionGroup{ - { - GroupName: "", - Connections: []types.Connection{}, - }, - } +func (c *ConnectionsStorage) defaultConnections() types.Connections { + return types.Connections{} } -func (c *ConnectionsStorage) defaultConnectionItem() types.Connection { - return types.Connection{ +func (c *ConnectionsStorage) defaultConnectionItem() types.ConnectionConfig { + return types.ConnectionConfig{ Name: "", Addr: "127.0.0.1", Port: 6379, @@ -43,7 +37,7 @@ func (c *ConnectionsStorage) defaultConnectionItem() types.Connection { } } -func (c *ConnectionsStorage) getConnections() (ret []types.ConnectionGroup) { +func (c *ConnectionsStorage) getConnections() (ret types.Connections) { b, err := c.storage.Load() if err != nil { ret = c.defaultConnections() @@ -66,15 +60,19 @@ func (c *ConnectionsStorage) getConnections() (ret []types.ConnectionGroup) { } // GetConnections get all store connections from local -func (c *ConnectionsStorage) GetConnections() (ret []types.ConnectionGroup) { +func (c *ConnectionsStorage) GetConnections() (ret types.Connections) { return c.getConnections() } // GetConnectionsFlat get all store connections from local flat(exclude group level) -func (c *ConnectionsStorage) GetConnectionsFlat() (ret []types.Connection) { +func (c *ConnectionsStorage) GetConnectionsFlat() (ret types.Connections) { conns := c.getConnections() - for _, group := range conns { - ret = append(ret, group.Connections...) + for _, conn := range conns { + if conn.Type == "group" { + ret = append(ret, conn.Connections...) + } else { + ret = append(ret, conn) + } } return } @@ -82,18 +80,40 @@ func (c *ConnectionsStorage) GetConnectionsFlat() (ret []types.Connection) { // GetConnection get connection by name func (c *ConnectionsStorage) GetConnection(name string) *types.Connection { conns := c.getConnections() - for _, group := range conns { - for _, conn := range group.Connections { - if conn.Name == name { - conn.Group = group.GroupName - return &conn + + var findConn func(string, string, types.Connections) *types.Connection + findConn = func(name, groupName string, conns types.Connections) *types.Connection { + for i, conn := range conns { + if conn.Type != "group" { + if conn.Name == name { + conns[i].Group = groupName + return &conns[i] + } + } else { + if ret := findConn(name, conn.Name, conn.Connections); ret != nil { + return ret + } } } + return nil + } + + return findConn(name, "", conns) +} + +// GetGroup get connection group by name +func (c *ConnectionsStorage) GetGroup(name string) *types.Connection { + conns := c.getConnections() + + for i, conn := range conns { + if conn.Type == "group" && conn.Name == name { + return &conns[i] + } } return nil } -func (c *ConnectionsStorage) saveConnections(conns []types.ConnectionGroup) error { +func (c *ConnectionsStorage) saveConnections(conns types.Connections) error { b, err := yaml.Marshal(&conns) if err != nil { return err @@ -105,7 +125,7 @@ func (c *ConnectionsStorage) saveConnections(conns []types.ConnectionGroup) erro } // CreateConnection create new connection -func (c *ConnectionsStorage) CreateConnection(param types.Connection) error { +func (c *ConnectionsStorage) CreateConnection(param types.ConnectionConfig) error { c.mutex.Lock() defer c.mutex.Unlock() @@ -115,72 +135,75 @@ func (c *ConnectionsStorage) CreateConnection(param types.Connection) error { } conns := c.getConnections() - groupIndex, existsGroup := sliceutil.Find(conns, func(i int) bool { - return conns[i].GroupName == param.Group - }) - if !existsGroup { - // no group matched, create new group - group := types.ConnectionGroup{ - GroupName: param.Group, - Connections: []types.Connection{param}, + var group *types.Connection + if len(param.Group) > 0 { + for i, conn := range conns { + if conn.Type == "group" && conn.Name == param.Group { + group = &conns[i] + break + } } - conns = append(conns, group) + } + if group != nil { + group.Connections = append(group.Connections, types.Connection{ + ConnectionConfig: param, + }) } else { - conns[groupIndex].Connections = append(conns[groupIndex].Connections, param) + if len(param.Group) > 0 { + // no group matched, create new group + conns = append(conns, types.Connection{ + Type: "group", + Connections: types.Connections{ + types.Connection{ + ConnectionConfig: param, + }, + }, + }) + } else { + conns = append(conns, types.Connection{ + ConnectionConfig: param, + }) + } } return c.saveConnections(conns) } // UpdateConnection update existing connection by name -func (c *ConnectionsStorage) UpdateConnection(name string, param types.Connection) error { +func (c *ConnectionsStorage) UpdateConnection(name string, param types.ConnectionConfig) error { c.mutex.Lock() defer c.mutex.Unlock() + var updated bool conns := c.getConnections() - groupIndex := -1 - connIndex := -1 - // find out edit connection - for i, group := range conns { - for j, conn := range group.Connections { - // check conflict connection name - if conn.Name == name { - // different group name, should move to new group - // remove from current group first - if group.GroupName != param.Group { - conns[i].Connections = append(conns[i].Connections[:j], conns[i].Connections[j+1:]...) - - // find new group index - groupIndex, _ = sliceutil.Find(conns, func(i int) bool { - return conns[i].GroupName == param.Group - }) - } else { - groupIndex = i - connIndex = j + for i, conn := range conns { + if conn.Name == name { + conns[i] = types.Connection{ + ConnectionConfig: param, + } + updated = true + } else if conn.Type == "group" { + for j, conn2 := range conn.Connections { + if conn2.Name == name { + conns[i].Connections[j] = types.Connection{ + ConnectionConfig: param, + } + updated = true + break } - break } } + + if updated { + break + } } - if groupIndex >= 0 { - // group exists - if connIndex >= 0 { - // connection exists - conns[groupIndex].Connections[connIndex] = param - } else { - // new connection - conns[groupIndex].Connections = append(conns[groupIndex].Connections, param) - } - } else { - // new group - group := types.ConnectionGroup{ - GroupName: param.Group, - Connections: []types.Connection{param}, - } - conns = append(conns, group) + if updated { + return c.saveConnections(conns) } - return c.saveConnections(conns) + + return errors.New("connection not found") } // RemoveConnection remove special connection @@ -189,55 +212,82 @@ func (c *ConnectionsStorage) RemoveConnection(name string) error { defer c.mutex.Unlock() conns := c.getConnections() - for i, connGroup := range conns { - for j, conn := range connGroup.Connections { - if conn.Name == name { - connList := conns[i].Connections - connList = append(connList[:j], connList[j+1:]...) - conns[i].Connections = connList - return c.saveConnections(conns) + var updated bool + for i, conn := range conns { + if conn.Type == "group" { + for j, subConn := range conn.Connections { + if subConn.Name == name { + conns[i].Connections = append(conns[i].Connections[:j], conns[i].Connections[j+1:]...) + updated = true + break + } } + } else if conn.Name == name { + conns = append(conns[:i], conns[i+1:]...) + updated = true + break + } + if updated { + break } } - - return errors.New("no match connection") -} - -// UpsertGroup update or insert a group -// When want to create group only, set group == param.name -func (c *ConnectionsStorage) UpsertGroup(group string, param types.ConnectionGroup) error { - c.mutex.Lock() - defer c.mutex.Unlock() - - conns := c.getConnections() - for i, connGroup := range conns { - if connGroup.GroupName == group { - conns[i].GroupName = param.GroupName - return c.saveConnections(conns) - } + if !updated { + return errors.New("no match connection") } - - // No match group, create one - connGroup := types.ConnectionGroup{ - GroupName: param.GroupName, - Connections: []types.Connection{}, - } - conns = append(conns, connGroup) return c.saveConnections(conns) } -// RemoveGroup remove special group, include all connections under it -func (c *ConnectionsStorage) RemoveGroup(group string) error { +// CreateGroup create new group +func (c *ConnectionsStorage) CreateGroup(name string) error { c.mutex.Lock() defer c.mutex.Unlock() conns := c.getConnections() - for i, connGroup := range conns { - if connGroup.GroupName == group { - conns = append(conns[:i], conns[i+1:]...) + for _, conn := range conns { + if conn.Type == "group" && conn.Name == name { + return errors.New("duplicated group name") + } + } + + conns = append(conns, types.Connection{ + ConnectionConfig: types.ConnectionConfig{ + Name: name, + }, + Type: "group", + }) + return c.saveConnections(conns) +} + +// RenameGroup rename group +func (c *ConnectionsStorage) RenameGroup(name, newName string) error { + c.mutex.Lock() + defer c.mutex.Unlock() + + conns := c.getConnections() + for i, conn := range conns { + if conn.Type == "group" && conn.Name == name { + conns[i].Name = newName return c.saveConnections(conns) } } - return errors.New("no match group") + return errors.New("group not found") +} + +// RemoveGroup remove special group, include all connections under it +func (c *ConnectionsStorage) RemoveGroup(group string, includeConnection bool) error { + c.mutex.Lock() + defer c.mutex.Unlock() + + conns := c.getConnections() + for i, conn := range conns { + if conn.Type == "group" && conn.Name == group { + conns = append(conns[:i], conns[i+1:]...) + if includeConnection { + conns = append(conns, conn.Connections...) + } + return c.saveConnections(conns) + } + } + return errors.New("group not found") } diff --git a/backend/types/connection.go b/backend/types/connection.go index 52fb703..06cdedd 100644 --- a/backend/types/connection.go +++ b/backend/types/connection.go @@ -2,20 +2,28 @@ package types type ConnectionCategory int -type Connection struct { - Group string `json:"group" yaml:"-"` +type ConnectionConfig struct { Name string `json:"name" yaml:"name"` - Addr string `json:"addr" yaml:"addr"` - Port int `json:"port" yaml:"port"` - Username string `json:"username" yaml:"username"` - Password string `json:"password" yaml:"password"` - DefaultFilter string `json:"defaultFilter" yaml:"default_filter"` - KeySeparator string `json:"keySeparator" yaml:"key_separator"` - ConnTimeout int `json:"connTimeout" yaml:"conn_timeout"` - ExecTimeout int `json:"execTimeout" yaml:"exec_timeout"` - MarkColor string `json:"markColor" yaml:"mark_color"` + Group string `json:"group" yaml:"-"` + 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"` + DefaultFilter string `json:"defaultFilter,omitempty" yaml:"default_filter,omitempty"` + KeySeparator string `json:"keySeparator,omitempty" yaml:"key_separator,omitempty"` + ConnTimeout int `json:"connTimeout,omitempty" yaml:"conn_timeout,omitempty"` + ExecTimeout int `json:"execTimeout,omitempty" yaml:"exec_timeout,omitempty"` + MarkColor string `json:"markColor,omitempty" yaml:"mark_color,omitempty"` } +type Connection struct { + ConnectionConfig `json:",inline" yaml:",inline"` + Type string `json:"type,omitempty" yaml:"type,omitempty"` + Connections []Connection `json:"connections,omitempty" yaml:"connections,omitempty"` +} + +type Connections []Connection + type ConnectionGroup struct { GroupName string `json:"groupName" yaml:"group_name"` Connections []Connection `json:"connections" yaml:"connections"` diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 7dd05f6..6716a6d 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -9,6 +9,7 @@ import json from 'highlight.js/lib/languages/json' import plaintext from 'highlight.js/lib/languages/plaintext' import AddFieldsDialog from './components/dialogs/AddFieldsDialog.vue' import AppContent from './AppContent.vue' +import GroupDialog from './components/dialogs/GroupDialog.vue' hljs.registerLanguage('json', json) hljs.registerLanguage('plaintext', plaintext) @@ -41,6 +42,7 @@ const themeOverrides = { + diff --git a/frontend/src/components/content/ContentPane.vue b/frontend/src/components/content/ContentPane.vue index 0977cef..8a3c7d3 100644 --- a/frontend/src/components/content/ContentPane.vue +++ b/frontend/src/components/content/ContentPane.vue @@ -11,6 +11,7 @@ import useTabStore from '../../stores/tab.js' import { useDialog } from 'naive-ui' import useConnectionStore from '../../stores/connections.js' import { useI18n } from 'vue-i18n' +import { useConfirmDialog } from '../../utils/confirm_dialog.js' const valueComponents = { [types.STRING]: ContentValueString, @@ -58,22 +59,13 @@ const onAddTab = () => { } const i18n = useI18n() +const confirmDialog = useConfirmDialog() const onCloseTab = (tabIndex) => { - dialog.warning({ - title: i18n.t('close_confirm_title'), - content: i18n.t('close_confirm'), - positiveText: i18n.t('confirm'), - negativeText: i18n.t('cancel'), - closable: false, - closeOnEsc: false, - maskClosable: false, - transformOrigin: 'center', - onPositiveClick: () => { - const tab = get(tabStore.tabs, tabIndex) - if (tab != null) { - connectionStore.closeConnection(tab.name) - } - }, + confirmDialog.warning(i18n.t('close_confirm'), () => { + const tab = get(tabStore.tabs, tabIndex) + if (tab != null) { + connectionStore.closeConnection(tab.name) + } }) } diff --git a/frontend/src/components/dialogs/ConnectionDialog.vue b/frontend/src/components/dialogs/ConnectionDialog.vue index 5b3e2d2..36f7cf8 100644 --- a/frontend/src/components/dialogs/ConnectionDialog.vue +++ b/frontend/src/components/dialogs/ConnectionDialog.vue @@ -85,8 +85,8 @@ const onSaveConnection = async () => { return } - message.success(i18n.t('new_conn_succ')) - dialogStore.closeNewDialog() + message.success(i18n.t('handle_succ')) + onClose() } const resetForm = () => { @@ -132,7 +132,11 @@ const onTestConnection = async () => { } const onClose = () => { - dialogStore.closeNewDialog() + if (isEditMode.value) { + dialogStore.closeEditDialog() + } else { + dialogStore.closeNewDialog() + } } @@ -162,7 +166,7 @@ const onClose = () => { - + diff --git a/frontend/src/components/dialogs/GroupDialog.vue b/frontend/src/components/dialogs/GroupDialog.vue new file mode 100644 index 0000000..82a891e --- /dev/null +++ b/frontend/src/components/dialogs/GroupDialog.vue @@ -0,0 +1,97 @@ + + + + + diff --git a/frontend/src/components/sidebar/ConnectionPane.vue b/frontend/src/components/sidebar/ConnectionPane.vue index 557366e..f5580f2 100644 --- a/frontend/src/components/sidebar/ConnectionPane.vue +++ b/frontend/src/components/sidebar/ConnectionPane.vue @@ -16,6 +16,10 @@ const connectionStore = useConnectionStore() const onSort = () => { dialogStore.openPreferencesDialog() } + +const onDisconnectAll = () => { + connectionStore.closeAllConnection() +}