Compare commits
7 Commits
23087a5374
...
7f2fac7fe8
Author | SHA1 | Date |
---|---|---|
Lykin | 7f2fac7fe8 | |
Lykin | 13dbc9b3b6 | |
Lykin | 1d1fab54d8 | |
Lykin | ed18d8b5ee | |
Lykin | e071e65701 | |
Lykin | b823f18794 | |
Lykin | ac6d68d17d |
|
@ -483,8 +483,8 @@ func (b *browserService) scanKeys(ctx context.Context, client redis.UniversalCli
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadNextKeys load next key from saved cursor
|
// LoadNextKeys load next key from saved cursor
|
||||||
func (b *browserService) LoadNextKeys(connName string, db int, match, keyType string) (resp types.JSResp) {
|
func (b *browserService) LoadNextKeys(server string, db int, match, keyType string) (resp types.JSResp) {
|
||||||
item, err := b.getRedisClient(connName, db)
|
item, err := b.getRedisClient(server, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Msg = err.Error()
|
resp.Msg = err.Error()
|
||||||
return
|
return
|
||||||
|
@ -497,7 +497,7 @@ func (b *browserService) LoadNextKeys(connName string, db int, match, keyType st
|
||||||
resp.Msg = err.Error()
|
resp.Msg = err.Error()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
b.setClientCursor(connName, db, cursor)
|
b.setClientCursor(server, db, cursor)
|
||||||
maxKeys := b.loadDBSize(ctx, client)
|
maxKeys := b.loadDBSize(ctx, client)
|
||||||
|
|
||||||
resp.Success = true
|
resp.Success = true
|
||||||
|
@ -510,8 +510,8 @@ func (b *browserService) LoadNextKeys(connName string, db int, match, keyType st
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadNextAllKeys load next all keys
|
// LoadNextAllKeys load next all keys
|
||||||
func (b *browserService) LoadNextAllKeys(connName string, db int, match, keyType string) (resp types.JSResp) {
|
func (b *browserService) LoadNextAllKeys(server string, db int, match, keyType string) (resp types.JSResp) {
|
||||||
item, err := b.getRedisClient(connName, db)
|
item, err := b.getRedisClient(server, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Msg = err.Error()
|
resp.Msg = err.Error()
|
||||||
return
|
return
|
||||||
|
@ -524,7 +524,7 @@ func (b *browserService) LoadNextAllKeys(connName string, db int, match, keyType
|
||||||
resp.Msg = err.Error()
|
resp.Msg = err.Error()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
b.setClientCursor(connName, db, 0)
|
b.setClientCursor(server, db, 0)
|
||||||
maxKeys := b.loadDBSize(ctx, client)
|
maxKeys := b.loadDBSize(ctx, client)
|
||||||
|
|
||||||
resp.Success = true
|
resp.Success = true
|
||||||
|
@ -536,8 +536,8 @@ func (b *browserService) LoadNextAllKeys(connName string, db int, match, keyType
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadAllKeys load all keys
|
// LoadAllKeys load all keys
|
||||||
func (b *browserService) LoadAllKeys(connName string, db int, match, keyType string) (resp types.JSResp) {
|
func (b *browserService) LoadAllKeys(server string, db int, match, keyType string) (resp types.JSResp) {
|
||||||
item, err := b.getRedisClient(connName, db)
|
item, err := b.getRedisClient(server, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Msg = err.Error()
|
resp.Msg = err.Error()
|
||||||
return
|
return
|
||||||
|
@ -1320,8 +1320,8 @@ func (b *browserService) SetHashValue(param types.SetHashParam) (resp types.JSRe
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddHashField add or update hash field
|
// AddHashField add or update hash field
|
||||||
func (b *browserService) AddHashField(connName string, db int, k any, action int, fieldItems []any) (resp types.JSResp) {
|
func (b *browserService) AddHashField(server string, db int, k any, action int, fieldItems []any) (resp types.JSResp) {
|
||||||
item, err := b.getRedisClient(connName, db)
|
item, err := b.getRedisClient(server, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Msg = err.Error()
|
resp.Msg = err.Error()
|
||||||
return
|
return
|
||||||
|
@ -1383,8 +1383,8 @@ func (b *browserService) AddHashField(connName string, db int, k any, action int
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddListItem add item to list or remove from it
|
// AddListItem add item to list or remove from it
|
||||||
func (b *browserService) AddListItem(connName string, db int, k any, action int, items []any) (resp types.JSResp) {
|
func (b *browserService) AddListItem(server string, db int, k any, action int, items []any) (resp types.JSResp) {
|
||||||
item, err := b.getRedisClient(connName, db)
|
item, err := b.getRedisClient(server, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Msg = err.Error()
|
resp.Msg = err.Error()
|
||||||
return
|
return
|
||||||
|
@ -1699,8 +1699,8 @@ func (b *browserService) UpdateZSetValue(param types.SetZSetParam) (resp types.J
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddZSetValue add item to sorted set
|
// AddZSetValue add item to sorted set
|
||||||
func (b *browserService) AddZSetValue(connName string, db int, k any, action int, valueScore map[string]float64) (resp types.JSResp) {
|
func (b *browserService) AddZSetValue(server string, db int, k any, action int, valueScore map[string]float64) (resp types.JSResp) {
|
||||||
item, err := b.getRedisClient(connName, db)
|
item, err := b.getRedisClient(server, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Msg = err.Error()
|
resp.Msg = err.Error()
|
||||||
return
|
return
|
||||||
|
@ -1757,8 +1757,8 @@ func (b *browserService) AddZSetValue(connName string, db int, k any, action int
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddStreamValue add stream field
|
// AddStreamValue add stream field
|
||||||
func (b *browserService) AddStreamValue(connName string, db int, k any, ID string, fieldItems []any) (resp types.JSResp) {
|
func (b *browserService) AddStreamValue(server string, db int, k any, ID string, fieldItems []any) (resp types.JSResp) {
|
||||||
item, err := b.getRedisClient(connName, db)
|
item, err := b.getRedisClient(server, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Msg = err.Error()
|
resp.Msg = err.Error()
|
||||||
return
|
return
|
||||||
|
@ -1800,8 +1800,8 @@ func (b *browserService) AddStreamValue(connName string, db int, k any, ID strin
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveStreamValues remove stream values by id
|
// RemoveStreamValues remove stream values by id
|
||||||
func (b *browserService) RemoveStreamValues(connName string, db int, k any, IDs []string) (resp types.JSResp) {
|
func (b *browserService) RemoveStreamValues(server string, db int, k any, IDs []string) (resp types.JSResp) {
|
||||||
item, err := b.getRedisClient(connName, db)
|
item, err := b.getRedisClient(server, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Msg = err.Error()
|
resp.Msg = err.Error()
|
||||||
return
|
return
|
||||||
|
@ -1827,8 +1827,8 @@ func (b *browserService) RemoveStreamValues(connName string, db int, k any, IDs
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetKeyTTL set ttl of key
|
// SetKeyTTL set ttl of key
|
||||||
func (b *browserService) SetKeyTTL(connName string, db int, k any, ttl int64) (resp types.JSResp) {
|
func (b *browserService) SetKeyTTL(server string, db int, k any, ttl int64) (resp types.JSResp) {
|
||||||
item, err := b.getRedisClient(connName, db)
|
item, err := b.getRedisClient(server, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Msg = err.Error()
|
resp.Msg = err.Error()
|
||||||
return
|
return
|
||||||
|
@ -1836,14 +1836,13 @@ func (b *browserService) SetKeyTTL(connName string, db int, k any, ttl int64) (r
|
||||||
|
|
||||||
client, ctx := item.client, item.ctx
|
client, ctx := item.client, item.ctx
|
||||||
key := strutil.DecodeRedisKey(k)
|
key := strutil.DecodeRedisKey(k)
|
||||||
var expiration time.Duration
|
|
||||||
if ttl < 0 {
|
if ttl < 0 {
|
||||||
if err = client.Persist(ctx, key).Err(); err != nil {
|
if err = client.Persist(ctx, key).Err(); err != nil {
|
||||||
resp.Msg = err.Error()
|
resp.Msg = err.Error()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
expiration = time.Duration(ttl) * time.Second
|
expiration := time.Duration(ttl) * time.Second
|
||||||
if err = client.Expire(ctx, key, expiration).Err(); err != nil {
|
if err = client.Expire(ctx, key, expiration).Err(); err != nil {
|
||||||
resp.Msg = err.Error()
|
resp.Msg = err.Error()
|
||||||
return
|
return
|
||||||
|
@ -1854,6 +1853,95 @@ func (b *browserService) SetKeyTTL(connName string, db int, k any, ttl int64) (r
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BatchSetTTL batch set ttl
|
||||||
|
func (b *browserService) BatchSetTTL(server string, db int, ks []any, ttl int64, serialNo string) (resp types.JSResp) {
|
||||||
|
conf := Connection().getConnection(server)
|
||||||
|
if conf == nil {
|
||||||
|
resp.Msg = fmt.Sprintf("no connection profile named: %s", server)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var client redis.UniversalClient
|
||||||
|
var err error
|
||||||
|
var connConfig = conf.ConnectionConfig
|
||||||
|
connConfig.LastDB = db
|
||||||
|
if client, err = b.createRedisClient(connConfig); err != nil {
|
||||||
|
resp.Msg = err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx, cancelFunc := context.WithCancel(b.ctx)
|
||||||
|
defer client.Close()
|
||||||
|
defer cancelFunc()
|
||||||
|
|
||||||
|
//cancelEvent := "ttling:stop:" + serialNo
|
||||||
|
//runtime.EventsOnce(ctx, cancelEvent, func(data ...any) {
|
||||||
|
// cancelFunc()
|
||||||
|
//})
|
||||||
|
//processEvent := "ttling:" + serialNo
|
||||||
|
total := len(ks)
|
||||||
|
var failed, updated atomic.Int64
|
||||||
|
var canceled bool
|
||||||
|
|
||||||
|
expiration := time.Now().Add(time.Duration(ttl) * time.Second)
|
||||||
|
del := func(ctx context.Context, cli redis.UniversalClient) error {
|
||||||
|
startTime := time.Now().Add(-10 * time.Second)
|
||||||
|
for i, k := range ks {
|
||||||
|
// emit progress per second
|
||||||
|
//param := map[string]any{
|
||||||
|
// "total": total,
|
||||||
|
// "progress": i + 1,
|
||||||
|
// "processing": k,
|
||||||
|
//}
|
||||||
|
if i >= total-1 || time.Now().Sub(startTime).Milliseconds() > 100 {
|
||||||
|
startTime = time.Now()
|
||||||
|
//runtime.EventsEmit(b.ctx, processEvent, param)
|
||||||
|
// do some sleep to prevent blocking the Redis server
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
key := strutil.DecodeRedisKey(k)
|
||||||
|
var expErr error
|
||||||
|
if ttl < 0 {
|
||||||
|
expErr = cli.Persist(ctx, key).Err()
|
||||||
|
} else {
|
||||||
|
expErr = cli.ExpireAt(ctx, key, expiration).Err()
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
failed.Add(1)
|
||||||
|
} else {
|
||||||
|
// save deleted key
|
||||||
|
updated.Add(1)
|
||||||
|
}
|
||||||
|
if errors.Is(expErr, context.Canceled) || canceled {
|
||||||
|
canceled = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if cluster, ok := client.(*redis.ClusterClient); ok {
|
||||||
|
// cluster mode
|
||||||
|
err = cluster.ForEachMaster(ctx, func(ctx context.Context, cli *redis.Client) error {
|
||||||
|
return del(ctx, cli)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
err = del(ctx, client)
|
||||||
|
}
|
||||||
|
|
||||||
|
//runtime.EventsOff(ctx, cancelEvent)
|
||||||
|
resp.Success = true
|
||||||
|
resp.Data = struct {
|
||||||
|
Canceled bool `json:"canceled"`
|
||||||
|
Updated int64 `json:"updated"`
|
||||||
|
Failed int64 `json:"failed"`
|
||||||
|
}{
|
||||||
|
Canceled: canceled,
|
||||||
|
Updated: updated.Load(),
|
||||||
|
Failed: failed.Load(),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteKey remove redis key
|
// DeleteKey remove redis key
|
||||||
func (b *browserService) DeleteKey(server string, db int, k any, async bool) (resp types.JSResp) {
|
func (b *browserService) DeleteKey(server string, db int, k any, async bool) (resp types.JSResp) {
|
||||||
item, err := b.getRedisClient(server, db)
|
item, err := b.getRedisClient(server, db)
|
||||||
|
@ -2007,13 +2095,13 @@ func (b *browserService) DeleteKeys(server string, db int, ks []any, serialNo st
|
||||||
startTime := time.Now().Add(-10 * time.Second)
|
startTime := time.Now().Add(-10 * time.Second)
|
||||||
for i, k := range ks {
|
for i, k := range ks {
|
||||||
// emit progress per second
|
// emit progress per second
|
||||||
|
if i >= total-1 || time.Now().Sub(startTime).Milliseconds() > 100 {
|
||||||
|
startTime = time.Now()
|
||||||
param := map[string]any{
|
param := map[string]any{
|
||||||
"total": total,
|
"total": total,
|
||||||
"progress": i + 1,
|
"progress": i + 1,
|
||||||
"processing": k,
|
"processing": k,
|
||||||
}
|
}
|
||||||
if i >= total-1 || time.Now().Sub(startTime).Milliseconds() > 100 {
|
|
||||||
startTime = time.Now()
|
|
||||||
runtime.EventsEmit(b.ctx, processEvent, param)
|
runtime.EventsEmit(b.ctx, processEvent, param)
|
||||||
// do some sleep to prevent blocking the Redis server
|
// do some sleep to prevent blocking the Redis server
|
||||||
time.Sleep(10 * time.Millisecond)
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
@ -2146,7 +2234,7 @@ func (b *browserService) ExportKey(server string, db int, ks []any, path string,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImportCSV import data from csv file
|
// ImportCSV import data from csv file
|
||||||
func (b *browserService) ImportCSV(server string, db int, path string, conflict int, includeExpire bool) (resp types.JSResp) {
|
func (b *browserService) ImportCSV(server string, db int, path string, conflict int, ttl int64) (resp types.JSResp) {
|
||||||
// connect a new connection to export keys
|
// connect a new connection to export keys
|
||||||
conf := Connection().getConnection(server)
|
conf := Connection().getConnection(server)
|
||||||
if conf == nil {
|
if conf == nil {
|
||||||
|
@ -2182,14 +2270,14 @@ func (b *browserService) ImportCSV(server string, db int, path string, conflict
|
||||||
var line []string
|
var line []string
|
||||||
var readErr error
|
var readErr error
|
||||||
var key, value []byte
|
var key, value []byte
|
||||||
var ttl time.Duration
|
var ttlValue time.Duration
|
||||||
var imported, ignored int64
|
var imported, ignored int64
|
||||||
var canceled bool
|
var canceled bool
|
||||||
startTime := time.Now().Add(-10 * time.Second)
|
startTime := time.Now().Add(-10 * time.Second)
|
||||||
for {
|
for {
|
||||||
readErr = nil
|
readErr = nil
|
||||||
|
|
||||||
ttl = redis.KeepTTL
|
ttlValue = redis.KeepTTL
|
||||||
line, readErr = reader.Read()
|
line, readErr = reader.Read()
|
||||||
if readErr != nil {
|
if readErr != nil {
|
||||||
break
|
break
|
||||||
|
@ -2205,21 +2293,25 @@ func (b *browserService) ImportCSV(server string, db int, path string, conflict
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// get ttl
|
// get ttl
|
||||||
if includeExpire && len(line) > 2 {
|
if ttl < 0 {
|
||||||
|
// use previous
|
||||||
if expire, ttlErr := strconv.ParseInt(line[2], 10, 64); ttlErr == nil && expire > 0 {
|
if expire, ttlErr := strconv.ParseInt(line[2], 10, 64); ttlErr == nil && expire > 0 {
|
||||||
ttl = time.UnixMilli(expire).Sub(time.Now())
|
ttlValue = time.UnixMilli(expire).Sub(time.Now())
|
||||||
}
|
}
|
||||||
|
} else if ttl > 0 {
|
||||||
|
// custom ttl
|
||||||
|
ttlValue = time.Duration(ttl) * time.Second
|
||||||
}
|
}
|
||||||
if conflict == 0 {
|
if conflict == 0 {
|
||||||
readErr = client.RestoreReplace(ctx, string(key), ttl, string(value)).Err()
|
readErr = client.RestoreReplace(ctx, string(key), ttlValue, string(value)).Err()
|
||||||
} else {
|
} else {
|
||||||
keyStr := string(key)
|
keyStr := string(key)
|
||||||
// go-redis may crash when batch calling restore
|
// go-redis may crash when batch calling restore
|
||||||
// use "exists" to filter first
|
// use "exists" to filter first
|
||||||
if n, _ := client.Exists(ctx, keyStr).Result(); n <= 0 {
|
if n, _ := client.Exists(ctx, keyStr).Result(); n <= 0 {
|
||||||
readErr = client.Restore(ctx, keyStr, ttl, string(value)).Err()
|
readErr = client.Restore(ctx, keyStr, ttlValue, string(value)).Err()
|
||||||
} else {
|
} else {
|
||||||
readErr = errors.New("key existed")
|
readErr = errors.New("key already existed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if readErr != nil {
|
if readErr != nil {
|
||||||
|
@ -2261,8 +2353,8 @@ func (b *browserService) ImportCSV(server string, db int, path string, conflict
|
||||||
}
|
}
|
||||||
|
|
||||||
// FlushDB flush database
|
// FlushDB flush database
|
||||||
func (b *browserService) FlushDB(connName string, db int, async bool) (resp types.JSResp) {
|
func (b *browserService) FlushDB(server string, db int, async bool) (resp types.JSResp) {
|
||||||
item, err := b.getRedisClient(connName, db)
|
item, err := b.getRedisClient(server, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Msg = err.Error()
|
resp.Msg = err.Error()
|
||||||
return
|
return
|
||||||
|
@ -2309,8 +2401,8 @@ func (b *browserService) FlushDB(connName string, db int, async bool) (resp type
|
||||||
}
|
}
|
||||||
|
|
||||||
// RenameKey rename key
|
// RenameKey rename key
|
||||||
func (b *browserService) RenameKey(connName string, db int, key, newKey string) (resp types.JSResp) {
|
func (b *browserService) RenameKey(server string, db int, key, newKey string) (resp types.JSResp) {
|
||||||
item, err := b.getRedisClient(connName, db)
|
item, err := b.getRedisClient(server, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Msg = err.Error()
|
resp.Msg = err.Error()
|
||||||
return
|
return
|
||||||
|
@ -2362,8 +2454,8 @@ func (b *browserService) CleanCmdHistory() (resp types.JSResp) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSlowLogs get slow log list
|
// GetSlowLogs get slow log list
|
||||||
func (b *browserService) GetSlowLogs(connName string, db int, num int64) (resp types.JSResp) {
|
func (b *browserService) GetSlowLogs(server string, db int, num int64) (resp types.JSResp) {
|
||||||
item, err := b.getRedisClient(connName, db)
|
item, err := b.getRedisClient(server, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Msg = err.Error()
|
resp.Msg = err.Error()
|
||||||
return
|
return
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
type monitorItem struct {
|
type monitorItem struct {
|
||||||
client *redis.Client
|
client *redis.Client
|
||||||
cmd *redis.MonitorCmd
|
cmd *redis.MonitorCmd
|
||||||
|
mutex sync.Mutex
|
||||||
ch chan string
|
ch chan string
|
||||||
closeCh chan struct{}
|
closeCh chan struct{}
|
||||||
eventName string
|
eventName string
|
||||||
|
@ -88,7 +89,7 @@ func (c *monitorService) StartMonitor(server string) (resp types.JSResp) {
|
||||||
item.cmd = item.client.Monitor(c.ctx, item.ch)
|
item.cmd = item.client.Monitor(c.ctx, item.ch)
|
||||||
item.cmd.Start()
|
item.cmd.Start()
|
||||||
|
|
||||||
go c.processMonitor(item.ch, item.closeCh, item.eventName)
|
go c.processMonitor(&item.mutex, item.ch, item.closeCh, item.eventName)
|
||||||
resp.Success = true
|
resp.Success = true
|
||||||
resp.Data = struct {
|
resp.Data = struct {
|
||||||
EventName string `json:"eventName"`
|
EventName string `json:"eventName"`
|
||||||
|
@ -98,12 +99,23 @@ func (c *monitorService) StartMonitor(server string) (resp types.JSResp) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *monitorService) processMonitor(ch <-chan string, closeCh <-chan struct{}, eventName string) {
|
func (c *monitorService) processMonitor(mutex *sync.Mutex, ch <-chan string, closeCh <-chan struct{}, eventName string) {
|
||||||
|
lastEmitTime := time.Now().Add(-1 * time.Minute)
|
||||||
|
cache := make([]string, 0, 1000)
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case data := <-ch:
|
case data := <-ch:
|
||||||
if data != "OK" {
|
if data != "OK" {
|
||||||
runtime.EventsEmit(c.ctx, eventName, data)
|
go func() {
|
||||||
|
mutex.Lock()
|
||||||
|
defer mutex.Unlock()
|
||||||
|
cache = append(cache, data)
|
||||||
|
if time.Now().Sub(lastEmitTime) > 1*time.Second || len(cache) > 300 {
|
||||||
|
runtime.EventsEmit(c.ctx, eventName, cache)
|
||||||
|
cache = cache[:0:cap(cache)]
|
||||||
|
lastEmitTime = time.Now()
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
case <-closeCh:
|
case <-closeCh:
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
<script setup>
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
value: {
|
||||||
|
type: Number,
|
||||||
|
default: -1,
|
||||||
|
},
|
||||||
|
unit: {
|
||||||
|
type: Number,
|
||||||
|
default: 1,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:value', 'update:unit'])
|
||||||
|
|
||||||
|
const i18n = useI18n()
|
||||||
|
const unit = computed(() => [
|
||||||
|
{ value: 1, label: i18n.t('common.second') },
|
||||||
|
{
|
||||||
|
value: 60,
|
||||||
|
label: i18n.t('common.minute'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 3600,
|
||||||
|
label: i18n.t('common.hour'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 86400,
|
||||||
|
label: i18n.t('common.day'),
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const unitValue = computed(() => {
|
||||||
|
switch (props.unit) {
|
||||||
|
case 60:
|
||||||
|
return 60
|
||||||
|
case 3600:
|
||||||
|
return 3600
|
||||||
|
case 86400:
|
||||||
|
return 86400
|
||||||
|
default:
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<n-input-group>
|
||||||
|
<n-input-number
|
||||||
|
:max="Number.MAX_SAFE_INTEGER"
|
||||||
|
:min="-1"
|
||||||
|
:show-button="false"
|
||||||
|
:value="props.value"
|
||||||
|
class="flex-item-expand"
|
||||||
|
@update:value="(val) => emit('update:value', val)" />
|
||||||
|
<n-select
|
||||||
|
:options="unit"
|
||||||
|
:value="unitValue"
|
||||||
|
style="max-width: 150px"
|
||||||
|
@update:value="(val) => emit('update:unit', val)" />
|
||||||
|
</n-input-group>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
|
@ -1,6 +1,6 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, nextTick, onMounted, onUnmounted, reactive, ref } from 'vue'
|
import { computed, nextTick, onMounted, onUnmounted, reactive, ref } from 'vue'
|
||||||
import { filter, get, includes, isEmpty, join } from 'lodash'
|
import { debounce, filter, get, includes, isEmpty, join } from 'lodash'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useThemeVars } from 'naive-ui'
|
import { useThemeVars } from 'naive-ui'
|
||||||
import useBrowserStore from 'stores/browser.js'
|
import useBrowserStore from 'stores/browser.js'
|
||||||
|
@ -53,6 +53,13 @@ const displayList = computed(() => {
|
||||||
return data.list
|
return data.list
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const _scrollToBottom = () => {
|
||||||
|
nextTick(() => {
|
||||||
|
listRef.value?.scrollTo({ position: 'bottom' })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const scrollToBottom = debounce(_scrollToBottom, 1000, { leading: true, trailing: true })
|
||||||
|
|
||||||
const onStartMonitor = async () => {
|
const onStartMonitor = async () => {
|
||||||
if (isMonitoring.value) {
|
if (isMonitoring.value) {
|
||||||
return
|
return
|
||||||
|
@ -65,11 +72,13 @@ const onStartMonitor = async () => {
|
||||||
}
|
}
|
||||||
data.monitorEvent = get(ret, 'eventName')
|
data.monitorEvent = get(ret, 'eventName')
|
||||||
EventsOn(data.monitorEvent, (content) => {
|
EventsOn(data.monitorEvent, (content) => {
|
||||||
|
if (content instanceof Array) {
|
||||||
|
data.list.push(...content)
|
||||||
|
} else {
|
||||||
data.list.push(content)
|
data.list.push(content)
|
||||||
|
}
|
||||||
if (data.autoShowLast) {
|
if (data.autoShowLast) {
|
||||||
nextTick(() => {
|
scrollToBottom()
|
||||||
listRef.value.scrollTo({ position: 'bottom' })
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -108,7 +117,7 @@ const onCleanLog = () => {
|
||||||
<template>
|
<template>
|
||||||
<div class="content-log content-container fill-height flex-box-v">
|
<div class="content-log content-container fill-height flex-box-v">
|
||||||
<n-form class="flex-item" label-align="left" label-placement="left" label-width="auto" size="small">
|
<n-form class="flex-item" label-align="left" label-placement="left" label-width="auto" size="small">
|
||||||
<n-form-item :label="$t('monitor.actions')">
|
<n-form-item :feedback="$t('monitor.warning')" :label="$t('monitor.actions')">
|
||||||
<n-space>
|
<n-space>
|
||||||
<n-button
|
<n-button
|
||||||
v-if="!isMonitoring"
|
v-if="!isMonitoring"
|
||||||
|
|
|
@ -72,6 +72,15 @@ const onCopyKey = () => {
|
||||||
$message.error(e.message)
|
$message.error(e.message)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onTTL = () => {
|
||||||
|
dialogStore.openTTLDialog({
|
||||||
|
server: props.server,
|
||||||
|
db: props.db,
|
||||||
|
key: binaryKey.value ? props.keyCode : props.keyPath,
|
||||||
|
ttl: props.ttl,
|
||||||
|
})
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -93,7 +102,7 @@ const onCopyKey = () => {
|
||||||
<n-button-group>
|
<n-button-group>
|
||||||
<n-tooltip>
|
<n-tooltip>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<n-button :focusable="false" @click="dialogStore.openTTLDialog(props.ttl)">
|
<n-button :focusable="false" @click="onTTL">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<n-icon :component="Timer" size="18" />
|
<n-icon :component="Timer" size="18" />
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -222,7 +222,7 @@ const actionColumn = {
|
||||||
})
|
})
|
||||||
if (success) {
|
if (success) {
|
||||||
props.value.splice(index, 1)
|
props.value.splice(index, 1)
|
||||||
$message.success(i18n.t('dialogue.delete_key_succ', { key: row.k }))
|
$message.success(i18n.t('dialogue.delete.success', { key: row.k }))
|
||||||
} else {
|
} else {
|
||||||
$message.error(msg)
|
$message.error(msg)
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,7 +194,7 @@ const actionColumn = {
|
||||||
index,
|
index,
|
||||||
})
|
})
|
||||||
if (success) {
|
if (success) {
|
||||||
$message.success(i18n.t('dialogue.delete_key_succ', { key: `#${index + 1}` }))
|
$message.success(i18n.t('dialogue.delete.success', { key: `#${index + 1}` }))
|
||||||
} else {
|
} else {
|
||||||
$message.error(msg)
|
$message.error(msg)
|
||||||
}
|
}
|
||||||
|
|
|
@ -191,7 +191,7 @@ const actionColumn = {
|
||||||
value: row.v,
|
value: row.v,
|
||||||
})
|
})
|
||||||
if (success) {
|
if (success) {
|
||||||
$message.success(i18n.t('dialogue.delete_key_succ', { key: row.v }))
|
$message.success(i18n.t('dialogue.delete.success', { key: row.v }))
|
||||||
} else {
|
} else {
|
||||||
$message.error(msg)
|
$message.error(msg)
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,7 +127,7 @@ const actionColumn = {
|
||||||
ids: row.id,
|
ids: row.id,
|
||||||
})
|
})
|
||||||
if (success) {
|
if (success) {
|
||||||
$message.success(i18n.t('dialogue.delete_key_succ', { key: row.id }))
|
$message.success(i18n.t('dialogue.delete.success', { key: row.id }))
|
||||||
} else {
|
} else {
|
||||||
$message.error(msg)
|
$message.error(msg)
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,7 +129,7 @@ const onDelete = () => {
|
||||||
const { name, db } = data.value
|
const { name, db } = data.value
|
||||||
browserStore.deleteKey(name, db, keyName.value).then((success) => {
|
browserStore.deleteKey(name, db, keyName.value).then((success) => {
|
||||||
if (success) {
|
if (success) {
|
||||||
$message.success(i18n.t('dialogue.delete_key_succ', { key: data.value.keyPath }))
|
$message.success(i18n.t('dialogue.delete.success', { key: data.value.keyPath }))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -238,7 +238,7 @@ const actionColumn = {
|
||||||
value: row.v,
|
value: row.v,
|
||||||
})
|
})
|
||||||
if (success) {
|
if (success) {
|
||||||
$message.success(i18n.t('dialogue.delete_key_succ', { key: row.v }))
|
$message.success(i18n.t('dialogue.delete.success', { key: row.v }))
|
||||||
} else {
|
} else {
|
||||||
$message.error(msg)
|
$message.error(msg)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,15 +5,18 @@ import { useI18n } from 'vue-i18n'
|
||||||
import useBrowserStore from 'stores/browser.js'
|
import useBrowserStore from 'stores/browser.js'
|
||||||
import { isEmpty } from 'lodash'
|
import { isEmpty } from 'lodash'
|
||||||
import FileOpenInput from '@/components/common/FileOpenInput.vue'
|
import FileOpenInput from '@/components/common/FileOpenInput.vue'
|
||||||
|
import TtlInput from '@/components/common/TtlInput.vue'
|
||||||
|
|
||||||
const importKeyForm = reactive({
|
const importKeyForm = reactive({
|
||||||
server: '',
|
server: '',
|
||||||
db: 0,
|
db: 0,
|
||||||
expire: true,
|
|
||||||
reload: true,
|
reload: true,
|
||||||
file: '',
|
file: '',
|
||||||
type: 0,
|
type: 0,
|
||||||
conflict: 0,
|
conflict: 0,
|
||||||
|
ttlType: 0,
|
||||||
|
ttl: -1,
|
||||||
|
ttlUnit: 1,
|
||||||
})
|
})
|
||||||
|
|
||||||
const dialogStore = useDialog()
|
const dialogStore = useDialog()
|
||||||
|
@ -25,11 +28,12 @@ watchEffect(() => {
|
||||||
const { server, db } = dialogStore.importKeyParam
|
const { server, db } = dialogStore.importKeyParam
|
||||||
importKeyForm.server = server
|
importKeyForm.server = server
|
||||||
importKeyForm.db = db
|
importKeyForm.db = db
|
||||||
importKeyForm.expire = true
|
|
||||||
importKeyForm.reload = true
|
importKeyForm.reload = true
|
||||||
importKeyForm.file = ''
|
importKeyForm.file = ''
|
||||||
importKeyForm.type = 0
|
importKeyForm.type = 0
|
||||||
importKeyForm.conflict = 0
|
importKeyForm.conflict = 0
|
||||||
|
importKeyForm.ttlType = 0
|
||||||
|
importKeyForm.ttl = -1
|
||||||
importing.value = false
|
importing.value = false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -46,6 +50,21 @@ const conflictOption = computed(() => [
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
|
const ttlOption = computed(() => [
|
||||||
|
{
|
||||||
|
value: 0,
|
||||||
|
label: i18n.t('dialogue.import.ttl_include'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 1,
|
||||||
|
label: i18n.t('dialogue.import.ttl_ignore'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 2,
|
||||||
|
label: i18n.t('dialogue.import.ttl_custom'),
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
const importEnable = computed(() => {
|
const importEnable = computed(() => {
|
||||||
return !isEmpty(importKeyForm.file)
|
return !isEmpty(importKeyForm.file)
|
||||||
})
|
})
|
||||||
|
@ -53,8 +72,19 @@ const importEnable = computed(() => {
|
||||||
const onConfirmImport = async () => {
|
const onConfirmImport = async () => {
|
||||||
try {
|
try {
|
||||||
importing.value = true
|
importing.value = true
|
||||||
const { server, db, file, conflict, expire, reload } = importKeyForm
|
const { server, db, file, conflict, ttlType, ttl, ttlUnit, reload } = importKeyForm
|
||||||
browserStore.importKeysFromCSVFile(server, db, file, conflict, expire, reload).catch((e) => {})
|
let ttlVal = 0
|
||||||
|
switch (ttlType) {
|
||||||
|
case 0:
|
||||||
|
ttlVal = -1
|
||||||
|
break
|
||||||
|
case 1:
|
||||||
|
ttlVal = 0
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
ttlVal = ttl * (ttlUnit || 1)
|
||||||
|
}
|
||||||
|
browserStore.importKeysFromCSVFile(server, db, file, conflict, ttlVal, reload).catch((e) => {})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
$message.error(e.message)
|
$message.error(e.message)
|
||||||
return
|
return
|
||||||
|
@ -104,15 +134,21 @@ const onClose = () => {
|
||||||
:value="op.value" />
|
:value="op.value" />
|
||||||
</n-radio-group>
|
</n-radio-group>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item :label="$t('dialogue.import.import_expire_title')" :show-label="false">
|
<n-form-item :label="$t('dialogue.import.import_expire_title')">
|
||||||
<n-space :wrap-item="false">
|
<n-space :wrap-item="false">
|
||||||
<n-checkbox v-model:checked="importKeyForm.expire" :autofocus="false">
|
<n-radio-group v-model:value="importKeyForm.ttlType">
|
||||||
{{ $t('dialogue.import.import_expire') }}
|
<n-radio-button v-for="(op, i) in ttlOption" :key="i" :label="op.label" :value="op.value" />
|
||||||
</n-checkbox>
|
</n-radio-group>
|
||||||
|
<ttl-input
|
||||||
|
v-if="importKeyForm.ttlType === 2"
|
||||||
|
v-model:unit="importKeyForm.ttlUnit"
|
||||||
|
v-model:value="importKeyForm.ttl" />
|
||||||
|
</n-space>
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item :label="$t('dialogue.import.import_expire_title')" :show-label="false">
|
||||||
<n-checkbox v-model:checked="importKeyForm.reload" :autofocus="false">
|
<n-checkbox v-model:checked="importKeyForm.reload" :autofocus="false">
|
||||||
{{ $t('dialogue.import.reload') }}
|
{{ $t('dialogue.import.reload') }}
|
||||||
</n-checkbox>
|
</n-checkbox>
|
||||||
</n-space>
|
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</n-form>
|
</n-form>
|
||||||
</n-spin>
|
</n-spin>
|
||||||
|
|
|
@ -214,7 +214,7 @@ const onClose = () => {
|
||||||
</template>
|
</template>
|
||||||
</n-input-number>
|
</n-input-number>
|
||||||
<n-button :focusable="false" secondary type="primary" @click="() => (newForm.ttl = -1)">
|
<n-button :focusable="false" secondary type="primary" @click="() => (newForm.ttl = -1)">
|
||||||
{{ $t('dialogue.key.persist_key') }}
|
{{ $t('interface.forever') }}
|
||||||
</n-button>
|
</n-button>
|
||||||
</n-input-group>
|
</n-input-group>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
|
|
|
@ -1,62 +1,48 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, reactive, watchEffect } from 'vue'
|
import { computed, reactive, ref, watchEffect } from 'vue'
|
||||||
import useDialog from 'stores/dialog'
|
import useDialog from 'stores/dialog'
|
||||||
import useTabStore from 'stores/tab.js'
|
|
||||||
import Binary from '@/components/icons/Binary.vue'
|
|
||||||
import { isEmpty } from 'lodash'
|
|
||||||
import useBrowserStore from 'stores/browser.js'
|
import useBrowserStore from 'stores/browser.js'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { isEmpty, size } from 'lodash'
|
||||||
|
import TtlInput from '@/components/common/TtlInput.vue'
|
||||||
|
|
||||||
const ttlForm = reactive({
|
const ttlForm = reactive({
|
||||||
server: '',
|
server: '',
|
||||||
db: 0,
|
db: 0,
|
||||||
key: '',
|
key: '',
|
||||||
keyCode: null,
|
keys: [],
|
||||||
ttl: -1,
|
ttl: -1,
|
||||||
unit: 1,
|
unit: 1,
|
||||||
})
|
})
|
||||||
|
|
||||||
const dialogStore = useDialog()
|
const dialogStore = useDialog()
|
||||||
const browserStore = useBrowserStore()
|
const browserStore = useBrowserStore()
|
||||||
const tabStore = useTabStore()
|
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
if (dialogStore.ttlDialogVisible) {
|
if (dialogStore.ttlDialogVisible) {
|
||||||
// get ttl from current tab
|
// get ttl from current tab
|
||||||
const tab = tabStore.currentTab
|
const { server, db, key, keys, ttl } = dialogStore.ttlParam
|
||||||
if (tab != null) {
|
ttlForm.server = server
|
||||||
ttlForm.server = tab.name
|
ttlForm.db = db
|
||||||
ttlForm.db = tab.db
|
ttlForm.key = key
|
||||||
ttlForm.key = tab.key
|
ttlForm.keys = keys
|
||||||
ttlForm.keyCode = tab.keyCode
|
|
||||||
ttlForm.unit = 1
|
ttlForm.unit = 1
|
||||||
if (tab.ttl < 0) {
|
if (ttl < 0) {
|
||||||
// forever
|
// forever
|
||||||
ttlForm.ttl = -1
|
ttlForm.ttl = -1
|
||||||
} else {
|
} else {
|
||||||
ttlForm.ttl = tab.ttl
|
ttlForm.ttl = ttl
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
procssing.value = false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const i18n = useI18n()
|
const procssing = ref(false)
|
||||||
const unit = computed(() => [
|
const isBatchAction = computed(() => {
|
||||||
{ value: 1, label: i18n.t('common.second') },
|
return !isEmpty(ttlForm.keys)
|
||||||
{
|
})
|
||||||
value: 60,
|
|
||||||
label: i18n.t('common.minute'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 3600,
|
|
||||||
label: i18n.t('common.hour'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 86400,
|
|
||||||
label: i18n.t('common.day'),
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
|
const i18n = useI18n()
|
||||||
const quickOption = computed(() => [
|
const quickOption = computed(() => [
|
||||||
{ value: -1, unit: 1, label: i18n.t('interface.forever') },
|
{ value: -1, unit: 1, label: i18n.t('interface.forever') },
|
||||||
{ value: 10, unit: 1, label: `10 ${i18n.t('common.second')}` },
|
{ value: 10, unit: 1, label: `10 ${i18n.t('common.second')}` },
|
||||||
|
@ -76,24 +62,20 @@ const onClose = () => {
|
||||||
|
|
||||||
const onConfirm = async () => {
|
const onConfirm = async () => {
|
||||||
try {
|
try {
|
||||||
const tab = tabStore.currentTab
|
procssing.value = true
|
||||||
if (tab == null) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const key = isEmpty(ttlForm.keyCode) ? ttlForm.key : ttlForm.keyCode
|
|
||||||
const ttl = ttlForm.ttl * (ttlForm.unit || 1)
|
const ttl = ttlForm.ttl * (ttlForm.unit || 1)
|
||||||
const success = await browserStore.setTTL(tab.name, tab.db, key, ttl)
|
let success = false
|
||||||
|
if (isBatchAction.value) {
|
||||||
|
success = await browserStore.setTTLs(ttlForm.server, ttlForm.db, ttlForm.keys, ttl)
|
||||||
|
} else {
|
||||||
|
success = await browserStore.setTTL(ttlForm.server, ttlForm.db, ttlForm.key, ttl)
|
||||||
|
}
|
||||||
if (success) {
|
if (success) {
|
||||||
tabStore.updateTTL({
|
|
||||||
server: ttlForm.server,
|
|
||||||
db: ttlForm.db,
|
|
||||||
key: ttlForm.key,
|
|
||||||
ttl: ttl,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
$message.error(e.message || 'set ttl fail')
|
$message.error(e.message || 'set ttl fail')
|
||||||
} finally {
|
} finally {
|
||||||
|
procssing.value = false
|
||||||
dialogStore.closeTTLDialog()
|
dialogStore.closeTTLDialog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,30 +91,20 @@ const onConfirm = async () => {
|
||||||
:negative-text="$t('common.cancel')"
|
:negative-text="$t('common.cancel')"
|
||||||
:on-negative-click="onClose"
|
:on-negative-click="onClose"
|
||||||
:on-positive-click="onConfirm"
|
:on-positive-click="onConfirm"
|
||||||
:positive-button-props="{ focusable: false, size: 'medium' }"
|
:positive-button-props="{ focusable: false, size: 'medium', loading: procssing }"
|
||||||
:positive-text="$t('common.save')"
|
:positive-text="$t('common.save')"
|
||||||
:show-icon="false"
|
:show-icon="false"
|
||||||
:title="$t('dialogue.ttl.title')"
|
:title="
|
||||||
|
isBatchAction ? $t('dialogue.ttl.title_batch', { count: size(ttlForm.keys) }) : $t('dialogue.ttl.title')
|
||||||
|
"
|
||||||
preset="dialog"
|
preset="dialog"
|
||||||
transform-origin="center">
|
transform-origin="center">
|
||||||
<n-form :model="ttlForm" :show-require-mark="false" label-placement="top">
|
<n-form :model="ttlForm" :show-require-mark="false" label-placement="top">
|
||||||
<n-form-item :label="$t('common.key')">
|
<n-form-item v-if="!isBatchAction" :label="$t('common.key')">
|
||||||
<n-input :value="ttlForm.key" readonly>
|
<n-input :value="ttlForm.key" readonly />
|
||||||
<template #prefix>
|
|
||||||
<n-icon v-if="!!ttlForm.keyCode" :component="Binary" size="20" />
|
|
||||||
</template>
|
|
||||||
</n-input>
|
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item :label="$t('interface.ttl')" required>
|
<n-form-item :label="$t('interface.ttl')" required>
|
||||||
<n-input-group>
|
<ttl-input v-model:unit="ttlForm.unit" v-model:value="ttlForm.ttl" />
|
||||||
<n-input-number
|
|
||||||
v-model:value="ttlForm.ttl"
|
|
||||||
:max="Number.MAX_SAFE_INTEGER"
|
|
||||||
:min="-1"
|
|
||||||
:show-button="false"
|
|
||||||
class="flex-item-expand" />
|
|
||||||
<n-select v-model:value="ttlForm.unit" :options="unit" style="max-width: 150px" />
|
|
||||||
</n-input-group>
|
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item :label="$t('dialogue.ttl.quick_set')" :show-feedback="false">
|
<n-form-item :label="$t('dialogue.ttl.quick_set')" :show-feedback="false">
|
||||||
<n-space :wrap="true" :wrap-item="false">
|
<n-space :wrap="true" :wrap-item="false">
|
||||||
|
|
|
@ -27,6 +27,7 @@ import { ConnectionType } from '@/consts/connection_type.js'
|
||||||
import Import from '@/components/icons/Import.vue'
|
import Import from '@/components/icons/Import.vue'
|
||||||
import Down from '@/components/icons/Down.vue'
|
import Down from '@/components/icons/Down.vue'
|
||||||
import Checkbox from '@/components/icons/Checkbox.vue'
|
import Checkbox from '@/components/icons/Checkbox.vue'
|
||||||
|
import Timer from '@/components/icons/Timer.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
server: String,
|
server: String,
|
||||||
|
@ -127,6 +128,7 @@ const onAddKey = () => {
|
||||||
const selectedKey = get(browserTreeRef.value?.getSelectedKey(), 0)
|
const selectedKey = get(browserTreeRef.value?.getSelectedKey(), 0)
|
||||||
if (selectedKey != null) {
|
if (selectedKey != null) {
|
||||||
const node = browserStore.getNode(selectedKey)
|
const node = browserStore.getNode(selectedKey)
|
||||||
|
if (node != null) {
|
||||||
const { type = ConnectionType.RedisValue, redisKey } = node
|
const { type = ConnectionType.RedisValue, redisKey } = node
|
||||||
if (type === ConnectionType.RedisKey) {
|
if (type === ConnectionType.RedisKey) {
|
||||||
// has prefix
|
// has prefix
|
||||||
|
@ -134,6 +136,7 @@ const onAddKey = () => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
dialogStore.openNewKeyDialog('', props.server, props.db)
|
dialogStore.openNewKeyDialog('', props.server, props.db)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,6 +171,10 @@ const onExportChecked = () => {
|
||||||
browserTreeRef.value?.exportCheckedItems()
|
browserTreeRef.value?.exportCheckedItems()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onUpdateTTLChecked = () => {
|
||||||
|
browserTreeRef.value?.updateTTLCheckedItems()
|
||||||
|
}
|
||||||
|
|
||||||
const onImportData = () => {
|
const onImportData = () => {
|
||||||
dialogStore.openImportKeyDialog(props.server, props.db)
|
dialogStore.openImportKeyDialog(props.server, props.db)
|
||||||
}
|
}
|
||||||
|
@ -346,7 +353,7 @@ watch(
|
||||||
:icon="LoadList"
|
:icon="LoadList"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:stroke-width="3.5"
|
:stroke-width="3.5"
|
||||||
size="20"
|
size="21"
|
||||||
t-tooltip="interface.load_more"
|
t-tooltip="interface.load_more"
|
||||||
@click="onLoadMore" />
|
@click="onLoadMore" />
|
||||||
<icon-button
|
<icon-button
|
||||||
|
@ -355,7 +362,7 @@ watch(
|
||||||
:icon="LoadAll"
|
:icon="LoadAll"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:stroke-width="3.5"
|
:stroke-width="3.5"
|
||||||
size="20"
|
size="21"
|
||||||
t-tooltip="interface.load_all"
|
t-tooltip="interface.load_all"
|
||||||
@click="onLoadAll" />
|
@click="onLoadAll" />
|
||||||
<div class="flex-item-expand" style="min-width: 10px" />
|
<div class="flex-item-expand" style="min-width: 10px" />
|
||||||
|
@ -363,7 +370,7 @@ watch(
|
||||||
:button-class="['nav-pane-func-btn']"
|
:button-class="['nav-pane-func-btn']"
|
||||||
:icon="Checkbox"
|
:icon="Checkbox"
|
||||||
:stroke-width="3.5"
|
:stroke-width="3.5"
|
||||||
size="20"
|
size="19"
|
||||||
t-tooltip="interface.check_mode"
|
t-tooltip="interface.check_mode"
|
||||||
@click="inCheckState = true" />
|
@click="inCheckState = true" />
|
||||||
<n-dropdown
|
<n-dropdown
|
||||||
|
@ -385,6 +392,14 @@ watch(
|
||||||
size="20"
|
size="20"
|
||||||
t-tooltip="interface.export_checked"
|
t-tooltip="interface.export_checked"
|
||||||
@click="onExportChecked" />
|
@click="onExportChecked" />
|
||||||
|
<icon-button
|
||||||
|
:button-class="['nav-pane-func-btn']"
|
||||||
|
:disabled="checkedCount <= 0"
|
||||||
|
:icon="Timer"
|
||||||
|
:stroke-width="3.5"
|
||||||
|
size="20"
|
||||||
|
t-tooltip="interface.ttl_checked"
|
||||||
|
@click="onUpdateTTLChecked" />
|
||||||
<icon-button
|
<icon-button
|
||||||
:button-class="['nav-pane-func-btn']"
|
:button-class="['nav-pane-func-btn']"
|
||||||
:disabled="checkedCount <= 0"
|
:disabled="checkedCount <= 0"
|
||||||
|
|
|
@ -233,7 +233,7 @@ const handleSelectContextMenu = (key) => {
|
||||||
$dialog.warning(i18n.t('dialogue.remove_tip', { name: redisKeyName }), () => {
|
$dialog.warning(i18n.t('dialogue.remove_tip', { name: redisKeyName }), () => {
|
||||||
browserStore.deleteKey(props.server, db, redisKey).then((success) => {
|
browserStore.deleteKey(props.server, db, redisKey).then((success) => {
|
||||||
if (success) {
|
if (success) {
|
||||||
$message.success(i18n.t('dialogue.delete_key_succ', { key: redisKeyName }))
|
$message.success(i18n.t('dialogue.delete.success', { key: redisKeyName }))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -469,7 +469,7 @@ const calcLayerMenu = (loading) => {
|
||||||
return [
|
return [
|
||||||
// reload layer enable only full loaded
|
// reload layer enable only full loaded
|
||||||
h(IconButton, {
|
h(IconButton, {
|
||||||
tTooltip: props.fullLoaded ? 'interface.reload' : 'interface.reload',
|
tTooltip: props.fullLoaded ? 'interface.reload' : 'interface.reload_disable',
|
||||||
icon: Refresh,
|
icon: Refresh,
|
||||||
loading: loading === true,
|
loading: loading === true,
|
||||||
disabled: !props.fullLoaded,
|
disabled: !props.fullLoaded,
|
||||||
|
@ -565,7 +565,7 @@ watchEffect(
|
||||||
)
|
)
|
||||||
|
|
||||||
// the NTree node may get incorrect height after change data
|
// the NTree node may get incorrect height after change data
|
||||||
// add key property to force refresh the component and then everything back to normal
|
// add key property for force refresh the component so that everything back to normal
|
||||||
const treeKey = ref(0)
|
const treeKey = ref(0)
|
||||||
defineExpose({
|
defineExpose({
|
||||||
handleSelectContextMenu,
|
handleSelectContextMenu,
|
||||||
|
@ -589,6 +589,17 @@ defineExpose({
|
||||||
dialogStore.openExportKeyDialog(props.server, props.db, redisKeys)
|
dialogStore.openExportKeyDialog(props.server, props.db, redisKeys)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
updateTTLCheckedItems: () => {
|
||||||
|
const checkedKeys = tabStore.currentCheckedKeys
|
||||||
|
const redisKeys = map(checkedKeys, 'redisKey')
|
||||||
|
if (!isEmpty(redisKeys)) {
|
||||||
|
dialogStore.openTTLDialog({
|
||||||
|
server: props.server,
|
||||||
|
db: props.db,
|
||||||
|
keys: redisKeys,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
getSelectedKey: () => {
|
getSelectedKey: () => {
|
||||||
return selectedKeys.value || []
|
return selectedKeys.value || []
|
||||||
},
|
},
|
||||||
|
|
|
@ -33,6 +33,7 @@ const filterPattern = ref('')
|
||||||
stroke-width="4"
|
stroke-width="4"
|
||||||
t-tooltip="interface.new_group"
|
t-tooltip="interface.new_group"
|
||||||
@click="dialogStore.openNewGroupDialog()" />
|
@click="dialogStore.openNewGroupDialog()" />
|
||||||
|
<n-divider vertical />
|
||||||
<n-input v-model:value="filterPattern" :placeholder="$t('interface.filter')" clearable>
|
<n-input v-model:value="filterPattern" :placeholder="$t('interface.filter')" clearable>
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<n-icon :component="Filter" size="20" />
|
<n-icon :component="Filter" size="20" />
|
||||||
|
|
|
@ -447,6 +447,9 @@ const handleDrop = ({ node, dragNode, dropPosition }) => {
|
||||||
if (dragNodeSiblings === null || dragNodeIndex === null) {
|
if (dragNodeSiblings === null || dragNodeIndex === null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (node.type === ConnectionType.Group && dragNode.type === ConnectionType.Group) {
|
||||||
|
return
|
||||||
|
}
|
||||||
dragNodeSiblings.splice(dragNodeIndex, 1)
|
dragNodeSiblings.splice(dragNodeIndex, 1)
|
||||||
if (dropPosition === 'inside') {
|
if (dropPosition === 'inside') {
|
||||||
if (node.children) {
|
if (node.children) {
|
||||||
|
|
|
@ -84,6 +84,7 @@
|
||||||
"quit_check_mode": "Quit Check Mode",
|
"quit_check_mode": "Quit Check Mode",
|
||||||
"delete_checked": "Delete Checked Items",
|
"delete_checked": "Delete Checked Items",
|
||||||
"export_checked": "Export Checked Items",
|
"export_checked": "Export Checked Items",
|
||||||
|
"ttl_checked": "Update TTL for Checked Items",
|
||||||
"copy_value": "Copy Value",
|
"copy_value": "Copy Value",
|
||||||
"edit_value": "Edit Value",
|
"edit_value": "Edit Value",
|
||||||
"save_update": "Save Update",
|
"save_update": "Save Update",
|
||||||
|
@ -106,6 +107,7 @@
|
||||||
"view_as": "View As",
|
"view_as": "View As",
|
||||||
"decode_with": "Decode / Decompression",
|
"decode_with": "Decode / Decompression",
|
||||||
"reload": "Reload",
|
"reload": "Reload",
|
||||||
|
"reload_disable": "Reload will enable after full loaded",
|
||||||
"open_connection": "Open Connection",
|
"open_connection": "Open Connection",
|
||||||
"copy_path": "Copy Path",
|
"copy_path": "Copy Path",
|
||||||
"copy_key": "Copy Key",
|
"copy_key": "Copy Key",
|
||||||
|
@ -146,9 +148,6 @@
|
||||||
"interrupt_connection": "Cancel",
|
"interrupt_connection": "Cancel",
|
||||||
"remove_tip": "{type} \"{name}\" will be deleted",
|
"remove_tip": "{type} \"{name}\" will be deleted",
|
||||||
"remove_group_tip": "Group \"{name}\" and all connections in it will be deleted",
|
"remove_group_tip": "Group \"{name}\" and all connections in it will be deleted",
|
||||||
"delete_key_succ": "\"{key}\" has been deleted",
|
|
||||||
"deleting_key": "Deleting key({index}/{count})",
|
|
||||||
"delete_completed": "Deletion process has been completed, {success} successed, {fail} failed",
|
|
||||||
"rename_binary_key_fail": "Rename binary key name is unsupported",
|
"rename_binary_key_fail": "Rename binary key name is unsupported",
|
||||||
"handle_succ": "Success!",
|
"handle_succ": "Success!",
|
||||||
"handle_cancel": "The operation has been canceled.",
|
"handle_cancel": "The operation has been canceled.",
|
||||||
|
@ -240,7 +239,6 @@
|
||||||
"key": {
|
"key": {
|
||||||
"new": "New Key",
|
"new": "New Key",
|
||||||
"new_name": "New Key Name",
|
"new_name": "New Key Name",
|
||||||
"persist_key": "Persist Key",
|
|
||||||
"server": "Connection",
|
"server": "Connection",
|
||||||
"db_index": "Database Index",
|
"db_index": "Database Index",
|
||||||
"key_expression": "Key Expression",
|
"key_expression": "Key Expression",
|
||||||
|
@ -252,6 +250,11 @@
|
||||||
"confirm_flush": "I know what I'm doing!",
|
"confirm_flush": "I know what I'm doing!",
|
||||||
"confirm_flush_db": "Confirm Flush Database"
|
"confirm_flush_db": "Confirm Flush Database"
|
||||||
},
|
},
|
||||||
|
"delete": {
|
||||||
|
"success": "\"{key}\" has been deleted",
|
||||||
|
"doing": "Deleting key({index}/{count})",
|
||||||
|
"completed": "Deletion process has been completed, {success} successed, {fail} failed"
|
||||||
|
},
|
||||||
"field": {
|
"field": {
|
||||||
"new": "Add New Field",
|
"new": "Add New Field",
|
||||||
"new_item": "Add New Item",
|
"new_item": "Add New Item",
|
||||||
|
@ -288,7 +291,6 @@
|
||||||
"import": {
|
"import": {
|
||||||
"name": "Import Data",
|
"name": "Import Data",
|
||||||
"import_expire_title": "Expiration",
|
"import_expire_title": "Expiration",
|
||||||
"import_expire": "Import Expiration Time",
|
|
||||||
"import": "Import",
|
"import": "Import",
|
||||||
"reload": "Reload Keys After Imported",
|
"reload": "Reload Keys After Imported",
|
||||||
"open_csv_file": "Import File",
|
"open_csv_file": "Import File",
|
||||||
|
@ -296,17 +298,20 @@
|
||||||
"conflict_handle": "Key Conflict Resolution",
|
"conflict_handle": "Key Conflict Resolution",
|
||||||
"conflict_overwrite": "Overwrite",
|
"conflict_overwrite": "Overwrite",
|
||||||
"conflict_ignore": "Ignore",
|
"conflict_ignore": "Ignore",
|
||||||
|
"ttl_include": "Import From File",
|
||||||
|
"ttl_ignore": "Do Not Set",
|
||||||
|
"ttl_custom": "Custom",
|
||||||
"importing": "Importing Keys imported/overwrite:{imported} conflict/fail:{conflict}",
|
"importing": "Importing Keys imported/overwrite:{imported} conflict/fail:{conflict}",
|
||||||
"import_completed": "Import completed, {success} successes, {ignored} failed"
|
"import_completed": "Import completed, {success} successes, {ignored} failed"
|
||||||
},
|
},
|
||||||
"ttl": {
|
"ttl": {
|
||||||
"title": "Set Key TTL",
|
"title": "Update TTL",
|
||||||
"quick_set": "Quick Settings"
|
"title_batch": "Batch Update TTL({count})",
|
||||||
|
"quick_set": "Quick Settings",
|
||||||
|
"success": "All TTL of keys have been updated"
|
||||||
},
|
},
|
||||||
"upgrade": {
|
"upgrade": {
|
||||||
"title": "New Version Available",
|
"title": "New Version Available",
|
||||||
"import_expire_title": "Expiration",
|
|
||||||
"import_expire": "Include Expiration Time",
|
|
||||||
"new_version_tip": "A new version({ver}) is available. Download now?",
|
"new_version_tip": "A new version({ver}) is available. Download now?",
|
||||||
"no_update": "You're update to date",
|
"no_update": "You're update to date",
|
||||||
"download_now": "Download",
|
"download_now": "Download",
|
||||||
|
@ -364,6 +369,7 @@
|
||||||
"monitor": {
|
"monitor": {
|
||||||
"title": "Monitor Commands",
|
"title": "Monitor Commands",
|
||||||
"actions": "Actions",
|
"actions": "Actions",
|
||||||
|
"warning": "Monitor command may lead to server congestion. Please use it cautiously on production servers.",
|
||||||
"start": "Start",
|
"start": "Start",
|
||||||
"stop": "Stop",
|
"stop": "Stop",
|
||||||
"search": "Search",
|
"search": "Search",
|
||||||
|
|
|
@ -215,7 +215,6 @@
|
||||||
"key": {
|
"key": {
|
||||||
"new": "Nova Chave",
|
"new": "Nova Chave",
|
||||||
"new_name": "Novo Nome da Chave",
|
"new_name": "Novo Nome da Chave",
|
||||||
"persist_key": "Persistir Chave",
|
|
||||||
"server": "Conexão",
|
"server": "Conexão",
|
||||||
"db_index": "Índice do Banco de Dados",
|
"db_index": "Índice do Banco de Dados",
|
||||||
"key_expression": "Expressão da Chave",
|
"key_expression": "Expressão da Chave",
|
||||||
|
|
|
@ -84,6 +84,7 @@
|
||||||
"quit_check_mode": "退出勾选模式",
|
"quit_check_mode": "退出勾选模式",
|
||||||
"delete_checked": "删除所选项",
|
"delete_checked": "删除所选项",
|
||||||
"export_checked": "导出所选项",
|
"export_checked": "导出所选项",
|
||||||
|
"ttl_checked": "为所选项更新TTL",
|
||||||
"copy_value": "复制值",
|
"copy_value": "复制值",
|
||||||
"edit_value": "修改值",
|
"edit_value": "修改值",
|
||||||
"save_update": "保存修改",
|
"save_update": "保存修改",
|
||||||
|
@ -106,6 +107,7 @@
|
||||||
"view_as": "查看方式",
|
"view_as": "查看方式",
|
||||||
"decode_with": "解码/解压方式",
|
"decode_with": "解码/解压方式",
|
||||||
"reload": "重新载入",
|
"reload": "重新载入",
|
||||||
|
"reload_disable": "全量加载后可重新载入",
|
||||||
"open_connection": "打开连接",
|
"open_connection": "打开连接",
|
||||||
"copy_path": "复制路径",
|
"copy_path": "复制路径",
|
||||||
"copy_key": "复制键名",
|
"copy_key": "复制键名",
|
||||||
|
@ -146,9 +148,6 @@
|
||||||
"interrupt_connection": "中断连接",
|
"interrupt_connection": "中断连接",
|
||||||
"remove_tip": "{type} \"{name}\" 将会被删除",
|
"remove_tip": "{type} \"{name}\" 将会被删除",
|
||||||
"remove_group_tip": "分组 \"{name}\"及其所有连接将会被删除",
|
"remove_group_tip": "分组 \"{name}\"及其所有连接将会被删除",
|
||||||
"delete_key_succ": "{key} 已被删除",
|
|
||||||
"deleting_key": "正在删除键({index}/{count})",
|
|
||||||
"delete_completed": "已完成删除操作,成功{success}个,失败{fail}个",
|
|
||||||
"rename_binary_key_fail": "不支持重命名二进制键名",
|
"rename_binary_key_fail": "不支持重命名二进制键名",
|
||||||
"handle_succ": "操作成功",
|
"handle_succ": "操作成功",
|
||||||
"handle_cancel": "操作已取消",
|
"handle_cancel": "操作已取消",
|
||||||
|
@ -240,7 +239,6 @@
|
||||||
"key": {
|
"key": {
|
||||||
"new": "添加新键",
|
"new": "添加新键",
|
||||||
"new_name": "新键名",
|
"new_name": "新键名",
|
||||||
"persist_key": "持久化键",
|
|
||||||
"server": "所属连接",
|
"server": "所属连接",
|
||||||
"db_index": "数据库编号",
|
"db_index": "数据库编号",
|
||||||
"key_expression": "键名表达式",
|
"key_expression": "键名表达式",
|
||||||
|
@ -252,6 +250,11 @@
|
||||||
"confirm_flush": "我知道我正在执行的操作!",
|
"confirm_flush": "我知道我正在执行的操作!",
|
||||||
"confirm_flush_db": "确认清空数据库"
|
"confirm_flush_db": "确认清空数据库"
|
||||||
},
|
},
|
||||||
|
"delete": {
|
||||||
|
"success": "{key} 已被删除",
|
||||||
|
"doing": "正在删除键({index}/{count})",
|
||||||
|
"completed": "已完成删除操作,成功{success}个,失败{fail}个"
|
||||||
|
},
|
||||||
"field": {
|
"field": {
|
||||||
"new": "添加新字段",
|
"new": "添加新字段",
|
||||||
"new_item": "添加新元素",
|
"new_item": "添加新元素",
|
||||||
|
@ -288,7 +291,6 @@
|
||||||
"import": {
|
"import": {
|
||||||
"name": "导入数据",
|
"name": "导入数据",
|
||||||
"import_expire_title": "过期时间",
|
"import_expire_title": "过期时间",
|
||||||
"import_expire": "包含键过期时间",
|
|
||||||
"reload": "导入完成后重新载入",
|
"reload": "导入完成后重新载入",
|
||||||
"import": "确认导入",
|
"import": "确认导入",
|
||||||
"open_csv_file": "导入文件路径",
|
"open_csv_file": "导入文件路径",
|
||||||
|
@ -296,12 +298,17 @@
|
||||||
"conflict_handle": "键冲突处理",
|
"conflict_handle": "键冲突处理",
|
||||||
"conflict_overwrite": "覆盖",
|
"conflict_overwrite": "覆盖",
|
||||||
"conflict_ignore": "忽略",
|
"conflict_ignore": "忽略",
|
||||||
|
"ttl_include": "尝试导入",
|
||||||
|
"ttl_ignore": "不设置",
|
||||||
|
"ttl_custom": "自定义",
|
||||||
"importing": "正在导入数据 已导入/覆盖:{imported} 冲突/失败:{conflict}",
|
"importing": "正在导入数据 已导入/覆盖:{imported} 冲突/失败:{conflict}",
|
||||||
"import_completed": "已完成导入操作,成功{success}个,忽略{ignored}个"
|
"import_completed": "已完成导入操作,成功{success}个,忽略{ignored}个"
|
||||||
},
|
},
|
||||||
"ttl": {
|
"ttl": {
|
||||||
"title": "设置键存活时间",
|
"title": "设置键存活时间",
|
||||||
"quick_set": "快捷设置"
|
"title_batch": "批量设置键存活时间({count})",
|
||||||
|
"quick_set": "快捷设置",
|
||||||
|
"success": "已全部更新TTL"
|
||||||
},
|
},
|
||||||
"upgrade": {
|
"upgrade": {
|
||||||
"title": "有可用新版本",
|
"title": "有可用新版本",
|
||||||
|
@ -362,6 +369,7 @@
|
||||||
"monitor": {
|
"monitor": {
|
||||||
"title": "监控命令",
|
"title": "监控命令",
|
||||||
"actions": "操作",
|
"actions": "操作",
|
||||||
|
"warning": "命令监控可能会造成服务端堵塞,请谨慎在生产环境的服务器使用",
|
||||||
"start": "开启监控",
|
"start": "开启监控",
|
||||||
"stop": "停止监控",
|
"stop": "停止监控",
|
||||||
"search": "搜索",
|
"search": "搜索",
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {
|
||||||
AddListItem,
|
AddListItem,
|
||||||
AddStreamValue,
|
AddStreamValue,
|
||||||
AddZSetValue,
|
AddZSetValue,
|
||||||
|
BatchSetTTL,
|
||||||
CleanCmdHistory,
|
CleanCmdHistory,
|
||||||
CloseConnection,
|
CloseConnection,
|
||||||
ConvertValue,
|
ConvertValue,
|
||||||
|
@ -1484,19 +1485,81 @@ const useBrowserStore = defineStore('browser', {
|
||||||
* reset key's ttl
|
* reset key's ttl
|
||||||
* @param {string} server
|
* @param {string} server
|
||||||
* @param {number} db
|
* @param {number} db
|
||||||
* @param {string} key
|
* @param {string|number[]} key
|
||||||
* @param {number} ttl
|
* @param {number} ttl
|
||||||
* @returns {Promise<boolean>}
|
* @returns {Promise<boolean>}
|
||||||
*/
|
*/
|
||||||
async setTTL(server, db, key, ttl) {
|
async setTTL(server, db, key, ttl) {
|
||||||
try {
|
try {
|
||||||
const { success, msg } = await SetKeyTTL(server, db, key, ttl)
|
const { success, msg } = await SetKeyTTL(server, db, key, ttl)
|
||||||
|
if (success) {
|
||||||
|
const tabStore = useTabStore()
|
||||||
|
tabStore.updateTTL({
|
||||||
|
server,
|
||||||
|
db,
|
||||||
|
key,
|
||||||
|
ttl,
|
||||||
|
})
|
||||||
|
}
|
||||||
return success === true
|
return success === true
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async setTTLs(server, db, keys, ttl) {
|
||||||
|
// const msgRef = $message.loading('', { duration: 0, closable: true })
|
||||||
|
// let updated = []
|
||||||
|
// let failCount = 0
|
||||||
|
// let canceled = false
|
||||||
|
const serialNo = Date.now().valueOf().toString()
|
||||||
|
// const eventName = 'ttling:' + serialNo
|
||||||
|
// const cancelEvent = 'ttling:stop:' + serialNo
|
||||||
|
try {
|
||||||
|
// let maxProgress = 0
|
||||||
|
// EventsOn(eventName, ({ total, progress, processing }) => {
|
||||||
|
// // update delete progress
|
||||||
|
// if (progress > maxProgress) {
|
||||||
|
// maxProgress = progress
|
||||||
|
// }
|
||||||
|
// const k = decodeRedisKey(processing)
|
||||||
|
// msgRef.content = i18nGlobal.t('dialogue.delete.doing', {
|
||||||
|
// key: k,
|
||||||
|
// index: maxProgress,
|
||||||
|
// count: total,
|
||||||
|
// })
|
||||||
|
// })
|
||||||
|
// msgRef.onClose = () => {
|
||||||
|
// EventsEmit(cancelEvent)
|
||||||
|
// }
|
||||||
|
const { data, success, msg } = await BatchSetTTL(server, db, keys, ttl, serialNo)
|
||||||
|
if (success) {
|
||||||
|
// canceled = get(data, 'canceled', false)
|
||||||
|
// updated = get(data, 'updated', [])
|
||||||
|
// failCount = get(data, 'failed', 0)
|
||||||
|
} else {
|
||||||
|
$message.error(msg)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
// msgRef.destroy()
|
||||||
|
// EventsOff(eventName)
|
||||||
|
}
|
||||||
|
$message.success(i18nGlobal.t('dialogue.ttl.success'))
|
||||||
|
// const deletedCount = size(updated)
|
||||||
|
// if (canceled) {
|
||||||
|
// $message.info(i18nGlobal.t('dialogue.handle_cancel'))
|
||||||
|
// } else if (failCount <= 0) {
|
||||||
|
// // no fail
|
||||||
|
// $message.success(i18nGlobal.t('dialogue.delete.completed', { success: deletedCount, fail: failCount }))
|
||||||
|
// } else if (failCount >= deletedCount) {
|
||||||
|
// // all fail
|
||||||
|
// $message.error(i18nGlobal.t('dialogue.delete.completed', { success: deletedCount, fail: failCount }))
|
||||||
|
// } else {
|
||||||
|
// // some fail
|
||||||
|
// $message.warn(i18nGlobal.t('dialogue.delete.completed', { success: deletedCount, fail: failCount }))
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* delete redis key
|
* delete redis key
|
||||||
* @param {string} server
|
* @param {string} server
|
||||||
|
@ -1526,6 +1589,8 @@ const useBrowserStore = defineStore('browser', {
|
||||||
// set tab content empty
|
// set tab content empty
|
||||||
const tab = useTabStore()
|
const tab = useTabStore()
|
||||||
tab.emptyTab(server)
|
tab.emptyTab(server)
|
||||||
|
tab.setSelectedKeys(server)
|
||||||
|
tab.setCheckedKeys(server)
|
||||||
return true
|
return true
|
||||||
} finally {
|
} finally {
|
||||||
}
|
}
|
||||||
|
@ -1555,7 +1620,7 @@ const useBrowserStore = defineStore('browser', {
|
||||||
maxProgress = progress
|
maxProgress = progress
|
||||||
}
|
}
|
||||||
const k = decodeRedisKey(processing)
|
const k = decodeRedisKey(processing)
|
||||||
msgRef.content = i18nGlobal.t('dialogue.deleting_key', {
|
msgRef.content = i18nGlobal.t('dialogue.delete.doing', {
|
||||||
key: k,
|
key: k,
|
||||||
index: maxProgress,
|
index: maxProgress,
|
||||||
count: total,
|
count: total,
|
||||||
|
@ -1585,13 +1650,13 @@ const useBrowserStore = defineStore('browser', {
|
||||||
$message.info(i18nGlobal.t('dialogue.handle_cancel'))
|
$message.info(i18nGlobal.t('dialogue.handle_cancel'))
|
||||||
} else if (failCount <= 0) {
|
} else if (failCount <= 0) {
|
||||||
// no fail
|
// no fail
|
||||||
$message.success(i18nGlobal.t('dialogue.delete_completed', { success: deletedCount, fail: failCount }))
|
$message.success(i18nGlobal.t('dialogue.delete.completed', { success: deletedCount, fail: failCount }))
|
||||||
} else if (failCount >= deletedCount) {
|
} else if (failCount >= deletedCount) {
|
||||||
// all fail
|
// all fail
|
||||||
$message.error(i18nGlobal.t('dialogue.delete_completed', { success: deletedCount, fail: failCount }))
|
$message.error(i18nGlobal.t('dialogue.delete.completed', { success: deletedCount, fail: failCount }))
|
||||||
} else {
|
} else {
|
||||||
// some fail
|
// some fail
|
||||||
$message.warn(i18nGlobal.t('dialogue.delete_completed', { success: deletedCount, fail: failCount }))
|
$message.warn(i18nGlobal.t('dialogue.delete.completed', { success: deletedCount, fail: failCount }))
|
||||||
}
|
}
|
||||||
// update ui
|
// update ui
|
||||||
timeout(100).then(async () => {
|
timeout(100).then(async () => {
|
||||||
|
@ -1673,11 +1738,11 @@ const useBrowserStore = defineStore('browser', {
|
||||||
* @param {number} db
|
* @param {number} db
|
||||||
* @param {string} path
|
* @param {string} path
|
||||||
* @param {number} conflict
|
* @param {number} conflict
|
||||||
* @param {boolean} [expire]
|
* @param {number} [ttl] <0:use previous; ==0: persist; >0: custom ttl
|
||||||
* @param {boolean} [reload]
|
* @param {boolean} [reload]
|
||||||
* @return {Promise<void>}
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async importKeysFromCSVFile(server, db, path, conflict, expire, reload) {
|
async importKeysFromCSVFile(server, db, path, conflict, ttl, reload) {
|
||||||
const msgRef = $message.loading('', { duration: 0, closable: true })
|
const msgRef = $message.loading('', { duration: 0, closable: true })
|
||||||
let imported = 0
|
let imported = 0
|
||||||
let ignored = 0
|
let ignored = 0
|
||||||
|
@ -1695,7 +1760,7 @@ const useBrowserStore = defineStore('browser', {
|
||||||
msgRef.onClose = () => {
|
msgRef.onClose = () => {
|
||||||
EventsEmit('import:stop:' + path)
|
EventsEmit('import:stop:' + path)
|
||||||
}
|
}
|
||||||
const { data, success, msg } = await ImportCSV(server, db, path, conflict, expire)
|
const { data, success, msg } = await ImportCSV(server, db, path, conflict, ttl)
|
||||||
if (success) {
|
if (success) {
|
||||||
canceled = get(data, 'canceled', false)
|
canceled = get(data, 'canceled', false)
|
||||||
imported = get(data, 'imported', 0)
|
imported = get(data, 'imported', 0)
|
||||||
|
@ -1720,9 +1785,9 @@ const useBrowserStore = defineStore('browser', {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* flush database
|
* flush database
|
||||||
* @param server
|
* @param {string} server
|
||||||
* @param db
|
* @param {number} db
|
||||||
* @param async
|
* @param {boolean} async
|
||||||
* @return {Promise<boolean>}
|
* @return {Promise<boolean>}
|
||||||
*/
|
*/
|
||||||
async flushDatabase(server, db, async) {
|
async flushDatabase(server, db, async) {
|
||||||
|
|
|
@ -82,8 +82,14 @@ const useDialogStore = defineStore('dialog', {
|
||||||
},
|
},
|
||||||
flushDBDialogVisible: false,
|
flushDBDialogVisible: false,
|
||||||
|
|
||||||
selectTTL: -1,
|
|
||||||
ttlDialogVisible: false,
|
ttlDialogVisible: false,
|
||||||
|
ttlParam: {
|
||||||
|
server: '',
|
||||||
|
db: 0,
|
||||||
|
key: '',
|
||||||
|
keys: [],
|
||||||
|
ttl: 0,
|
||||||
|
},
|
||||||
|
|
||||||
preferencesDialogVisible: false,
|
preferencesDialogVisible: false,
|
||||||
aboutDialogVisible: false,
|
aboutDialogVisible: false,
|
||||||
|
@ -264,12 +270,23 @@ const useDialogStore = defineStore('dialog', {
|
||||||
this.addFieldsDialogVisible = false
|
this.addFieldsDialogVisible = false
|
||||||
},
|
},
|
||||||
|
|
||||||
openTTLDialog(ttl) {
|
/**
|
||||||
this.selectTTL = ttl
|
*
|
||||||
|
* @param {string} server
|
||||||
|
* @param {number} db
|
||||||
|
* @param {string|number[]} [key]
|
||||||
|
* @param {string[]|number[][]} [keys]
|
||||||
|
* @param {number} [ttl]
|
||||||
|
*/
|
||||||
|
openTTLDialog({ server, db, key, keys, ttl = -1 }) {
|
||||||
this.ttlDialogVisible = true
|
this.ttlDialogVisible = true
|
||||||
|
this.ttlParam.server = server
|
||||||
|
this.ttlParam.db = db
|
||||||
|
this.ttlParam.key = key
|
||||||
|
this.ttlParam.keys = keys
|
||||||
|
this.ttlParam.ttl = ttl
|
||||||
},
|
},
|
||||||
closeTTLDialog() {
|
closeTTLDialog() {
|
||||||
this.selectTTL = -1
|
|
||||||
this.ttlDialogVisible = false
|
this.ttlDialogVisible = false
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { assign, find, findIndex, get, isEmpty, pullAt, remove, set, size } from 'lodash'
|
import { assign, find, findIndex, get, isEmpty, pullAt, remove, set, size } from 'lodash'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { TabItem } from '@/objects/tabItem.js'
|
import { TabItem } from '@/objects/tabItem.js'
|
||||||
|
import { decodeRedisKey } from '@/utils/key_convert.js'
|
||||||
|
|
||||||
const useTabStore = defineStore('tab', {
|
const useTabStore = defineStore('tab', {
|
||||||
/**
|
/**
|
||||||
|
@ -546,7 +547,7 @@ const useTabStore = defineStore('tab', {
|
||||||
* @param {number} ttl
|
* @param {number} ttl
|
||||||
*/
|
*/
|
||||||
updateTTL({ server, db, key, ttl }) {
|
updateTTL({ server, db, key, ttl }) {
|
||||||
let tab = find(this.tabList, { name: server, db, key })
|
let tab = find(this.tabList, { name: server, db, key: decodeRedisKey(key) })
|
||||||
if (tab == null) {
|
if (tab == null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -673,7 +674,7 @@ const useTabStore = defineStore('tab', {
|
||||||
* @param {string} server
|
* @param {string} server
|
||||||
* @param {CheckedKey[]} [keys]
|
* @param {CheckedKey[]} [keys]
|
||||||
*/
|
*/
|
||||||
setCheckedKeys(server, keys) {
|
setCheckedKeys(server, keys = null) {
|
||||||
let tab = find(this.tabList, { name: server })
|
let tab = find(this.tabList, { name: server })
|
||||||
if (tab != null) {
|
if (tab != null) {
|
||||||
if (isEmpty(keys)) {
|
if (isEmpty(keys)) {
|
||||||
|
|
Loading…
Reference in New Issue