diff --git a/cmd/main-server.go b/cmd/main-server.go index 409730ac..9364fe5f 100644 --- a/cmd/main-server.go +++ b/cmd/main-server.go @@ -10,6 +10,7 @@ import ( "net/http" "os" "runtime/debug" + "strconv" "strings" "sync" "time" @@ -163,6 +164,37 @@ func HandleGetRemotes(w http.ResponseWriter, r *http.Request) { return } +// params: sessionid +func HandleGetHistory(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin")) + w.Header().Set("Access-Control-Allow-Credentials", "true") + w.Header().Set("Vary", "Origin") + w.Header().Set("Cache-Control", "no-cache") + qvals := r.URL.Query() + sessionId := qvals.Get("sessionid") + if _, err := uuid.Parse(sessionId); err != nil { + WriteJsonError(w, fmt.Errorf("invalid sessionid: %w", err)) + return + } + numItems := 1000 + numStr := qvals.Get("num") + if numStr != "" { + parsedNum, err := strconv.Atoi(numStr) + if err == nil { + numItems = parsedNum + } + } + hitems, err := sstore.GetSessionHistoryItems(r.Context(), sessionId, numItems) + if err != nil { + WriteJsonError(w, err) + return + } + rtnMap := make(map[string]interface{}) + rtnMap["history"] = hitems + WriteJsonSuccess(w, rtnMap) + return +} + // params: sessionid, windowid func HandleGetWindow(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin")) @@ -441,6 +473,7 @@ func main() { gr.HandleFunc("/api/get-all-sessions", HandleGetAllSessions) gr.HandleFunc("/api/get-window", HandleGetWindow) gr.HandleFunc("/api/get-remotes", HandleGetRemotes) + gr.HandleFunc("/api/get-history", HandleGetHistory) gr.HandleFunc("/api/run-command", HandleRunCommand).Methods("GET", "POST", "OPTIONS") server := &http.Server{ Addr: MainServerAddr, diff --git a/db/migrations/000001_init.up.sql b/db/migrations/000001_init.up.sql index 21ae524d..5aa14286 100644 --- a/db/migrations/000001_init.up.sql +++ b/db/migrations/000001_init.up.sql @@ -88,6 +88,7 @@ CREATE TABLE history ( screenid varchar(36) NOT NULL, windowid varchar(36) NOT NULL, lineid int NOT NULL, + haderror boolean NOT NULL, cmdid varchar(36) NOT NULL, cmdstr text NOT NULL ); diff --git a/db/schema.sql b/db/schema.sql index 1acc4fff..043cc348 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -82,6 +82,7 @@ CREATE TABLE history ( screenid varchar(36) NOT NULL, windowid varchar(36) NOT NULL, lineid int NOT NULL, + haderror boolean NOT NULL, cmdid varchar(36) NOT NULL, cmdstr text NOT NULL ); diff --git a/pkg/cmdrunner/cmdrunner.go b/pkg/cmdrunner/cmdrunner.go index 0b27579c..479ed4a3 100644 --- a/pkg/cmdrunner/cmdrunner.go +++ b/pkg/cmdrunner/cmdrunner.go @@ -9,6 +9,7 @@ import ( "sort" "strconv" "strings" + "time" "github.com/google/uuid" "github.com/scripthaus-dev/mshell/pkg/base" @@ -278,7 +279,29 @@ func RunCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.U return sstore.LineUpdate{Line: rtnLine, Cmd: cmd}, nil } -func addToHistory(ctx context.Context, pk *scpacket.FeCommandPacketType, cmdStr string) error { +func addToHistory(ctx context.Context, pk *scpacket.FeCommandPacketType, update sstore.UpdatePacket, 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) + hitem := &sstore.HistoryItemType{ + HistoryId: uuid.New().String(), + Ts: time.Now().UnixMilli(), + UserId: DefaultUserId, + SessionId: ids.SessionId, + ScreenId: ids.ScreenId, + WindowId: ids.WindowId, + LineId: lineId, + HadError: hadError, + CmdId: cmdId, + CmdStr: cmdStr, + } + err = sstore.InsertHistoryItem(ctx, hitem) + if err != nil { + return err + } return nil } @@ -291,9 +314,19 @@ func EvalCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore. if commandStr == "" { return nil, fmt.Errorf("/eval, invalid emtpty command") } + update, err := evalCommandInternal(ctx, pk) if !resolveBool(pk.Kwargs["nohist"], false) { - addToHistory(ctx, pk, pk.Args[0]) + err := addToHistory(ctx, pk, update, (err != nil)) + if err != nil { + fmt.Printf("[error] adding to history: %w\n", err) + // continue... + } } + return update, err +} + +func evalCommandInternal(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { + commandStr := strings.TrimSpace(pk.Args[0]) metaCmd := "" metaSubCmd := "" if commandStr == "cd" || strings.HasPrefix(commandStr, "cd ") { @@ -337,7 +370,6 @@ func EvalCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore. newPk.Kwargs[fields[0]] = fields[1] } } - return HandleCommand(ctx, newPk) } diff --git a/pkg/sstore/dbops.go b/pkg/sstore/dbops.go index e88d6d97..7218cc12 100644 --- a/pkg/sstore/dbops.go +++ b/pkg/sstore/dbops.go @@ -93,8 +93,8 @@ 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, cmdstr) VALUES - (:historyid,:ts,:userid,:sessionid,:screenid,:windowid,:lineid,:cmdid,:cmdstr)` + 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) if err != nil { return err @@ -102,6 +102,19 @@ func InsertHistoryItem(ctx context.Context, hitem *HistoryItemType) error { return nil } +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) + return nil + }) + if err != nil { + return nil, err + } + return rtn, nil +} + func GetBareSessions(ctx context.Context) ([]*SessionType, error) { var rtn []*SessionType err := WithTx(ctx, func(tx *TxWrap) error { diff --git a/pkg/sstore/sstore.go b/pkg/sstore/sstore.go index 265676c1..479decf8 100644 --- a/pkg/sstore/sstore.go +++ b/pkg/sstore/sstore.go @@ -164,6 +164,7 @@ type HistoryItemType struct { ScreenId string `json:"screenid"` WindowId string `json:"windowid"` LineId int64 `json:"lineid"` + HadError bool `json:"haderror"` CmdId string `json:"cmdid"` CmdStr string `json:"cmdstr"` diff --git a/pkg/sstore/updatebus.go b/pkg/sstore/updatebus.go index 62a1f4ea..c633f909 100644 --- a/pkg/sstore/updatebus.go +++ b/pkg/sstore/updatebus.go @@ -74,6 +74,17 @@ func (LineUpdate) UpdateType() string { return LineCmdUpdateStr } +func ReadLineCmdIdFromUpdate(update UpdatePacket) (int64, string) { + lineUpdate, ok := update.(LineUpdate) + if !ok { + return 0, "" + } + if lineUpdate.Line == nil { + return 0, "" + } + return lineUpdate.Line.LineId, lineUpdate.Line.CmdId +} + type InfoMsgType struct { InfoTitle string `json:"infotitle"` InfoError string `json:"infoerror,omitempty"`