mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-22 16:48:23 +01:00
working on history command, remote:showall
This commit is contained in:
parent
9d6cc1f67a
commit
f2a5985349
@ -114,7 +114,11 @@ CREATE TABLE history (
|
|||||||
screenid varchar(36) NOT NULL,
|
screenid varchar(36) NOT NULL,
|
||||||
windowid varchar(36) NOT NULL,
|
windowid varchar(36) NOT NULL,
|
||||||
lineid int NOT NULL,
|
lineid int NOT NULL,
|
||||||
|
remoteownerid varchar(36) NOT NULL,
|
||||||
|
remoteid varchar(36) NOT NULL,
|
||||||
|
remotename varchar(50) NOT NULL,
|
||||||
haderror boolean NOT NULL,
|
haderror boolean NOT NULL,
|
||||||
cmdid varchar(36) NOT NULL,
|
cmdid varchar(36) NOT NULL,
|
||||||
cmdstr text NOT NULL
|
cmdstr text NOT NULL,
|
||||||
|
ismetacmd boolean
|
||||||
);
|
);
|
||||||
|
@ -107,7 +107,11 @@ CREATE TABLE history (
|
|||||||
screenid varchar(36) NOT NULL,
|
screenid varchar(36) NOT NULL,
|
||||||
windowid varchar(36) NOT NULL,
|
windowid varchar(36) NOT NULL,
|
||||||
lineid int NOT NULL,
|
lineid int NOT NULL,
|
||||||
|
remoteownerid varchar(36) NOT NULL,
|
||||||
|
remoteid varchar(36) NOT NULL,
|
||||||
|
remotename varchar(50) NOT NULL,
|
||||||
haderror boolean NOT NULL,
|
haderror boolean NOT NULL,
|
||||||
cmdid varchar(36) NOT NULL,
|
cmdid varchar(36) NOT NULL,
|
||||||
cmdstr text NOT NULL
|
cmdstr text NOT NULL,
|
||||||
|
ismetacmd boolean
|
||||||
);
|
);
|
||||||
|
@ -63,6 +63,9 @@ func init() {
|
|||||||
|
|
||||||
registerCmdAlias("remote", RemoteCommand)
|
registerCmdAlias("remote", RemoteCommand)
|
||||||
registerCmdFn("remote:show", RemoteShowCommand)
|
registerCmdFn("remote:show", RemoteShowCommand)
|
||||||
|
registerCmdFn("remote:showall", RemoteShowAllCommand)
|
||||||
|
|
||||||
|
registerCmdFn("history", HistoryCommand)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getValidCommands() []string {
|
func getValidCommands() []string {
|
||||||
@ -126,6 +129,17 @@ func resolveBool(arg string, def bool) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resolveInt(arg string, def int) (int, error) {
|
||||||
|
if arg == "" {
|
||||||
|
return def, nil
|
||||||
|
}
|
||||||
|
ival, err := strconv.Atoi(arg)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return ival, nil
|
||||||
|
}
|
||||||
|
|
||||||
func RunCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
|
func RunCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
|
||||||
ids, err := resolveIds(ctx, pk, R_Session|R_Window|R_Remote)
|
ids, err := resolveIds(ctx, pk, R_Session|R_Window|R_Remote)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -159,13 +173,13 @@ func RunCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.U
|
|||||||
return sstore.ModelUpdate{Line: rtnLine, Cmd: cmd}, nil
|
return sstore.ModelUpdate{Line: rtnLine, Cmd: cmd}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func addToHistory(ctx context.Context, pk *scpacket.FeCommandPacketType, update sstore.UpdatePacket, hadError bool) error {
|
func addToHistory(ctx context.Context, pk *scpacket.FeCommandPacketType, update sstore.UpdatePacket, isMetaCmd bool, hadError bool) error {
|
||||||
cmdStr := firstArg(pk)
|
cmdStr := firstArg(pk)
|
||||||
ids, err := resolveIds(ctx, pk, R_Session|R_Screen|R_Window)
|
ids, err := resolveIds(ctx, pk, R_Session|R_Screen|R_Window)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
lineId, cmdId := sstore.ReadLineCmdIdFromUpdate(update)
|
lineId, cmdId, rptr := sstore.ReadHistoryDataFromUpdate(update)
|
||||||
hitem := &sstore.HistoryItemType{
|
hitem := &sstore.HistoryItemType{
|
||||||
HistoryId: uuid.New().String(),
|
HistoryId: uuid.New().String(),
|
||||||
Ts: time.Now().UnixMilli(),
|
Ts: time.Now().UnixMilli(),
|
||||||
@ -177,6 +191,10 @@ func addToHistory(ctx context.Context, pk *scpacket.FeCommandPacketType, update
|
|||||||
HadError: hadError,
|
HadError: hadError,
|
||||||
CmdId: cmdId,
|
CmdId: cmdId,
|
||||||
CmdStr: cmdStr,
|
CmdStr: cmdStr,
|
||||||
|
IsMetaCmd: isMetaCmd,
|
||||||
|
}
|
||||||
|
if !isMetaCmd && rptr != nil {
|
||||||
|
hitem.Remote = *rptr
|
||||||
}
|
}
|
||||||
err = sstore.InsertHistoryItem(ctx, hitem)
|
err = sstore.InsertHistoryItem(ctx, hitem)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -195,7 +213,7 @@ func EvalCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.
|
|||||||
}
|
}
|
||||||
update, err := HandleCommand(ctx, newPk)
|
update, err := HandleCommand(ctx, newPk)
|
||||||
if !resolveBool(pk.Kwargs["nohist"], false) {
|
if !resolveBool(pk.Kwargs["nohist"], false) {
|
||||||
err := addToHistory(ctx, pk, update, (err != nil))
|
err := addToHistory(ctx, pk, update, (newPk.MetaCmd != "run"), (err != nil))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("[error] adding to history: %v\n", err)
|
fmt.Printf("[error] adding to history: %v\n", err)
|
||||||
// continue...
|
// continue...
|
||||||
@ -369,11 +387,30 @@ func RemoteShowCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (s
|
|||||||
if ids.RemoteState != nil {
|
if ids.RemoteState != nil {
|
||||||
buf.WriteString(fmt.Sprintf(" %-15s %s\n", "cwd", ids.RemoteState.Cwd))
|
buf.WriteString(fmt.Sprintf(" %-15s %s\n", "cwd", ids.RemoteState.Cwd))
|
||||||
}
|
}
|
||||||
output := buf.String()
|
|
||||||
return sstore.ModelUpdate{
|
return sstore.ModelUpdate{
|
||||||
Info: &sstore.InfoMsgType{
|
Info: &sstore.InfoMsgType{
|
||||||
InfoTitle: fmt.Sprintf("show remote '%s' info", ids.RemoteDisplayName),
|
InfoTitle: fmt.Sprintf("show remote '%s' info", ids.RemoteDisplayName),
|
||||||
InfoLines: splitLinesForInfo(output),
|
InfoLines: splitLinesForInfo(buf.String()),
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemoteShowAllCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
|
||||||
|
stateArr := remote.GetAllRemoteState()
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for _, rstate := range stateArr {
|
||||||
|
var name string
|
||||||
|
if rstate.RemoteAlias == "" {
|
||||||
|
name = rstate.RemoteCanonicalName
|
||||||
|
} else {
|
||||||
|
name = fmt.Sprintf("%s (%s)", rstate.RemoteCanonicalName, rstate.RemoteAlias)
|
||||||
|
}
|
||||||
|
buf.WriteString(fmt.Sprintf("%-12s %-5s %8s %s\n", rstate.Status, rstate.RemoteType, rstate.RemoteId[0:8], name))
|
||||||
|
}
|
||||||
|
return sstore.ModelUpdate{
|
||||||
|
Info: &sstore.InfoMsgType{
|
||||||
|
InfoTitle: fmt.Sprintf("show all remote info"),
|
||||||
|
InfoLines: splitLinesForInfo(buf.String()),
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@ -903,6 +940,55 @@ func ClearCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore
|
|||||||
return update, nil
|
return update, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DefaultMaxHistoryItems = 10000
|
||||||
|
|
||||||
|
func HistoryCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
|
||||||
|
ids, err := resolveIds(ctx, pk, R_Session|R_Screen|R_Window|R_Remote)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
maxItems, err := resolveInt(pk.Kwargs["maxitems"], DefaultMaxHistoryItems)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid maxitems value '%s' (must be a number): %v", pk.Kwargs["maxitems"], err)
|
||||||
|
}
|
||||||
|
if maxItems < 0 {
|
||||||
|
return nil, fmt.Errorf("invalid maxitems value '%s' (cannot be negative)", maxItems)
|
||||||
|
}
|
||||||
|
if maxItems == 0 {
|
||||||
|
maxItems = DefaultMaxHistoryItems
|
||||||
|
}
|
||||||
|
hitems, err := sstore.GetSessionHistoryItems(ctx, ids.SessionId, maxItems)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var filteredItems []*sstore.HistoryItemType
|
||||||
|
for _, hitem := range hitems {
|
||||||
|
if hitem.ScreenId == ids.ScreenId && hitem.WindowId == ids.WindowId && (hitem.Remote == ids.RemotePtr || hitem.IsMetaCmd) {
|
||||||
|
filteredItems = append(filteredItems, hitem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if len(filteredItems) == 0 {
|
||||||
|
buf.WriteString("(no history)")
|
||||||
|
} else {
|
||||||
|
for idx := len(filteredItems) - 1; idx >= 0; idx-- {
|
||||||
|
hitem := filteredItems[idx]
|
||||||
|
hnumStr := hitem.HistoryNum
|
||||||
|
if hitem.IsMetaCmd {
|
||||||
|
hnumStr = "*" + hnumStr
|
||||||
|
}
|
||||||
|
hstr := fmt.Sprintf("%6s %s\n", hnumStr, hitem.CmdStr)
|
||||||
|
buf.WriteString(hstr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
update := &sstore.ModelUpdate{}
|
||||||
|
update.Info = &sstore.InfoMsgType{
|
||||||
|
InfoMsg: fmt.Sprintf("history, limited to current session, screen, window, and remote (maxitems=%d)", maxItems),
|
||||||
|
InfoLines: splitLinesForInfo(buf.String()),
|
||||||
|
}
|
||||||
|
return update, nil
|
||||||
|
}
|
||||||
|
|
||||||
func splitLinesForInfo(str string) []string {
|
func splitLinesForInfo(str string) []string {
|
||||||
rtn := strings.Split(str, "\n")
|
rtn := strings.Split(str, "\n")
|
||||||
if rtn[len(rtn)-1] == "" {
|
if rtn[len(rtn)-1] == "" {
|
||||||
|
@ -155,6 +155,9 @@ func EvalMetaCommand(ctx context.Context, origPk *scpacket.FeCommandPacketType)
|
|||||||
if len(origPk.Args) == 0 {
|
if len(origPk.Args) == 0 {
|
||||||
return nil, fmt.Errorf("empty command (no fields)")
|
return nil, fmt.Errorf("empty command (no fields)")
|
||||||
}
|
}
|
||||||
|
if strings.TrimSpace(origPk.Args[0]) == "" {
|
||||||
|
return nil, fmt.Errorf("empty command")
|
||||||
|
}
|
||||||
metaCmd, metaSubCmd, commandArgs := parseMetaCmd(origPk.Args[0])
|
metaCmd, metaSubCmd, commandArgs := parseMetaCmd(origPk.Args[0])
|
||||||
rtnPk := scpacket.MakeFeCommandPacket()
|
rtnPk := scpacket.MakeFeCommandPacket()
|
||||||
rtnPk.MetaCmd = metaCmd
|
rtnPk.MetaCmd = metaCmd
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
@ -107,9 +108,9 @@ func InsertHistoryItem(ctx context.Context, hitem *HistoryItemType) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
query := `INSERT INTO history ( historyid, ts, userid, sessionid, screenid, windowid, lineid, cmdid, haderror, cmdstr) VALUES
|
query := `INSERT INTO history ( historyid, ts, userid, sessionid, screenid, windowid, lineid, cmdid, haderror, cmdstr, remoteownerid, remoteid, remotename, ismetacmd) VALUES
|
||||||
(:historyid,:ts,:userid,:sessionid,:screenid,:windowid,:lineid,:cmdid,:haderror,:cmdstr)`
|
(:historyid,:ts,:userid,:sessionid,:screenid,:windowid,:lineid,:cmdid,:haderror,:cmdstr,:remoteownerid,:remoteid,:remotename,:ismetacmd)`
|
||||||
_, err = db.NamedExec(query, hitem)
|
_, err = db.NamedExec(query, hitem.ToMap())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -119,8 +120,16 @@ func InsertHistoryItem(ctx context.Context, hitem *HistoryItemType) error {
|
|||||||
func GetSessionHistoryItems(ctx context.Context, sessionId string, maxItems int) ([]*HistoryItemType, error) {
|
func GetSessionHistoryItems(ctx context.Context, sessionId string, maxItems int) ([]*HistoryItemType, error) {
|
||||||
var rtn []*HistoryItemType
|
var rtn []*HistoryItemType
|
||||||
err := WithTx(ctx, func(tx *TxWrap) error {
|
err := WithTx(ctx, func(tx *TxWrap) error {
|
||||||
query := `SELECT * FROM history WHERE sessionid = ? ORDER BY ts DESC LIMIT ?`
|
query := `SELECT count(*) FROM history WHERE sessionid = ?`
|
||||||
tx.SelectWrap(&rtn, query, sessionId, maxItems)
|
totalNum := tx.GetInt(query, sessionId)
|
||||||
|
query = `SELECT * FROM history WHERE sessionid = ? ORDER BY ts DESC, historyid LIMIT ?`
|
||||||
|
marr := tx.SelectMaps(query, sessionId, maxItems)
|
||||||
|
for idx, m := range marr {
|
||||||
|
hnum := totalNum - idx
|
||||||
|
hitem := HistoryItemFromMap(m)
|
||||||
|
hitem.HistoryNum = strconv.Itoa(hnum)
|
||||||
|
rtn = append(rtn, hitem)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -206,6 +206,47 @@ func WindowFromMap(m map[string]interface{}) *WindowType {
|
|||||||
return &w
|
return &w
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *HistoryItemType) ToMap() map[string]interface{} {
|
||||||
|
rtn := make(map[string]interface{})
|
||||||
|
rtn["historyid"] = h.HistoryId
|
||||||
|
rtn["ts"] = h.Ts
|
||||||
|
rtn["userid"] = h.UserId
|
||||||
|
rtn["sessionid"] = h.SessionId
|
||||||
|
rtn["screenid"] = h.ScreenId
|
||||||
|
rtn["windowid"] = h.WindowId
|
||||||
|
rtn["lineid"] = h.LineId
|
||||||
|
rtn["haderror"] = h.HadError
|
||||||
|
rtn["cmdid"] = h.CmdId
|
||||||
|
rtn["cmdstr"] = h.CmdStr
|
||||||
|
rtn["remoteownerid"] = h.Remote.OwnerId
|
||||||
|
rtn["remoteid"] = h.Remote.RemoteId
|
||||||
|
rtn["remotename"] = h.Remote.Name
|
||||||
|
rtn["ismetacmd"] = h.IsMetaCmd
|
||||||
|
return rtn
|
||||||
|
}
|
||||||
|
|
||||||
|
func HistoryItemFromMap(m map[string]interface{}) *HistoryItemType {
|
||||||
|
if len(m) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var h HistoryItemType
|
||||||
|
quickSetStr(&h.HistoryId, m, "historyid")
|
||||||
|
quickSetInt64(&h.Ts, m, "ts")
|
||||||
|
quickSetStr(&h.UserId, m, "userid")
|
||||||
|
quickSetStr(&h.SessionId, m, "sessionid")
|
||||||
|
quickSetStr(&h.ScreenId, m, "screenid")
|
||||||
|
quickSetStr(&h.WindowId, m, "windowid")
|
||||||
|
quickSetStr(&h.LineId, m, "lineid")
|
||||||
|
quickSetBool(&h.HadError, m, "haderror")
|
||||||
|
quickSetStr(&h.CmdId, m, "cmdid")
|
||||||
|
quickSetStr(&h.CmdStr, m, "cmdstr")
|
||||||
|
quickSetStr(&h.Remote.OwnerId, m, "remoteownerid")
|
||||||
|
quickSetStr(&h.Remote.RemoteId, m, "remoteid")
|
||||||
|
quickSetStr(&h.Remote.Name, m, "remotename")
|
||||||
|
quickSetBool(&h.IsMetaCmd, m, "ismetacmd")
|
||||||
|
return &h
|
||||||
|
}
|
||||||
|
|
||||||
type ScreenOptsType struct {
|
type ScreenOptsType struct {
|
||||||
TabColor string `json:"tabcolor,omitempty"`
|
TabColor string `json:"tabcolor,omitempty"`
|
||||||
}
|
}
|
||||||
@ -281,9 +322,14 @@ type HistoryItemType struct {
|
|||||||
HadError bool `json:"haderror"`
|
HadError bool `json:"haderror"`
|
||||||
CmdId string `json:"cmdid"`
|
CmdId string `json:"cmdid"`
|
||||||
CmdStr string `json:"cmdstr"`
|
CmdStr string `json:"cmdstr"`
|
||||||
|
Remote RemotePtrType `json:"remote"`
|
||||||
|
IsMetaCmd bool `json:"ismetacmd"`
|
||||||
|
|
||||||
// only for updates
|
// only for updates
|
||||||
Remove bool `json:"remove"`
|
Remove bool `json:"remove"`
|
||||||
|
|
||||||
|
// transient (string because of different history orderings)
|
||||||
|
HistoryNum string `json:"historynum"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RemoteState struct {
|
type RemoteState struct {
|
||||||
|
@ -49,15 +49,19 @@ func MakeSingleSessionUpdate(sessionId string) (ModelUpdate, *SessionType) {
|
|||||||
return update, session
|
return update, session
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadLineCmdIdFromUpdate(update UpdatePacket) (string, string) {
|
func ReadHistoryDataFromUpdate(update UpdatePacket) (string, string, *RemotePtrType) {
|
||||||
modelUpdate, ok := update.(ModelUpdate)
|
modelUpdate, ok := update.(ModelUpdate)
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", ""
|
return "", "", nil
|
||||||
}
|
}
|
||||||
if modelUpdate.Line == nil {
|
if modelUpdate.Line == nil {
|
||||||
return "", ""
|
return "", "", nil
|
||||||
}
|
}
|
||||||
return modelUpdate.Line.LineId, modelUpdate.Line.CmdId
|
var rptr *RemotePtrType
|
||||||
|
if modelUpdate.Cmd != nil {
|
||||||
|
rptr = &modelUpdate.Cmd.Remote
|
||||||
|
}
|
||||||
|
return modelUpdate.Line.LineId, modelUpdate.Line.CmdId, rptr
|
||||||
}
|
}
|
||||||
|
|
||||||
type InfoMsgType struct {
|
type InfoMsgType struct {
|
||||||
|
Loading…
Reference in New Issue
Block a user