-
Notifications
You must be signed in to change notification settings - Fork 487
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
5b4683a
commit 1cf8931
Showing
10 changed files
with
677 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
package services | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"github.com/redis/go-redis/v9" | ||
"github.com/wailsapp/wails/v2/pkg/runtime" | ||
"strings" | ||
"sync" | ||
"tinyrdm/backend/types" | ||
sliceutil "tinyrdm/backend/utils/slice" | ||
strutil "tinyrdm/backend/utils/string" | ||
) | ||
|
||
type cliService struct { | ||
ctx context.Context | ||
ctxCancel context.CancelFunc | ||
mutex sync.Mutex | ||
clients map[string]redis.UniversalClient | ||
selectedDB map[string]int | ||
} | ||
|
||
type cliOutput struct { | ||
Content string `json:"content"` // output content | ||
Prompt string `json:"prompt,omitempty"` // new line prompt, empty if not ready to input | ||
} | ||
|
||
var cli *cliService | ||
var onceCli sync.Once | ||
|
||
func Cli() *cliService { | ||
if cli == nil { | ||
onceCli.Do(func() { | ||
cli = &cliService{ | ||
clients: map[string]redis.UniversalClient{}, | ||
selectedDB: map[string]int{}, | ||
} | ||
}) | ||
} | ||
return cli | ||
} | ||
|
||
func (c *cliService) runCommand(server, data string) { | ||
if cmds := strings.Split(data, " "); len(cmds) > 0 && len(cmds[0]) > 0 { | ||
if client, err := c.getRedisClient(server); err == nil { | ||
args := sliceutil.Map(cmds, func(i int) any { | ||
return cmds[i] | ||
}) | ||
if result, err := client.Do(c.ctx, args...).Result(); err == nil || err == redis.Nil { | ||
if strings.ToLower(cmds[0]) == "select" { | ||
// switch database | ||
if db, ok := strutil.AnyToInt(cmds[1]); ok { | ||
c.selectedDB[server] = db | ||
} | ||
} | ||
|
||
c.echo(server, strutil.AnyToString(result), true) | ||
} else { | ||
c.echoError(server, err.Error()) | ||
} | ||
return | ||
} | ||
} | ||
|
||
c.echoReady(server) | ||
} | ||
|
||
func (c *cliService) echo(server, data string, newLineReady bool) { | ||
output := cliOutput{ | ||
Content: data, | ||
} | ||
if newLineReady { | ||
output.Prompt = fmt.Sprintf("%s:db%d> ", server, c.selectedDB[server]) | ||
} | ||
runtime.EventsEmit(c.ctx, "cmd:output:"+server, output) | ||
} | ||
|
||
func (c *cliService) echoReady(server string) { | ||
c.echo(server, "", true) | ||
} | ||
|
||
func (c *cliService) echoError(server, data string) { | ||
c.echo(server, "\x1b[31m"+data+"\x1b[0m", true) | ||
} | ||
|
||
func (c *cliService) getRedisClient(server string) (redis.UniversalClient, error) { | ||
c.mutex.Lock() | ||
defer c.mutex.Unlock() | ||
|
||
client, ok := c.clients[server] | ||
if !ok { | ||
var err error | ||
conf := Connection().getConnection(server) | ||
if conf == nil { | ||
return nil, fmt.Errorf("no connection profile named: %s", server) | ||
} | ||
if client, err = Connection().createRedisClient(conf.ConnectionConfig); err != nil { | ||
return nil, err | ||
} | ||
c.clients[server] = client | ||
} | ||
return client, nil | ||
} | ||
|
||
func (c *cliService) Start(ctx context.Context) { | ||
c.ctx, c.ctxCancel = context.WithCancel(ctx) | ||
} | ||
|
||
// StartCli start a cli session | ||
func (c *cliService) StartCli(server string, db int) (resp types.JSResp) { | ||
client, err := c.getRedisClient(server) | ||
if err != nil { | ||
resp.Msg = err.Error() | ||
return | ||
} | ||
client.Do(c.ctx, "select", db) | ||
c.selectedDB[server] = db | ||
|
||
// monitor input | ||
runtime.EventsOn(c.ctx, "cmd:input:"+server, func(data ...interface{}) { | ||
if len(data) > 0 { | ||
if str, ok := data[0].(string); ok { | ||
c.runCommand(server, str) | ||
return | ||
} | ||
} | ||
c.echoReady(server) | ||
}) | ||
|
||
// echo prefix | ||
c.echoReady(server) | ||
resp.Success = true | ||
return | ||
} | ||
|
||
// CloseCli close cli session | ||
func (c *cliService) CloseCli(server string) (resp types.JSResp) { | ||
c.mutex.Lock() | ||
defer c.mutex.Unlock() | ||
|
||
if client, ok := c.clients[server]; ok { | ||
client.Close() | ||
delete(c.clients, server) | ||
delete(c.selectedDB, server) | ||
} | ||
runtime.EventsOff(c.ctx, "cmd:input:"+server) | ||
resp.Success = true | ||
return | ||
} | ||
|
||
// CloseAll close all cli sessions | ||
func (c *cliService) CloseAll() { | ||
if c.ctxCancel != nil { | ||
c.ctxCancel() | ||
} | ||
|
||
for server := range c.clients { | ||
c.CloseCli(server) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
package strutil | ||
|
||
import ( | ||
"encoding/json" | ||
"strconv" | ||
sliceutil "tinyrdm/backend/utils/slice" | ||
) | ||
|
||
func AnyToString(value interface{}) (s string) { | ||
if value == nil { | ||
return | ||
} | ||
|
||
switch value.(type) { | ||
case float64: | ||
ft := value.(float64) | ||
s = strconv.FormatFloat(ft, 'f', -1, 64) | ||
case float32: | ||
ft := value.(float32) | ||
s = strconv.FormatFloat(float64(ft), 'f', -1, 64) | ||
case int: | ||
it := value.(int) | ||
s = strconv.Itoa(it) | ||
case uint: | ||
it := value.(uint) | ||
s = strconv.Itoa(int(it)) | ||
case int8: | ||
it := value.(int8) | ||
s = strconv.Itoa(int(it)) | ||
case uint8: | ||
it := value.(uint8) | ||
s = strconv.Itoa(int(it)) | ||
case int16: | ||
it := value.(int16) | ||
s = strconv.Itoa(int(it)) | ||
case uint16: | ||
it := value.(uint16) | ||
s = strconv.Itoa(int(it)) | ||
case int32: | ||
it := value.(int32) | ||
s = strconv.Itoa(int(it)) | ||
case uint32: | ||
it := value.(uint32) | ||
s = strconv.Itoa(int(it)) | ||
case int64: | ||
it := value.(int64) | ||
s = strconv.FormatInt(it, 10) | ||
case uint64: | ||
it := value.(uint64) | ||
s = strconv.FormatUint(it, 10) | ||
case string: | ||
s = value.(string) | ||
case bool: | ||
val, _ := value.(bool) | ||
if val { | ||
s = "True" | ||
} else { | ||
s = "False" | ||
} | ||
case []byte: | ||
s = string(value.([]byte)) | ||
case []string: | ||
ss := value.([]string) | ||
anyStr := sliceutil.Map(ss, func(i int) string { | ||
str := AnyToString(ss[i]) | ||
return strconv.Itoa(i+1) + ") \"" + str + "\"" | ||
}) | ||
s = sliceutil.JoinString(anyStr, "\r\n") | ||
case []any: | ||
as := value.([]any) | ||
anyItems := sliceutil.Map(as, func(i int) string { | ||
str := AnyToString(as[i]) | ||
return strconv.Itoa(i+1) + ") \"" + str + "\"" | ||
}) | ||
s = sliceutil.JoinString(anyItems, "\r\n") | ||
default: | ||
b, _ := json.Marshal(value) | ||
s = string(b) | ||
} | ||
|
||
return | ||
} | ||
|
||
//func AnyToHex(val any) (string, bool) { | ||
// var src string | ||
// switch val.(type) { | ||
// case string: | ||
// src = val.(string) | ||
// case []byte: | ||
// src = string(val.([]byte)) | ||
// } | ||
// | ||
// if len(src) <= 0 { | ||
// return "", false | ||
// } | ||
// | ||
// var output strings.Builder | ||
// for i := range src { | ||
// if !utf8.ValidString(src[i : i+1]) { | ||
// output.WriteString(fmt.Sprintf("\\x%02x", src[i:i+1])) | ||
// } else { | ||
// output.WriteString(src[i : i+1]) | ||
// } | ||
// } | ||
// | ||
// return output.String(), true | ||
//} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.