1605 lines
54 KiB
JavaScript
1605 lines
54 KiB
JavaScript
import { defineStore } from 'pinia'
|
|
import {
|
|
endsWith,
|
|
find,
|
|
get,
|
|
isEmpty,
|
|
join,
|
|
map,
|
|
remove,
|
|
size,
|
|
slice,
|
|
sortedIndexBy,
|
|
split,
|
|
sumBy,
|
|
toUpper,
|
|
uniq,
|
|
} from 'lodash'
|
|
import {
|
|
AddHashField,
|
|
AddListItem,
|
|
AddStreamValue,
|
|
AddZSetValue,
|
|
CleanCmdHistory,
|
|
CloseConnection,
|
|
CreateGroup,
|
|
DeleteConnection,
|
|
DeleteGroup,
|
|
DeleteKey,
|
|
GetCmdHistory,
|
|
GetConnection,
|
|
GetKeyValue,
|
|
ListConnection,
|
|
OpenConnection,
|
|
OpenDatabase,
|
|
RemoveStreamValues,
|
|
RenameGroup,
|
|
RenameKey,
|
|
SaveConnection,
|
|
SaveSortedConnection,
|
|
ScanKeys,
|
|
ServerInfo,
|
|
SetHashValue,
|
|
SetKeyTTL,
|
|
SetKeyValue,
|
|
SetListItem,
|
|
SetSetItem,
|
|
UpdateSetItem,
|
|
UpdateZSetValue,
|
|
} from 'wailsjs/go/services/connectionService.js'
|
|
import { ConnectionType } from '@/consts/connection_type.js'
|
|
import useTabStore from './tab.js'
|
|
import { types } from '@/consts/support_redis_type.js'
|
|
import { decodeRedisKey, nativeRedisKey } from '@/utils/key_convert.js'
|
|
|
|
const useConnectionStore = defineStore('connections', {
|
|
/**
|
|
* @typedef {Object} ConnectionItem
|
|
* @property {string} key
|
|
* @property {string} label display label
|
|
* @property {string} name database name
|
|
* @property {number} type
|
|
* @property {boolean} cluster is cluster node
|
|
* @property {ConnectionItem[]} children
|
|
*/
|
|
|
|
/**
|
|
* @typedef {Object} DatabaseItem
|
|
* @property {string} key - tree node unique key
|
|
* @property {string} label
|
|
* @property {string} [name] - server name, type != ConnectionType.Group only
|
|
* @property {number} type
|
|
* @property {number} [db] - database index, type == ConnectionType.RedisDB only
|
|
* @property {string} [redisKey] - redis key, type == ConnectionType.RedisKey || type == ConnectionType.RedisValue only
|
|
* @property {string} [redisKeyCode] - redis key char code array, optional for redis key which contains binary data
|
|
* @property {number} [keys] - children key count
|
|
* @property {boolean} [isLeaf]
|
|
* @property {boolean} [opened] - redis db is opened, type == ConnectionType.RedisDB only
|
|
* @property {boolean} [expanded] - current node is expanded
|
|
* @property {DatabaseItem[]} [children]
|
|
*/
|
|
|
|
/**
|
|
* @typedef {Object} ConnectionState
|
|
* @property {string[]} groups
|
|
* @property {ConnectionItem[]} connections
|
|
* @property {Object} serverStats
|
|
* @property {Object.<string, ConnectionProfile>} serverProfile
|
|
* @property {Object.<string, string>} keyFilter key is 'server#db', 'server#-1' stores default filter pattern
|
|
* @property {Object.<string, string>} typeFilter key is 'server#db'
|
|
* @property {Object.<string, DatabaseItem[]>} databases
|
|
* @property {Object.<string, Map<string, DatabaseItem>>} nodeMap key format likes 'server#db', children key format likes 'key#type'
|
|
*/
|
|
|
|
/**
|
|
* @typedef {Object} HistoryItem
|
|
* @property {string} time
|
|
* @property {string} server
|
|
* @property {string} cmd
|
|
* @property {number} cost
|
|
*/
|
|
|
|
/**
|
|
* @typedef {Object} ConnectionProfile
|
|
* @property {string} defaultFilter
|
|
* @property {string} keySeparator
|
|
* @property {string} markColor
|
|
*/
|
|
|
|
/**
|
|
*
|
|
* @returns {ConnectionState}
|
|
*/
|
|
state: () => ({
|
|
groups: [], // all group name set
|
|
connections: [], // all connections
|
|
serverStats: {}, // current server status info
|
|
serverProfile: {}, // all server profile
|
|
keyFilter: {}, // all key filters in opened connections group by server+db
|
|
typeFilter: {}, // all key type filters in opened connections group by server+db
|
|
databases: {}, // all databases in opened connections group by server name
|
|
nodeMap: {}, // all nodes in opened connections group by server#db and type/key
|
|
}),
|
|
getters: {
|
|
anyConnectionOpened() {
|
|
return !isEmpty(this.databases)
|
|
},
|
|
},
|
|
actions: {
|
|
/**
|
|
* load all store connections struct from local profile
|
|
* @param {boolean} [force]
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async initConnections(force) {
|
|
if (!force && !isEmpty(this.connections)) {
|
|
return
|
|
}
|
|
const conns = []
|
|
const groups = []
|
|
const profiles = {}
|
|
const { data = [{ groupName: '', connections: [] }] } = await ListConnection()
|
|
for (const conn of data) {
|
|
if (conn.type !== 'group') {
|
|
// top level
|
|
conns.push({
|
|
key: '/' + conn.name,
|
|
label: conn.name,
|
|
name: conn.name,
|
|
type: ConnectionType.Server,
|
|
cluster: get(conn, 'cluster.enable', false),
|
|
// isLeaf: false,
|
|
})
|
|
profiles[conn.name] = {
|
|
defaultFilter: conn.defaultFilter,
|
|
keySeparator: conn.keySeparator,
|
|
markColor: conn.markColor,
|
|
}
|
|
} else {
|
|
// custom group
|
|
groups.push(conn.name)
|
|
const subConns = get(conn, 'connections', [])
|
|
const children = []
|
|
for (const item of subConns) {
|
|
const value = conn.name + '/' + item.name
|
|
children.push({
|
|
key: value,
|
|
label: item.name,
|
|
name: item.name,
|
|
type: ConnectionType.Server,
|
|
cluster: get(item, 'cluster.enable', false),
|
|
// isLeaf: false,
|
|
})
|
|
profiles[item.name] = {
|
|
defaultFilter: item.defaultFilter,
|
|
keySeparator: item.keySeparator,
|
|
markColor: item.markColor,
|
|
}
|
|
}
|
|
conns.push({
|
|
key: conn.name + '/',
|
|
label: conn.name,
|
|
type: ConnectionType.Group,
|
|
children,
|
|
})
|
|
}
|
|
this.setKeyFilter(conn.name, -1, conn.defaultFilter)
|
|
}
|
|
this.connections = conns
|
|
this.serverProfile = profiles
|
|
this.groups = uniq(groups)
|
|
},
|
|
|
|
/**
|
|
* get connection by name from local profile
|
|
* @param name
|
|
* @returns {Promise<ConnectionProfile|null>}
|
|
*/
|
|
async getConnectionProfile(name) {
|
|
try {
|
|
const { data, success } = await GetConnection(name)
|
|
if (success) {
|
|
this.serverProfile[name] = {
|
|
defaultFilter: data.defaultFilter,
|
|
keySeparator: data.keySeparator,
|
|
markColor: data.markColor,
|
|
}
|
|
return data
|
|
}
|
|
} finally {
|
|
}
|
|
return null
|
|
},
|
|
|
|
/**
|
|
* create a new default connection
|
|
* @param {string} [name]
|
|
* @returns {{}}
|
|
*/
|
|
newDefaultConnection(name) {
|
|
return {
|
|
group: '',
|
|
name: name || '',
|
|
addr: '127.0.0.1',
|
|
port: 6379,
|
|
username: '',
|
|
password: '',
|
|
defaultFilter: '*',
|
|
keySeparator: ':',
|
|
connTimeout: 60,
|
|
execTimeout: 60,
|
|
dbFilterType: 'none',
|
|
dbFilterList: [],
|
|
markColor: '',
|
|
ssh: {
|
|
enable: false,
|
|
addr: '',
|
|
port: 22,
|
|
loginType: 'pwd',
|
|
username: '',
|
|
password: '',
|
|
pkFile: '',
|
|
passphrase: '',
|
|
},
|
|
sentinel: {
|
|
enable: false,
|
|
master: 'mymaster',
|
|
username: '',
|
|
password: '',
|
|
},
|
|
cluster: {
|
|
enable: false,
|
|
},
|
|
}
|
|
},
|
|
|
|
mergeConnectionProfile(dest, src) {
|
|
const mergeObj = (destObj, srcObj) => {
|
|
for (const k in srcObj) {
|
|
const t = typeof srcObj[k]
|
|
if (t === 'string') {
|
|
destObj[k] = srcObj[k] || destObj[k] || ''
|
|
} else if (t === 'number') {
|
|
destObj[k] = srcObj[k] || destObj[k] || 0
|
|
} else if (t === 'object') {
|
|
mergeObj(destObj[k], srcObj[k] || {})
|
|
} else {
|
|
destObj[k] = srcObj[k]
|
|
}
|
|
}
|
|
return destObj
|
|
}
|
|
return mergeObj(dest, src)
|
|
},
|
|
|
|
/**
|
|
* get database server by name
|
|
* @param name
|
|
* @returns {ConnectionItem|null}
|
|
*/
|
|
getConnection(name) {
|
|
const conns = this.connections
|
|
for (let i = 0; i < conns.length; i++) {
|
|
if (conns[i].type === ConnectionType.Server && conns[i].key === name) {
|
|
return conns[i]
|
|
} else if (conns[i].type === ConnectionType.Group) {
|
|
const children = conns[i].children
|
|
for (let j = 0; j < children.length; j++) {
|
|
if (children[j].type === ConnectionType.Server && conns[i].key === name) {
|
|
return children[j]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return null
|
|
},
|
|
|
|
/**
|
|
* get database by server name and index
|
|
* @param {string} connName
|
|
* @param {number} db
|
|
* @return {{}|null}
|
|
*/
|
|
getDatabase(connName, db) {
|
|
const dbs = this.databases[connName]
|
|
if (dbs != null) {
|
|
const selDB = find(dbs, (item) => item.db === db)
|
|
if (selDB != null) {
|
|
return selDB
|
|
}
|
|
}
|
|
return null
|
|
},
|
|
|
|
/**
|
|
* create a new connection or update current connection profile
|
|
* @param {string} name set null if create a new connection
|
|
* @param {{}} param
|
|
* @returns {Promise<{success: boolean, [msg]: string}>}
|
|
*/
|
|
async saveConnection(name, param) {
|
|
const { success, msg } = await SaveConnection(name, param)
|
|
if (!success) {
|
|
return { success: false, msg }
|
|
}
|
|
|
|
// reload connection list
|
|
await this.initConnections(true)
|
|
return { success: true }
|
|
},
|
|
|
|
/**
|
|
* save connection after sort
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async saveConnectionSorted() {
|
|
const mapToList = (conns) => {
|
|
const list = []
|
|
for (const conn of conns) {
|
|
if (conn.type === ConnectionType.Group) {
|
|
const children = mapToList(conn.children)
|
|
list.push({
|
|
name: conn.label,
|
|
type: 'group',
|
|
connections: children,
|
|
})
|
|
} else if (conn.type === ConnectionType.Server) {
|
|
list.push({
|
|
name: conn.name,
|
|
})
|
|
}
|
|
}
|
|
return list
|
|
}
|
|
const s = mapToList(this.connections)
|
|
SaveSortedConnection(s)
|
|
},
|
|
|
|
/**
|
|
* check if connection is connected
|
|
* @param name
|
|
* @returns {boolean}
|
|
*/
|
|
isConnected(name) {
|
|
let dbs = get(this.databases, name, [])
|
|
return !isEmpty(dbs)
|
|
},
|
|
|
|
/**
|
|
* open connection
|
|
* @param {string} name
|
|
* @param {boolean} [reload]
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async openConnection(name, reload) {
|
|
if (this.isConnected(name)) {
|
|
if (reload !== true) {
|
|
return
|
|
} else {
|
|
// reload mode, try close connection first
|
|
await CloseConnection(name)
|
|
}
|
|
}
|
|
|
|
const { data, success, msg } = await OpenConnection(name)
|
|
if (!success) {
|
|
throw new Error(msg)
|
|
}
|
|
// append to db node to current connection
|
|
// const connNode = this.getConnection(name)
|
|
// if (connNode == null) {
|
|
// throw new Error('no such connection')
|
|
// }
|
|
const { db } = data
|
|
if (isEmpty(db)) {
|
|
throw new Error('no db loaded')
|
|
}
|
|
const dbs = []
|
|
for (let i = 0; i < db.length; i++) {
|
|
this._getNodeMap(name, i).clear()
|
|
dbs.push({
|
|
key: `${name}/${db[i].name}`,
|
|
label: db[i].name,
|
|
name: name,
|
|
keys: db[i].keys,
|
|
db: db[i].index,
|
|
type: ConnectionType.RedisDB,
|
|
isLeaf: false,
|
|
children: undefined,
|
|
})
|
|
}
|
|
this.databases[name] = dbs
|
|
},
|
|
|
|
/**
|
|
* close connection
|
|
* @param {string} name
|
|
* @returns {Promise<boolean>}
|
|
*/
|
|
async closeConnection(name) {
|
|
const { success, msg } = await CloseConnection(name)
|
|
if (!success) {
|
|
// throw new Error(msg)
|
|
return false
|
|
}
|
|
|
|
const dbs = this.databases[name]
|
|
if (!isEmpty(dbs)) {
|
|
for (const db of dbs) {
|
|
this.removeKeyFilter(name, db.db)
|
|
this._getNodeMap(name, db.db).clear()
|
|
}
|
|
}
|
|
this.removeKeyFilter(name, -1)
|
|
delete this.databases[name]
|
|
delete this.serverStats[name]
|
|
|
|
const tabStore = useTabStore()
|
|
tabStore.removeTabByName(name)
|
|
return true
|
|
},
|
|
|
|
/**
|
|
* close all connections
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async closeAllConnection() {
|
|
for (const name in this.databases) {
|
|
await CloseConnection(name)
|
|
}
|
|
|
|
this.databases = {}
|
|
this.serverStats = {}
|
|
const tabStore = useTabStore()
|
|
tabStore.removeAllTab()
|
|
},
|
|
|
|
/**
|
|
* remove connection
|
|
* @param name
|
|
* @returns {Promise<{success: boolean, [msg]: string}>}
|
|
*/
|
|
async deleteConnection(name) {
|
|
// close connection first
|
|
await this.closeConnection(name)
|
|
const { success, msg } = await DeleteConnection(name)
|
|
if (!success) {
|
|
return { success: false, msg }
|
|
}
|
|
await this.initConnections(true)
|
|
return { success: true }
|
|
},
|
|
|
|
/**
|
|
* create a connection group
|
|
* @param name
|
|
* @returns {Promise<{success: boolean, [msg]: string}>}
|
|
*/
|
|
async createGroup(name) {
|
|
const { success, msg } = await CreateGroup(name)
|
|
if (!success) {
|
|
return { success: false, msg }
|
|
}
|
|
await this.initConnections(true)
|
|
return { success: true }
|
|
},
|
|
|
|
/**
|
|
* rename connection group
|
|
* @param name
|
|
* @param newName
|
|
* @returns {Promise<{success: boolean, [msg]: string}>}
|
|
*/
|
|
async renameGroup(name, newName) {
|
|
if (name === newName) {
|
|
return { success: true }
|
|
}
|
|
const { success, msg } = await RenameGroup(name, newName)
|
|
if (!success) {
|
|
return { success: false, msg }
|
|
}
|
|
await this.initConnections(true)
|
|
return { success: true }
|
|
},
|
|
|
|
/**
|
|
* delete group by name
|
|
* @param {string} name
|
|
* @param {boolean} [includeConn]
|
|
* @returns {Promise<{success: boolean, [msg]: string}>}
|
|
*/
|
|
async deleteGroup(name, includeConn) {
|
|
const { success, msg } = await DeleteGroup(name, includeConn === true)
|
|
if (!success) {
|
|
return { success: false, msg }
|
|
}
|
|
await this.initConnections(true)
|
|
return { success: true }
|
|
},
|
|
|
|
/**
|
|
* open database and load all keys
|
|
* @param connName
|
|
* @param db
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async openDatabase(connName, db) {
|
|
const { match: filterPattern, type: keyType } = this.getKeyFilter(connName, db)
|
|
const { data, success, msg } = await OpenDatabase(connName, db, filterPattern, keyType)
|
|
if (!success) {
|
|
throw new Error(msg)
|
|
}
|
|
const { keys = [] } = data
|
|
const selDB = this.getDatabase(connName, db)
|
|
if (selDB == null) {
|
|
return
|
|
}
|
|
|
|
selDB.opened = true
|
|
if (isEmpty(keys)) {
|
|
selDB.children = []
|
|
} else {
|
|
// append db node to current connection's children
|
|
this._addKeyNodes(connName, db, keys)
|
|
}
|
|
this._tidyNode(connName, db)
|
|
},
|
|
|
|
/**
|
|
* reopen database
|
|
* @param connName
|
|
* @param db
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async reopenDatabase(connName, db) {
|
|
const selDB = this.getDatabase(connName, db)
|
|
if (selDB == null) {
|
|
return
|
|
}
|
|
selDB.children = undefined
|
|
selDB.isLeaf = false
|
|
|
|
this._getNodeMap(connName, db).clear()
|
|
},
|
|
|
|
/**
|
|
* close database
|
|
* @param connName
|
|
* @param db
|
|
*/
|
|
closeDatabase(connName, db) {
|
|
const selDB = this.getDatabase(connName, db)
|
|
if (selDB == null) {
|
|
return
|
|
}
|
|
delete selDB.children
|
|
selDB.isLeaf = false
|
|
selDB.opened = false
|
|
|
|
this._getNodeMap(connName, db).clear()
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param server
|
|
* @returns {Promise<{}>}
|
|
*/
|
|
async getServerInfo(server) {
|
|
try {
|
|
const { success, data } = await ServerInfo(server)
|
|
if (success) {
|
|
this.serverStats[server] = data
|
|
return data
|
|
}
|
|
} finally {
|
|
}
|
|
return {}
|
|
},
|
|
|
|
/**
|
|
* load redis key
|
|
* @param {string} server
|
|
* @param {number} db
|
|
* @param {string|number[]} [key] when key is null or blank, update tab to display normal content (blank content or server status)
|
|
* @param {string} [viewType]
|
|
*/
|
|
async loadKeyValue(server, db, key, viewType) {
|
|
try {
|
|
const tab = useTabStore()
|
|
if (!isEmpty(key)) {
|
|
const { data, success, msg } = await GetKeyValue(server, db, key, viewType)
|
|
if (success) {
|
|
const { type, ttl, value, size, viewAs } = data
|
|
const k = decodeRedisKey(key)
|
|
const binaryKey = k !== key
|
|
tab.upsertTab({
|
|
server,
|
|
db,
|
|
type,
|
|
ttl,
|
|
keyCode: binaryKey ? key : undefined,
|
|
key: k,
|
|
value,
|
|
size,
|
|
viewAs,
|
|
})
|
|
return
|
|
} else {
|
|
if (!isEmpty(msg)) {
|
|
$message.error('load key fail: ' + msg)
|
|
}
|
|
// its danger to delete "non-exists" key, just remove from tree view
|
|
await this.deleteKey(server, db, key, true)
|
|
// TODO: show key not found page or check exists on server first?
|
|
}
|
|
}
|
|
|
|
tab.upsertTab({
|
|
server,
|
|
db,
|
|
type: 'none',
|
|
ttl: -1,
|
|
key: null,
|
|
keyCode: null,
|
|
value: null,
|
|
size: 0,
|
|
})
|
|
} finally {
|
|
}
|
|
},
|
|
|
|
/**
|
|
* scan keys with prefix
|
|
* @param {string} connName
|
|
* @param {number} db
|
|
* @param {string} [prefix] full reload database if prefix is null
|
|
* @returns {Promise<{keys: string[]}>}
|
|
*/
|
|
async scanKeys(connName, db, prefix) {
|
|
const { data, success, msg } = await ScanKeys(connName, db, prefix || '*')
|
|
if (!success) {
|
|
throw new Error(msg)
|
|
}
|
|
const { keys = [] } = data
|
|
return { keys, success }
|
|
},
|
|
|
|
/**
|
|
* load keys with prefix
|
|
* @param {string} connName
|
|
* @param {number} db
|
|
* @param {string} [prefix]
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async loadKeys(connName, db, prefix) {
|
|
let scanPrefix = prefix
|
|
if (isEmpty(scanPrefix)) {
|
|
scanPrefix = '*'
|
|
} else {
|
|
const separator = this._getSeparator(connName)
|
|
if (!endsWith(prefix, separator + '*')) {
|
|
scanPrefix = prefix + separator + '*'
|
|
}
|
|
}
|
|
const { keys, success } = await this.scanKeys(connName, db, scanPrefix)
|
|
if (!success) {
|
|
return
|
|
}
|
|
|
|
// remove current keys below prefix
|
|
this._deleteKeyNode(connName, db, prefix, true)
|
|
this._addKeyNodes(connName, db, keys)
|
|
this._tidyNode(connName, db, prefix)
|
|
},
|
|
|
|
/**
|
|
* get custom separator of connection
|
|
* @param server
|
|
* @returns {string}
|
|
* @private
|
|
*/
|
|
_getSeparator(server) {
|
|
const { keySeparator } = this.serverProfile[server] || { keySeparator: ':' }
|
|
if (isEmpty(keySeparator)) {
|
|
return ':'
|
|
}
|
|
return keySeparator
|
|
},
|
|
|
|
/**
|
|
* get node map
|
|
* @param connName
|
|
* @param db
|
|
* @returns {Map<string, DatabaseItem>}
|
|
* @private
|
|
*/
|
|
_getNodeMap(connName, db) {
|
|
if (this.nodeMap[`${connName}#${db}`] == null) {
|
|
this.nodeMap[`${connName}#${db}`] = new Map()
|
|
}
|
|
// construct a tree node list, the format of item key likes 'server/db#type/key'
|
|
return this.nodeMap[`${connName}#${db}`]
|
|
},
|
|
|
|
/**
|
|
* remove keys in db
|
|
* @param {string} connName
|
|
* @param {number} db
|
|
* @param {Array<string|number[]>} keys
|
|
* @param {boolean} [sortInsert]
|
|
* @return {{success: boolean, newKey: number, newLayer: number, replaceKey: number}}
|
|
* @private
|
|
*/
|
|
_addKeyNodes(connName, db, keys, sortInsert) {
|
|
const result = { success: false, newLayer: 0, newKey: 0, replaceKey: 0 }
|
|
if (isEmpty(keys)) {
|
|
return result
|
|
}
|
|
const separator = this._getSeparator(connName)
|
|
const selDB = this.getDatabase(connName, db)
|
|
if (selDB == null) {
|
|
return result
|
|
}
|
|
|
|
if (selDB.children == null) {
|
|
selDB.children = []
|
|
}
|
|
const nodeMap = this._getNodeMap(connName, db)
|
|
const rootChildren = selDB.children
|
|
for (const key of keys) {
|
|
const k = decodeRedisKey(key)
|
|
const binaryKey = k !== key
|
|
const keyParts = binaryKey ? [nativeRedisKey(key)] : split(k, separator)
|
|
const len = size(keyParts)
|
|
const lastIdx = len - 1
|
|
let handlePath = ''
|
|
let children = rootChildren
|
|
for (let i = 0; i < len; i++) {
|
|
handlePath += keyParts[i]
|
|
if (i !== lastIdx) {
|
|
// layer
|
|
const nodeKey = `${ConnectionType.RedisKey}/${handlePath}`
|
|
let selectedNode = nodeMap.get(nodeKey)
|
|
if (selectedNode == null) {
|
|
selectedNode = {
|
|
key: `${connName}/db${db}#${nodeKey}`,
|
|
label: keyParts[i],
|
|
db,
|
|
keys: 0,
|
|
redisKey: handlePath,
|
|
type: ConnectionType.RedisKey,
|
|
isLeaf: false,
|
|
children: [],
|
|
}
|
|
nodeMap.set(nodeKey, selectedNode)
|
|
if (sortInsert) {
|
|
const index = sortedIndexBy(children, selectedNode, (elem) => {
|
|
return elem.key
|
|
})
|
|
children.splice(index, 0, selectedNode)
|
|
} else {
|
|
children.push(selectedNode)
|
|
}
|
|
result.newLayer += 1
|
|
}
|
|
children = selectedNode.children
|
|
handlePath += separator
|
|
} else {
|
|
// key
|
|
const nodeKey = `${ConnectionType.RedisValue}/${handlePath}`
|
|
const replaceKey = nodeMap.has(nodeKey)
|
|
const selectedNode = {
|
|
key: `${connName}/db${db}#${nodeKey}`,
|
|
label: binaryKey ? k : keyParts[i],
|
|
db,
|
|
keys: 0,
|
|
redisKey: handlePath,
|
|
redisKeyCode: binaryKey ? key : undefined,
|
|
type: ConnectionType.RedisValue,
|
|
isLeaf: true,
|
|
}
|
|
nodeMap.set(nodeKey, selectedNode)
|
|
if (!replaceKey) {
|
|
if (sortInsert) {
|
|
const index = sortedIndexBy(children, selectedNode, (elem) => {
|
|
return elem.key > selectedNode.key
|
|
})
|
|
children.splice(index, 0, selectedNode)
|
|
} else {
|
|
children.push(selectedNode)
|
|
}
|
|
result.newKey += 1
|
|
} else {
|
|
result.replaceKey += 1
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {DatabaseItem[]} nodeList
|
|
* @private
|
|
*/
|
|
_sortNodes(nodeList) {
|
|
if (nodeList == null) {
|
|
return
|
|
}
|
|
nodeList.sort((a, b) => {
|
|
return a.key > b.key ? 1 : -1
|
|
})
|
|
},
|
|
|
|
/**
|
|
* tidy node by key
|
|
* @param {string} connName
|
|
* @param {number} db
|
|
* @param {string} [key]
|
|
* @param {boolean} [skipResort]
|
|
* @private
|
|
*/
|
|
_tidyNode(connName, db, key, skipResort) {
|
|
const nodeMap = this._getNodeMap(connName, db)
|
|
const dbNode = this.getDatabase(connName, db) || {}
|
|
const separator = this._getSeparator(connName)
|
|
const keyParts = split(key, separator)
|
|
const totalParts = size(keyParts)
|
|
let node
|
|
// find last exists ancestor key
|
|
let i = totalParts - 1
|
|
for (; i > 0; i--) {
|
|
const parentKey = join(slice(keyParts, 0, i), separator)
|
|
node = nodeMap.get(`${ConnectionType.RedisKey}/${parentKey}`)
|
|
if (node != null) {
|
|
break
|
|
}
|
|
}
|
|
if (node == null) {
|
|
node = dbNode
|
|
}
|
|
const keyCountUpdated = this._tidyNodeChildren(node, skipResort)
|
|
|
|
if (keyCountUpdated) {
|
|
// update key count of parent and above
|
|
for (; i > 0; i--) {
|
|
const parentKey = join(slice(keyParts, 0, i), separator)
|
|
const parentNode = nodeMap.get(`${ConnectionType.RedisKey}/${parentKey}`)
|
|
if (parentNode == null) {
|
|
break
|
|
}
|
|
parentNode.keys = sumBy(parentNode.children, 'keys')
|
|
}
|
|
// update key count of db
|
|
dbNode.keys = sumBy(dbNode.children, 'keys')
|
|
}
|
|
return true
|
|
},
|
|
|
|
/**
|
|
* sort all node item's children and calculate keys count
|
|
* @param {DatabaseItem} node
|
|
* @param {boolean} skipSort skip sorting children
|
|
* @returns {boolean} return whether key count changed
|
|
* @private
|
|
*/
|
|
_tidyNodeChildren(node, skipSort) {
|
|
let count = 0
|
|
if (!isEmpty(node.children)) {
|
|
if (skipSort !== true) {
|
|
this._sortNodes(node.children)
|
|
}
|
|
|
|
for (const elem of node.children) {
|
|
this._tidyNodeChildren(elem, skipSort)
|
|
count += elem.keys
|
|
}
|
|
} else {
|
|
if (node.type === ConnectionType.RedisValue) {
|
|
count += 1
|
|
} else {
|
|
// no children in db node or layer node, set count to 0
|
|
count = 0
|
|
}
|
|
}
|
|
if (node.keys !== count) {
|
|
node.keys = count
|
|
return true
|
|
}
|
|
return false
|
|
},
|
|
|
|
/**
|
|
* get tree node by key name
|
|
* @param key
|
|
* @return {DatabaseItem|null}
|
|
*/
|
|
getNode(key) {
|
|
let idx = key.indexOf('#')
|
|
if (idx < 0) {
|
|
idx = size(key)
|
|
}
|
|
const dbPart = key.substring(0, idx)
|
|
// parse server and db index
|
|
const idx2 = dbPart.lastIndexOf('/db')
|
|
if (idx2 < 0) {
|
|
return null
|
|
}
|
|
const server = dbPart.substring(0, idx2)
|
|
const db = parseInt(dbPart.substring(idx2 + 3))
|
|
if (isNaN(db)) {
|
|
return null
|
|
}
|
|
|
|
if (size(key) > idx + 1) {
|
|
const keyPart = key.substring(idx + 1)
|
|
// contains redis key
|
|
const nodeMap = this._getNodeMap(server, db)
|
|
return nodeMap.get(keyPart)
|
|
} else {
|
|
return this.getDatabase(server, db)
|
|
}
|
|
},
|
|
|
|
/**
|
|
* set redis key
|
|
* @param {string} connName
|
|
* @param {number} db
|
|
* @param {string|number[]} key
|
|
* @param {string} keyType
|
|
* @param {any} value
|
|
* @param {number} ttl
|
|
* @param {string} [viewAs]
|
|
* @returns {Promise<{[msg]: string, success: boolean, [nodeKey]: {string}}>}
|
|
*/
|
|
async setKey(connName, db, key, keyType, value, ttl, viewAs) {
|
|
try {
|
|
const { data, success, msg } = await SetKeyValue(connName, db, key, keyType, value, ttl, viewAs)
|
|
if (success) {
|
|
// update tree view data
|
|
const { newKey = 0 } = this._addKeyNodes(connName, db, [key], true)
|
|
if (newKey > 0) {
|
|
this._tidyNode(connName, db, key)
|
|
}
|
|
return { success, nodeKey: `${connName}/db${db}#${ConnectionType.RedisValue}/${key}` }
|
|
} else {
|
|
return { success, msg }
|
|
}
|
|
} catch (e) {
|
|
return { success: false, msg: e.message }
|
|
}
|
|
},
|
|
|
|
/**
|
|
* update hash field
|
|
* when field is set, newField is null, delete field
|
|
* when field is null, newField is set, add new field
|
|
* when both field and newField are set, and field === newField, update field
|
|
* when both field and newField are set, and field !== newField, delete field and add newField
|
|
* @param {string} connName
|
|
* @param {number} db
|
|
* @param {string|number[]} key
|
|
* @param {string} field
|
|
* @param {string} newField
|
|
* @param {string} value
|
|
* @returns {Promise<{[msg]: string, success: boolean, [updated]: {}}>}
|
|
*/
|
|
async setHash(connName, db, key, field, newField, value) {
|
|
try {
|
|
const { data, success, msg } = await SetHashValue(connName, db, key, field, newField || '', value || '')
|
|
if (success) {
|
|
const { updated = {} } = data
|
|
return { success, updated }
|
|
} else {
|
|
return { success, msg }
|
|
}
|
|
} catch (e) {
|
|
return { success: false, msg: e.message }
|
|
}
|
|
},
|
|
|
|
/**
|
|
* insert or update hash field item
|
|
* @param {string} connName
|
|
* @param {number} db
|
|
* @param {string|number[]} key
|
|
* @param {number }action 0:ignore duplicated fields 1:overwrite duplicated fields
|
|
* @param {string[]} fieldItems field1, value1, filed2, value2...
|
|
* @returns {Promise<{[msg]: string, success: boolean, [updated]: {}}>}
|
|
*/
|
|
async addHashField(connName, db, key, action, fieldItems) {
|
|
try {
|
|
const { data, success, msg } = await AddHashField(connName, db, key, action, fieldItems)
|
|
if (success) {
|
|
const { updated = {} } = data
|
|
return { success, updated }
|
|
} else {
|
|
return { success: false, msg }
|
|
}
|
|
} catch (e) {
|
|
return { success: false, msg: e.message }
|
|
}
|
|
},
|
|
|
|
/**
|
|
* remove hash field
|
|
* @param {string} connName
|
|
* @param {number} db
|
|
* @param {string} key
|
|
* @param {string} field
|
|
* @returns {Promise<{[msg]: {}, success: boolean, [removed]: string[]}>}
|
|
*/
|
|
async removeHashField(connName, db, key, field) {
|
|
try {
|
|
const { data, success, msg } = await SetHashValue(connName, db, key, field, '', '')
|
|
if (success) {
|
|
const { removed = [] } = data
|
|
return { success, removed }
|
|
} else {
|
|
return { success, msg }
|
|
}
|
|
} catch (e) {
|
|
return { success: false, msg: e.message }
|
|
}
|
|
},
|
|
|
|
/**
|
|
* insert list item
|
|
* @param {string} connName
|
|
* @param {number} db
|
|
* @param {string|number[]} key
|
|
* @param {int} action 0: push to head, 1: push to tail
|
|
* @param {string[]}values
|
|
* @returns {Promise<*|{msg, success: boolean}>}
|
|
*/
|
|
async addListItem(connName, db, key, action, values) {
|
|
try {
|
|
return AddListItem(connName, db, key, action, values)
|
|
} catch (e) {
|
|
return { success: false, msg: e.message }
|
|
}
|
|
},
|
|
|
|
/**
|
|
* prepend item to head of list
|
|
* @param connName
|
|
* @param db
|
|
* @param key
|
|
* @param values
|
|
* @returns {Promise<[msg]: string, success: boolean, [item]: []>}
|
|
*/
|
|
async prependListItem(connName, db, key, values) {
|
|
try {
|
|
const { data, success, msg } = await AddListItem(connName, db, key, 0, values)
|
|
if (success) {
|
|
const { left = [] } = data
|
|
return { success, item: left }
|
|
} else {
|
|
return { success: false, msg }
|
|
}
|
|
} catch (e) {
|
|
return { success: false, msg: e.message }
|
|
}
|
|
},
|
|
|
|
/**
|
|
* append item to tail of list
|
|
* @param connName
|
|
* @param db
|
|
* @param key
|
|
* @param values
|
|
* @returns {Promise<[msg]: string, success: boolean, [item]: any[]>}
|
|
*/
|
|
async appendListItem(connName, db, key, values) {
|
|
try {
|
|
const { data, success, msg } = await AddListItem(connName, db, key, 1, values)
|
|
if (success) {
|
|
const { right = [] } = data
|
|
return { success, item: right }
|
|
} else {
|
|
return { success: false, msg }
|
|
}
|
|
} catch (e) {
|
|
return { success: false, msg: e.message }
|
|
}
|
|
},
|
|
|
|
/**
|
|
* update value of list item by index
|
|
* @param {string} connName
|
|
* @param {number} db
|
|
* @param {string|number[]} key
|
|
* @param {number} index
|
|
* @param {string} value
|
|
* @returns {Promise<{[msg]: string, success: boolean, [updated]: {}}>}
|
|
*/
|
|
async updateListItem(connName, db, key, index, value) {
|
|
try {
|
|
const { data, success, msg } = await SetListItem(connName, db, key, index, value)
|
|
if (success) {
|
|
const { updated = {} } = data
|
|
return { success, updated }
|
|
} else {
|
|
return { success, msg }
|
|
}
|
|
} catch (e) {
|
|
return { success: false, msg: e.message }
|
|
}
|
|
},
|
|
|
|
/**
|
|
* remove list item
|
|
* @param {string} connName
|
|
* @param {number} db
|
|
* @param {string|number[]} key
|
|
* @param {number} index
|
|
* @returns {Promise<{[msg]: string, success: boolean, [removed]: string[]}>}
|
|
*/
|
|
async removeListItem(connName, db, key, index) {
|
|
try {
|
|
const { data, success, msg } = await SetListItem(connName, db, key, index, '')
|
|
if (success) {
|
|
const { removed = [] } = data
|
|
return { success, removed }
|
|
} else {
|
|
return { success, msg }
|
|
}
|
|
} catch (e) {
|
|
return { success: false, msg: e.message }
|
|
}
|
|
},
|
|
|
|
/**
|
|
* add item to set
|
|
* @param {string} connName
|
|
* @param {number} db
|
|
* @param {string|number} key
|
|
* @param {string} value
|
|
* @returns {Promise<{[msg]: string, success: boolean}>}
|
|
*/
|
|
async addSetItem(connName, db, key, value) {
|
|
try {
|
|
const { success, msg } = await SetSetItem(connName, db, key, false, [value])
|
|
if (success) {
|
|
return { success }
|
|
} else {
|
|
return { success, msg }
|
|
}
|
|
} catch (e) {
|
|
return { success: false, msg: e.message }
|
|
}
|
|
},
|
|
|
|
/**
|
|
* update value of set item
|
|
* @param {string} connName
|
|
* @param {number} db
|
|
* @param {string|number[]} key
|
|
* @param {string} value
|
|
* @param {string} newValue
|
|
* @returns {Promise<{[msg]: string, success: boolean}>}
|
|
*/
|
|
async updateSetItem(connName, db, key, value, newValue) {
|
|
try {
|
|
const { success, msg } = await UpdateSetItem(connName, db, key, value, newValue)
|
|
if (success) {
|
|
return { success: true }
|
|
} else {
|
|
return { success, msg }
|
|
}
|
|
} catch (e) {
|
|
return { success: false, msg: e.message }
|
|
}
|
|
},
|
|
|
|
/**
|
|
* remove item from set
|
|
* @param {string} connName
|
|
* @param {number} db
|
|
* @param {string|number[]} key
|
|
* @param {string} value
|
|
* @returns {Promise<{[msg]: string, success: boolean}>}
|
|
*/
|
|
async removeSetItem(connName, db, key, value) {
|
|
try {
|
|
const { success, msg } = await SetSetItem(connName, db, key, true, [value])
|
|
if (success) {
|
|
return { success }
|
|
} else {
|
|
return { success, msg }
|
|
}
|
|
} catch (e) {
|
|
return { success: false, msg: e.message }
|
|
}
|
|
},
|
|
|
|
/**
|
|
* add item to sorted set
|
|
* @param {string} connName
|
|
* @param {number} db
|
|
* @param {string|number[]} key
|
|
* @param {number} action
|
|
* @param {Object.<string, number>} vs value: score
|
|
* @returns {Promise<{[msg]: string, success: boolean}>}
|
|
*/
|
|
async addZSetItem(connName, db, key, action, vs) {
|
|
try {
|
|
const { success, msg } = await AddZSetValue(connName, db, key, action, vs)
|
|
if (success) {
|
|
return { success }
|
|
} else {
|
|
return { success, msg }
|
|
}
|
|
} catch (e) {
|
|
return { success: false, msg: e.message }
|
|
}
|
|
},
|
|
|
|
/**
|
|
* update item of sorted set
|
|
* @param {string} connName
|
|
* @param {number} db
|
|
* @param {string|number[]} key
|
|
* @param {string} value
|
|
* @param {string} newValue
|
|
* @param {number} score
|
|
* @returns {Promise<{[msg]: string, success: boolean, [updated]: {}, [removed]: []}>}
|
|
*/
|
|
async updateZSetItem(connName, db, key, value, newValue, score) {
|
|
try {
|
|
const { data, success, msg } = await UpdateZSetValue(connName, db, key, value, newValue, score)
|
|
if (success) {
|
|
const { updated, removed } = data
|
|
return { success, updated, removed }
|
|
} else {
|
|
return { success, msg }
|
|
}
|
|
} catch (e) {
|
|
return { success: false, msg: e.message }
|
|
}
|
|
},
|
|
|
|
/**
|
|
* remove item from sorted set
|
|
* @param {string} connName
|
|
* @param {number} db
|
|
* @param {string|number[]} key
|
|
* @param {string} value
|
|
* @returns {Promise<{[msg]: string, success: boolean, [removed]: []}>}
|
|
*/
|
|
async removeZSetItem(connName, db, key, value) {
|
|
try {
|
|
const { data, success, msg } = await UpdateZSetValue(connName, db, key, value, '', 0)
|
|
if (success) {
|
|
const { removed } = data
|
|
return { success, removed }
|
|
} else {
|
|
return { success, msg }
|
|
}
|
|
} catch (e) {
|
|
return { success: false, msg: e.message }
|
|
}
|
|
},
|
|
|
|
/**
|
|
* insert new stream field item
|
|
* @param {string} connName
|
|
* @param {number} db
|
|
* @param {string|number[]} key
|
|
* @param {string} id
|
|
* @param {string[]} values field1, value1, filed2, value2...
|
|
* @returns {Promise<{[msg]: string, success: boolean, [updated]: {}}>}
|
|
*/
|
|
async addStreamValue(connName, db, key, id, values) {
|
|
try {
|
|
const { data = {}, success, msg } = await AddStreamValue(connName, db, key, id, values)
|
|
if (success) {
|
|
const { updated = {} } = data
|
|
return { success, updated }
|
|
} else {
|
|
return { success: false, msg }
|
|
}
|
|
} catch (e) {
|
|
return { success: false, msg: e.message }
|
|
}
|
|
},
|
|
|
|
/**
|
|
* remove stream field
|
|
* @param {string} connName
|
|
* @param {number} db
|
|
* @param {string|number[]} key
|
|
* @param {string[]|string} ids
|
|
* @returns {Promise<{[msg]: {}, success: boolean, [removed]: string[]}>}
|
|
*/
|
|
async removeStreamValues(connName, db, key, ids) {
|
|
if (typeof ids === 'string') {
|
|
ids = [ids]
|
|
}
|
|
try {
|
|
const { data = {}, success, msg } = await RemoveStreamValues(connName, db, key, ids)
|
|
if (success) {
|
|
const { removed = [] } = data
|
|
return { success, removed }
|
|
} else {
|
|
return { success, msg }
|
|
}
|
|
} catch (e) {
|
|
return { success: false, msg: e.message }
|
|
}
|
|
},
|
|
|
|
/**
|
|
* reset key's ttl
|
|
* @param {string} connName
|
|
* @param {number} db
|
|
* @param {string} key
|
|
* @param {number} ttl
|
|
* @returns {Promise<boolean>}
|
|
*/
|
|
async setTTL(connName, db, key, ttl) {
|
|
try {
|
|
const { success, msg } = await SetKeyTTL(connName, db, key, ttl)
|
|
return success === true
|
|
} catch (e) {
|
|
return false
|
|
}
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {string} connName
|
|
* @param {number} db
|
|
* @param {string} [key]
|
|
* @param {boolean} [isLayer]
|
|
* @private
|
|
*/
|
|
_deleteKeyNode(connName, db, key, isLayer) {
|
|
const dbRoot = this.getDatabase(connName, db) || {}
|
|
const separator = this._getSeparator(connName)
|
|
|
|
if (dbRoot == null) {
|
|
return false
|
|
}
|
|
|
|
const nodeMap = this._getNodeMap(connName, db)
|
|
if (isLayer === true) {
|
|
this._deleteChildrenKeyNodes(nodeMap, key)
|
|
}
|
|
if (isEmpty(key)) {
|
|
// clear all key nodes
|
|
dbRoot.children = []
|
|
dbRoot.keys = 0
|
|
} else {
|
|
const keyParts = split(key, separator)
|
|
const totalParts = size(keyParts)
|
|
// remove from parent in tree node
|
|
const parentKey = slice(keyParts, 0, totalParts - 1)
|
|
let parentNode
|
|
if (isEmpty(parentKey)) {
|
|
parentNode = dbRoot
|
|
} else {
|
|
parentNode = nodeMap.get(`${ConnectionType.RedisKey}/${join(parentKey, separator)}`)
|
|
}
|
|
|
|
// not found parent node
|
|
if (parentNode == null) {
|
|
return false
|
|
}
|
|
remove(parentNode.children, {
|
|
type: isLayer ? ConnectionType.RedisKey : ConnectionType.RedisValue,
|
|
redisKey: key,
|
|
})
|
|
|
|
// check and remove empty layer node
|
|
let i = totalParts - 1
|
|
for (; i >= 0; i--) {
|
|
const anceKey = join(slice(keyParts, 0, i), separator)
|
|
if (i > 0) {
|
|
const anceNode = nodeMap.get(`${ConnectionType.RedisKey}/${anceKey}`)
|
|
const redisKey = join(slice(keyParts, 0, i + 1), separator)
|
|
remove(anceNode.children, { type: ConnectionType.RedisKey, redisKey })
|
|
|
|
if (isEmpty(anceNode.children)) {
|
|
nodeMap.delete(`${ConnectionType.RedisKey}/${anceKey}`)
|
|
} else {
|
|
break
|
|
}
|
|
} else {
|
|
// last one, remove from db node
|
|
remove(dbRoot.children, { type: ConnectionType.RedisKey, redisKey: keyParts[0] })
|
|
}
|
|
}
|
|
}
|
|
|
|
return true
|
|
},
|
|
|
|
/**
|
|
* delete node and all it's children from nodeMap
|
|
* @param {Map<string, DatabaseItem>} nodeMap
|
|
* @param {string} [key] clean nodeMap if key is empty
|
|
* @private
|
|
*/
|
|
_deleteChildrenKeyNodes(nodeMap, key) {
|
|
if (isEmpty(key)) {
|
|
nodeMap.clear()
|
|
} else {
|
|
const mapKey = `${ConnectionType.RedisKey}/${key}`
|
|
const node = nodeMap.get(mapKey)
|
|
for (const child of node.children || []) {
|
|
if (child.type === ConnectionType.RedisValue) {
|
|
if (!nodeMap.delete(`${ConnectionType.RedisValue}/${child.redisKey}`)) {
|
|
console.warn('delete:', `${ConnectionType.RedisValue}/${child.redisKey}`)
|
|
}
|
|
} else if (child.type === ConnectionType.RedisKey) {
|
|
this._deleteChildrenKeyNodes(nodeMap, child.redisKey)
|
|
}
|
|
}
|
|
if (!nodeMap.delete(mapKey)) {
|
|
console.warn('delete map key', mapKey)
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* delete redis key
|
|
* @param {string} connName
|
|
* @param {number} db
|
|
* @param {string|number[]} key
|
|
* @param {boolean} [soft] do not try to remove from redis if true, just remove from tree data
|
|
* @returns {Promise<boolean>}
|
|
*/
|
|
async deleteKey(connName, db, key, soft) {
|
|
try {
|
|
if (soft !== true) {
|
|
await DeleteKey(connName, db, key)
|
|
}
|
|
|
|
const k = nativeRedisKey(key)
|
|
// update tree view data
|
|
this._deleteKeyNode(connName, db, k)
|
|
this._tidyNode(connName, db, k, true)
|
|
|
|
// set tab content empty
|
|
const tab = useTabStore()
|
|
tab.emptyTab(connName)
|
|
return true
|
|
} finally {
|
|
}
|
|
return false
|
|
},
|
|
|
|
/**
|
|
* delete keys with prefix
|
|
* @param connName
|
|
* @param db
|
|
* @param prefix
|
|
* @returns {Promise<boolean>}
|
|
*/
|
|
async deleteKeyPrefix(connName, db, prefix) {
|
|
if (isEmpty(prefix)) {
|
|
return false
|
|
}
|
|
try {
|
|
if (!endsWith(prefix, '*')) {
|
|
prefix += '*'
|
|
}
|
|
const { data, success, msg } = await DeleteKey(connName, db, prefix)
|
|
if (success) {
|
|
// const { deleted: keys = [] } = data
|
|
// for (const key of keys) {
|
|
// await this._deleteKeyNode(connName, db, key)
|
|
// }
|
|
const separator = this._getSeparator(connName)
|
|
if (endsWith(prefix, '*')) {
|
|
prefix = prefix.substring(0, prefix.length - 1)
|
|
}
|
|
if (endsWith(prefix, separator)) {
|
|
prefix = prefix.substring(0, prefix.length - 1)
|
|
}
|
|
this._deleteKeyNode(connName, db, prefix, true)
|
|
this._tidyNode(connName, db, prefix, true)
|
|
return true
|
|
}
|
|
} finally {
|
|
}
|
|
return false
|
|
},
|
|
|
|
/**
|
|
* rename key
|
|
* @param {string} connName
|
|
* @param {number} db
|
|
* @param {string} key
|
|
* @param {string} newKey
|
|
* @returns {Promise<{[msg]: string, success: boolean}>}
|
|
*/
|
|
async renameKey(connName, db, key, newKey) {
|
|
const { success = false, msg } = await RenameKey(connName, db, key, newKey)
|
|
if (success) {
|
|
// delete old key and add new key struct
|
|
this._deleteKeyNode(connName, db, key)
|
|
this._addKeyNodes(connName, db, [newKey])
|
|
return { success: true }
|
|
} else {
|
|
return { success: false, msg }
|
|
}
|
|
},
|
|
|
|
/**
|
|
* get command history
|
|
* @param {number} [pageNo]
|
|
* @param {number} [pageSize]
|
|
* @returns {Promise<HistoryItem[]>}
|
|
*/
|
|
async getCmdHistory(pageNo, pageSize) {
|
|
if (pageNo === undefined || pageSize === undefined) {
|
|
pageNo = -1
|
|
pageSize = -1
|
|
}
|
|
try {
|
|
const { success, data = { list: [] } } = await GetCmdHistory(pageNo, pageSize)
|
|
const { list } = data
|
|
return list
|
|
} catch {
|
|
return []
|
|
}
|
|
},
|
|
|
|
/**
|
|
* clean cmd history
|
|
* @return {Promise<boolean>}
|
|
*/
|
|
async cleanCmdHistory() {
|
|
try {
|
|
const { success } = await CleanCmdHistory()
|
|
return success === true
|
|
} catch {
|
|
return false
|
|
}
|
|
},
|
|
|
|
/**
|
|
* get key filter pattern and filter type
|
|
* @param {string} server
|
|
* @param {number} db
|
|
* @returns {{match: string, type: string}}
|
|
*/
|
|
getKeyFilter(server, db) {
|
|
let match, type
|
|
const key = `${server}#${db}`
|
|
if (!this.keyFilter.hasOwnProperty(key)) {
|
|
match = this.keyFilter[`${server}#-1`] || '*'
|
|
} else {
|
|
match = this.keyFilter[key] || '*'
|
|
}
|
|
type = this.typeFilter[`${server}#${db}`] || ''
|
|
return {
|
|
match,
|
|
type: toUpper(type),
|
|
}
|
|
},
|
|
|
|
/**
|
|
* set key filter
|
|
* @param {string} server
|
|
* @param {number} db
|
|
* @param {string} pattern
|
|
* @param {string} [type]
|
|
*/
|
|
setKeyFilter(server, db, pattern, type) {
|
|
this.keyFilter[`${server}#${db}`] = pattern || '*'
|
|
this.typeFilter[`${server}#${db}`] = types[toUpper(type)] || ''
|
|
},
|
|
|
|
removeKeyFilter(server, db) {
|
|
this.keyFilter[`${server}#${db}`] = '*'
|
|
delete this.typeFilter[`${server}#${db}`]
|
|
},
|
|
},
|
|
})
|
|
|
|
export default useConnectionStore
|