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,
|
||||
windowid varchar(36) 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,
|
||||
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,
|
||||
windowid varchar(36) 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,
|
||||
cmdid varchar(36) NOT NULL,
|
||||
cmdstr text NOT NULL
|
||||
cmdstr text NOT NULL,
|
||||
ismetacmd boolean
|
||||
);
|
||||
|
@ -63,6 +63,9 @@ func init() {
|
||||
|
||||
registerCmdAlias("remote", RemoteCommand)
|
||||
registerCmdFn("remote:show", RemoteShowCommand)
|
||||
registerCmdFn("remote:showall", RemoteShowAllCommand)
|
||||
|
||||
registerCmdFn("history", HistoryCommand)
|
||||
}
|
||||
|
||||
func getValidCommands() []string {
|
||||
@ -126,6 +129,17 @@ func resolveBool(arg string, def bool) bool {
|
||||
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) {
|
||||
ids, err := resolveIds(ctx, pk, R_Session|R_Window|R_Remote)
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
ids, err := resolveIds(ctx, pk, R_Session|R_Screen|R_Window)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lineId, cmdId := sstore.ReadLineCmdIdFromUpdate(update)
|
||||
lineId, cmdId, rptr := sstore.ReadHistoryDataFromUpdate(update)
|
||||
hitem := &sstore.HistoryItemType{
|
||||
HistoryId: uuid.New().String(),
|
||||
Ts: time.Now().UnixMilli(),
|
||||
@ -177,6 +191,10 @@ func addToHistory(ctx context.Context, pk *scpacket.FeCommandPacketType, update
|
||||
HadError: hadError,
|
||||
CmdId: cmdId,
|
||||
CmdStr: cmdStr,
|
||||
IsMetaCmd: isMetaCmd,
|
||||
}
|
||||
if !isMetaCmd && rptr != nil {
|
||||
hitem.Remote = *rptr
|
||||
}
|
||||
err = sstore.InsertHistoryItem(ctx, hitem)
|
||||
if err != nil {
|
||||
@ -195,7 +213,7 @@ func EvalCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.
|
||||
}
|
||||
update, err := HandleCommand(ctx, newPk)
|
||||
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 {
|
||||
fmt.Printf("[error] adding to history: %v\n", err)
|
||||
// continue...
|
||||
@ -369,11 +387,30 @@ func RemoteShowCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (s
|
||||
if ids.RemoteState != nil {
|
||||
buf.WriteString(fmt.Sprintf(" %-15s %s\n", "cwd", ids.RemoteState.Cwd))
|
||||
}
|
||||
output := buf.String()
|
||||
return sstore.ModelUpdate{
|
||||
Info: &sstore.InfoMsgType{
|
||||
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
|
||||
}
|
||||
@ -903,6 +940,55 @@ func ClearCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore
|
||||
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 {
|
||||
rtn := strings.Split(str, "\n")
|
||||
if rtn[len(rtn)-1] == "" {
|
||||
|
@ -155,6 +155,9 @@ func EvalMetaCommand(ctx context.Context, origPk *scpacket.FeCommandPacketType)
|
||||
if len(origPk.Args) == 0 {
|
||||
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])
|
||||
rtnPk := scpacket.MakeFeCommandPacket()
|
||||
rtnPk.MetaCmd = metaCmd
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@ -107,9 +108,9 @@ func InsertHistoryItem(ctx context.Context, hitem *HistoryItemType) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
query := `INSERT INTO history ( historyid, ts, userid, sessionid, screenid, windowid, lineid, cmdid, haderror, cmdstr) VALUES
|
||||
(:historyid,:ts,:userid,:sessionid,:screenid,:windowid,:lineid,:cmdid,:haderror,:cmdstr)`
|
||||
_, err = db.NamedExec(query, hitem)
|
||||
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,:remoteownerid,:remoteid,:remotename,:ismetacmd)`
|
||||
_, err = db.NamedExec(query, hitem.ToMap())
|
||||
if err != nil {
|
||||
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) {
|
||||
var rtn []*HistoryItemType
|
||||
err := WithTx(ctx, func(tx *TxWrap) error {
|
||||
query := `SELECT * FROM history WHERE sessionid = ? ORDER BY ts DESC LIMIT ?`
|
||||
tx.SelectWrap(&rtn, query, sessionId, maxItems)
|
||||
query := `SELECT count(*) FROM history WHERE sessionid = ?`
|
||||
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
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -206,6 +206,47 @@ func WindowFromMap(m map[string]interface{}) *WindowType {
|
||||
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 {
|
||||
TabColor string `json:"tabcolor,omitempty"`
|
||||
}
|
||||
@ -271,19 +312,24 @@ type ScreenWindowType struct {
|
||||
}
|
||||
|
||||
type HistoryItemType struct {
|
||||
HistoryId string `json:"historyid"`
|
||||
Ts int64 `json:"ts"`
|
||||
UserId string `json:"userid"`
|
||||
SessionId string `json:"sessionid"`
|
||||
ScreenId string `json:"screenid"`
|
||||
WindowId string `json:"windowid"`
|
||||
LineId string `json:"lineid"`
|
||||
HadError bool `json:"haderror"`
|
||||
CmdId string `json:"cmdid"`
|
||||
CmdStr string `json:"cmdstr"`
|
||||
HistoryId string `json:"historyid"`
|
||||
Ts int64 `json:"ts"`
|
||||
UserId string `json:"userid"`
|
||||
SessionId string `json:"sessionid"`
|
||||
ScreenId string `json:"screenid"`
|
||||
WindowId string `json:"windowid"`
|
||||
LineId string `json:"lineid"`
|
||||
HadError bool `json:"haderror"`
|
||||
CmdId string `json:"cmdid"`
|
||||
CmdStr string `json:"cmdstr"`
|
||||
Remote RemotePtrType `json:"remote"`
|
||||
IsMetaCmd bool `json:"ismetacmd"`
|
||||
|
||||
// only for updates
|
||||
Remove bool `json:"remove"`
|
||||
|
||||
// transient (string because of different history orderings)
|
||||
HistoryNum string `json:"historynum"`
|
||||
}
|
||||
|
||||
type RemoteState struct {
|
||||
|
@ -49,15 +49,19 @@ func MakeSingleSessionUpdate(sessionId string) (ModelUpdate, *SessionType) {
|
||||
return update, session
|
||||
}
|
||||
|
||||
func ReadLineCmdIdFromUpdate(update UpdatePacket) (string, string) {
|
||||
func ReadHistoryDataFromUpdate(update UpdatePacket) (string, string, *RemotePtrType) {
|
||||
modelUpdate, ok := update.(ModelUpdate)
|
||||
if !ok {
|
||||
return "", ""
|
||||
return "", "", 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 {
|
||||
|
Loading…
Reference in New Issue
Block a user