mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-02 18:39:05 +01:00
* checkpoint some ideas on a new branch * checkpoint on new errors / errorcode passing * get CodedError piped all the way through to infomsg * implement a /reset:cwd command to deal with cases when the cwd is invalid. other assorted debugging, utility, and fixups * on invalid cwd, show message to run /reset:cwd
This commit is contained in:
parent
33fc3518c0
commit
4357bcf1c8
@ -61,3 +61,6 @@ export enum StatusIndicatorLevel {
|
||||
Success = 2,
|
||||
Error = 3,
|
||||
}
|
||||
|
||||
// matches packet.go
|
||||
export const ErrorCode_InvalidCwd = "ERRCWD";
|
||||
|
@ -407,7 +407,7 @@
|
||||
}
|
||||
|
||||
.info-msg {
|
||||
color: var(--cmdinput-history-title-color);
|
||||
color: var(--term-blue);
|
||||
padding-bottom: 2px;
|
||||
|
||||
a {
|
||||
|
@ -8,7 +8,7 @@ import cn from "classnames";
|
||||
import dayjs from "dayjs";
|
||||
import localizedFormat from "dayjs/plugin/localizedFormat";
|
||||
import { GlobalModel } from "@/models";
|
||||
import { makeExternLink } from "@/util/util";
|
||||
import * as appconst from "@/app/appconst";
|
||||
|
||||
dayjs.extend(localizedFormat);
|
||||
|
||||
@ -104,6 +104,9 @@ class InfoMsg extends React.Component<{}, {}> {
|
||||
<div key="infoerror" className="info-error">
|
||||
[error] {infoMsg.infoerror}
|
||||
</div>
|
||||
<If condition={infoMsg.infoerrorcode == appconst.ErrorCode_InvalidCwd}>
|
||||
<div className="info-error">to reset, run: /reset:cwd</div>
|
||||
</If>
|
||||
</If>
|
||||
</div>
|
||||
);
|
||||
|
@ -1444,7 +1444,11 @@ class Model {
|
||||
if (err?.message) {
|
||||
errMsg = err.message;
|
||||
}
|
||||
this.inputModel.flashInfoMsg({ infoerror: errMsg }, null);
|
||||
let info: InfoType = { infoerror: errMsg };
|
||||
if (err?.errorcode) {
|
||||
info.infoerrorcode = err.errorcode;
|
||||
}
|
||||
this.inputModel.flashInfoMsg(info, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
1
src/types/custom.d.ts
vendored
1
src/types/custom.d.ts
vendored
@ -419,6 +419,7 @@ declare global {
|
||||
infomsghtml?: boolean;
|
||||
websharelink?: boolean;
|
||||
infoerror?: string;
|
||||
infoerrorcode?: string;
|
||||
infolines?: string[];
|
||||
infocomps?: string[];
|
||||
infocompsmore?: boolean;
|
||||
|
@ -50,7 +50,11 @@ function fetchJsonData(resp: any, ctErr: boolean): Promise<any> {
|
||||
throw rtnErr;
|
||||
}
|
||||
if (rtnData?.error) {
|
||||
throw new Error(rtnData.error);
|
||||
let err = new Error(rtnData.error);
|
||||
if (rtnData.errorcode) {
|
||||
err["errorcode"] = rtnData.errorcode;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
return rtnData;
|
||||
});
|
||||
|
37
waveshell/pkg/base/errors.go
Normal file
37
waveshell/pkg/base/errors.go
Normal file
@ -0,0 +1,37 @@
|
||||
package base
|
||||
|
||||
import "fmt"
|
||||
|
||||
type CodedError struct {
|
||||
ErrorCode string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (c *CodedError) Error() string {
|
||||
return fmt.Sprintf("%s %s", c.ErrorCode, c.Err.Error())
|
||||
}
|
||||
|
||||
func (c *CodedError) Unwrap() error {
|
||||
return c.Err
|
||||
}
|
||||
|
||||
func MakeCodedError(code string, err error) *CodedError {
|
||||
return &CodedError{
|
||||
ErrorCode: code,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
func CodedErrorf(code string, format string, args ...interface{}) *CodedError {
|
||||
return &CodedError{
|
||||
ErrorCode: code,
|
||||
Err: fmt.Errorf(format, args...),
|
||||
}
|
||||
}
|
||||
|
||||
func GetErrorCode(err error) string {
|
||||
if codedErr, ok := err.(*CodedError); ok {
|
||||
return codedErr.ErrorCode
|
||||
}
|
||||
return ""
|
||||
}
|
@ -72,6 +72,10 @@ const (
|
||||
ShellType_zsh = "zsh"
|
||||
)
|
||||
|
||||
const (
|
||||
EC_InvalidCwd = "ERRCWD"
|
||||
)
|
||||
|
||||
const PacketSenderQueueSize = 20
|
||||
|
||||
const PacketEOFStr = "EOF"
|
||||
@ -491,11 +495,12 @@ func MakeCompGenPacket() *CompGenPacketType {
|
||||
}
|
||||
|
||||
type ResponsePacketType struct {
|
||||
Type string `json:"type"`
|
||||
RespId string `json:"respid"`
|
||||
Success bool `json:"success"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
Type string `json:"type"`
|
||||
RespId string `json:"respid"`
|
||||
Success bool `json:"success"`
|
||||
Error string `json:"error,omitempty"`
|
||||
ErrorCode string `json:"errorcode,omitempty"` // can be used for structured errors
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
func (*ResponsePacketType) GetType() string {
|
||||
@ -516,6 +521,9 @@ func (p *ResponsePacketType) Err() error {
|
||||
}
|
||||
if !p.Success {
|
||||
if p.Error != "" {
|
||||
if p.ErrorCode != "" {
|
||||
return &base.CodedError{ErrorCode: p.ErrorCode, Err: errors.New(p.Error)}
|
||||
}
|
||||
return errors.New(p.Error)
|
||||
}
|
||||
return fmt.Errorf("rpc failed")
|
||||
@ -531,6 +539,9 @@ func (p *ResponsePacketType) String() string {
|
||||
}
|
||||
|
||||
func MakeErrorResponsePacket(reqId string, err error) *ResponsePacketType {
|
||||
if codedErr, ok := err.(*base.CodedError); ok {
|
||||
return &ResponsePacketType{Type: ResponsePacketStr, RespId: reqId, Error: codedErr.Err.Error(), ErrorCode: codedErr.ErrorCode}
|
||||
}
|
||||
return &ResponsePacketType{Type: ResponsePacketStr, RespId: reqId, Error: err.Error()}
|
||||
}
|
||||
|
||||
|
@ -231,10 +231,25 @@ func (sdiff *ShellStateDiff) GetHashVal(force bool) string {
|
||||
return sdiff.HashVal
|
||||
}
|
||||
|
||||
func (state ShellState) Dump() {
|
||||
fmt.Printf("ShellState:\n")
|
||||
fmt.Printf(" version: %s\n", state.Version)
|
||||
fmt.Printf(" shelltype: %s\n", state.GetShellType())
|
||||
fmt.Printf(" hashval: %s\n", state.GetHashVal(false))
|
||||
fmt.Printf(" cwd: %s\n", state.Cwd)
|
||||
fmt.Printf(" vars: %d, aliases: %d, funcs: %d\n", len(state.ShellVars), len(state.Aliases), len(state.Funcs))
|
||||
if state.Error != "" {
|
||||
fmt.Printf(" error: %s\n", state.Error)
|
||||
}
|
||||
}
|
||||
|
||||
func (sdiff ShellStateDiff) Dump(vars bool, aliases bool, funcs bool) {
|
||||
fmt.Printf("ShellStateDiff:\n")
|
||||
fmt.Printf(" version: %s\n", sdiff.Version)
|
||||
fmt.Printf(" base: %s\n", sdiff.BaseHash)
|
||||
fmt.Printf(" diffhash: %s\n", sdiff.GetHashVal(false))
|
||||
fmt.Printf(" diffhasharr: %v\n", sdiff.DiffHashArr)
|
||||
fmt.Printf(" cwd: %s\n", sdiff.Cwd)
|
||||
fmt.Printf(" vars: %d, aliases: %d, funcs: %d\n", len(sdiff.VarsDiff), len(sdiff.AliasesDiff), len(sdiff.FuncsDiff))
|
||||
if sdiff.Error != "" {
|
||||
fmt.Printf(" error: %s\n", sdiff.Error)
|
||||
|
@ -79,8 +79,22 @@ func (b bashShellApi) MakeShExecCommand(cmdStr string, rcFileName string, usePty
|
||||
return MakeBashShExecCommand(cmdStr, rcFileName, usePty)
|
||||
}
|
||||
|
||||
func (b bashShellApi) GetShellState() (*packet.ShellState, error) {
|
||||
return GetBashShellState()
|
||||
func (b bashShellApi) GetShellState() chan ShellStateOutput {
|
||||
ch := make(chan ShellStateOutput, 1)
|
||||
defer close(ch)
|
||||
ssPk, err := GetBashShellState()
|
||||
if err != nil {
|
||||
ch <- ShellStateOutput{
|
||||
Status: ShellStateOutputStatus_Done,
|
||||
Error: err.Error(),
|
||||
}
|
||||
return ch
|
||||
}
|
||||
ch <- ShellStateOutput{
|
||||
Status: ShellStateOutputStatus_Done,
|
||||
ShellState: ssPk,
|
||||
}
|
||||
return ch
|
||||
}
|
||||
|
||||
func (b bashShellApi) GetBaseShellOpts() string {
|
||||
|
@ -48,6 +48,17 @@ type RunCommandOpts struct {
|
||||
CommandStdinFdNum int // needed for SudoWithPass
|
||||
}
|
||||
|
||||
const (
|
||||
ShellStateOutputStatus_Done = "done"
|
||||
)
|
||||
|
||||
type ShellStateOutput struct {
|
||||
Status string
|
||||
StderrOutput []byte
|
||||
ShellState *packet.ShellState
|
||||
Error string
|
||||
}
|
||||
|
||||
type ShellApi interface {
|
||||
GetShellType() string
|
||||
MakeExitTrap(fdNum int) string
|
||||
@ -56,7 +67,7 @@ type ShellApi interface {
|
||||
GetRemoteShellPath() string
|
||||
MakeRunCommand(cmdStr string, opts RunCommandOpts) string
|
||||
MakeShExecCommand(cmdStr string, rcFileName string, usePty bool) *exec.Cmd
|
||||
GetShellState() (*packet.ShellState, error)
|
||||
GetShellState() chan ShellStateOutput
|
||||
GetBaseShellOpts() string
|
||||
ParseShellStateOutput(output []byte) (*packet.ShellState, error)
|
||||
MakeRcFileStr(pk *packet.RunPacketType) string
|
||||
@ -64,6 +75,9 @@ type ShellApi interface {
|
||||
ApplyShellStateDiff(oldState *packet.ShellState, diff *packet.ShellStateDiff) (*packet.ShellState, error)
|
||||
}
|
||||
|
||||
var _ ShellApi = &bashShellApi{}
|
||||
var _ ShellApi = &zshShellApi{}
|
||||
|
||||
func DetectLocalShellType() string {
|
||||
shellPath := GetMacUserShell()
|
||||
if shellPath == "" {
|
||||
|
@ -203,20 +203,25 @@ func (z zshShellApi) MakeShExecCommand(cmdStr string, rcFileName string, usePty
|
||||
return exec.Command(GetLocalZshPath(), "-l", "-i", "-c", cmdStr)
|
||||
}
|
||||
|
||||
func (z zshShellApi) GetShellState() (*packet.ShellState, error) {
|
||||
func (z zshShellApi) GetShellState() chan ShellStateOutput {
|
||||
ctx, cancelFn := context.WithTimeout(context.Background(), GetStateTimeout)
|
||||
defer cancelFn()
|
||||
rtnCh := make(chan ShellStateOutput, 1)
|
||||
defer close(rtnCh)
|
||||
cmdStr := BaseZshOpts + "; " + GetZshShellStateCmd(StateOutputFdNum)
|
||||
ecmd := exec.CommandContext(ctx, GetLocalZshPath(), "-l", "-i", "-c", cmdStr)
|
||||
_, outputBytes, err := RunCommandWithExtraFd(ecmd, StateOutputFdNum)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
rtnCh <- ShellStateOutput{Status: ShellStateOutputStatus_Done, Error: err.Error()}
|
||||
return rtnCh
|
||||
}
|
||||
rtn, err := z.ParseShellStateOutput(outputBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
rtnCh <- ShellStateOutput{Status: ShellStateOutputStatus_Done, Error: err.Error()}
|
||||
return rtnCh
|
||||
}
|
||||
return rtn, nil
|
||||
rtnCh <- ShellStateOutput{Status: ShellStateOutputStatus_Done, ShellState: rtn}
|
||||
return rtnCh
|
||||
}
|
||||
|
||||
func (z zshShellApi) GetBaseShellOpts() string {
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
@ -368,14 +369,18 @@ func ValidateRunPacket(pk *packet.RunPacketType) error {
|
||||
return fmt.Errorf("cannot detach command, constant rundata input too large len=%d, max=%d", totalRunData, mpio.MaxTotalRunDataSize)
|
||||
}
|
||||
}
|
||||
if pk.State != nil && pk.State.Cwd != "" {
|
||||
realCwd := base.ExpandHomeDir(pk.State.Cwd)
|
||||
if pk.State != nil {
|
||||
pkCwd := pk.State.Cwd
|
||||
if pkCwd == "" {
|
||||
pkCwd = "~"
|
||||
}
|
||||
realCwd := base.ExpandHomeDir(pkCwd)
|
||||
dirInfo, err := os.Stat(realCwd)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid cwd '%s' for command: %v", realCwd, err)
|
||||
return base.CodedErrorf(packet.EC_InvalidCwd, "invalid cwd '%s' for command: %v", realCwd, err)
|
||||
}
|
||||
if !dirInfo.IsDir() {
|
||||
return fmt.Errorf("invalid cwd '%s' for command, not a directory", realCwd)
|
||||
return base.CodedErrorf(packet.EC_InvalidCwd, "invalid cwd '%s' for command, not a directory", realCwd)
|
||||
}
|
||||
}
|
||||
for _, runData := range pk.RunData {
|
||||
@ -896,7 +901,9 @@ func RunCommandSimple(pk *packet.RunPacketType, sender *packet.PacketSender, fro
|
||||
if sapi.GetShellType() == packet.ShellType_zsh {
|
||||
shellutil.UpdateCmdEnv(cmd.Cmd, map[string]string{"ZDOTDIR": zdotdir})
|
||||
}
|
||||
if state.Cwd != "" {
|
||||
if state.Cwd == "" {
|
||||
cmd.Cmd.Dir = base.ExpandHomeDir("~")
|
||||
} else if state.Cwd != "" {
|
||||
cmd.Cmd.Dir = base.ExpandHomeDir(state.Cwd)
|
||||
}
|
||||
err = ValidateRemoteFds(pk.Fds)
|
||||
@ -1237,12 +1244,13 @@ func MakeShellStatePacket(shellType string) (*packet.ShellStatePacketType, error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
shellState, err := sapi.GetShellState()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
rtnCh := sapi.GetShellState()
|
||||
ssOutput := <-rtnCh
|
||||
if ssOutput.Error != "" {
|
||||
return nil, errors.New(ssOutput.Error)
|
||||
}
|
||||
rtn := packet.MakeShellStatePacket()
|
||||
rtn.State = shellState
|
||||
rtn.State = ssOutput.ShellState
|
||||
return rtn, nil
|
||||
}
|
||||
|
||||
|
@ -623,6 +623,10 @@ func WriteJsonError(w http.ResponseWriter, errVal error) {
|
||||
w.WriteHeader(200)
|
||||
errMap := make(map[string]interface{})
|
||||
errMap["error"] = errVal.Error()
|
||||
errorCode := base.GetErrorCode(errVal)
|
||||
if errorCode != "" {
|
||||
errMap["errorcode"] = errorCode
|
||||
}
|
||||
barr, _ := json.Marshal(errMap)
|
||||
w.Write(barr)
|
||||
}
|
||||
|
@ -167,6 +167,7 @@ func init() {
|
||||
registerCmdFn("_compgen", CompGenCommand)
|
||||
registerCmdFn("clear", ClearCommand)
|
||||
registerCmdFn("reset", RemoteResetCommand)
|
||||
registerCmdFn("reset:cwd", ResetCwdCommand)
|
||||
registerCmdFn("signal", SignalCommand)
|
||||
registerCmdFn("sync", SyncCommand)
|
||||
|
||||
@ -190,6 +191,7 @@ func init() {
|
||||
registerCmdFn("screen:reset", ScreenResetCommand)
|
||||
registerCmdFn("screen:webshare", ScreenWebShareCommand)
|
||||
registerCmdFn("screen:reorder", ScreenReorderCommand)
|
||||
registerCmdFn("screen:show", ScreenShowCommand)
|
||||
|
||||
registerCmdAlias("remote", RemoteCommand)
|
||||
registerCmdFn("remote:show", RemoteShowCommand)
|
||||
@ -707,7 +709,7 @@ func EvalCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (scbus.U
|
||||
if rtnErr == nil {
|
||||
update, rtnErr = HandleCommand(ctxWithHistory, newPk)
|
||||
} else {
|
||||
return nil, fmt.Errorf("error in Eval Meta Command: %v", rtnErr)
|
||||
return nil, fmt.Errorf("error in Eval Meta Command: %w", rtnErr)
|
||||
}
|
||||
if !resolveBool(pk.Kwargs["nohist"], false) {
|
||||
// TODO should this be "pk" or "newPk" (2nd arg)
|
||||
@ -3419,6 +3421,40 @@ func SessionArchiveCommand(ctx context.Context, pk *scpacket.FeCommandPacketType
|
||||
}
|
||||
}
|
||||
|
||||
func ScreenShowCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (scbus.UpdatePacket, error) {
|
||||
ids, err := resolveUiIds(ctx, pk, R_Session|R_Screen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
screen, err := sstore.GetScreenById(ctx, ids.ScreenId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot get screen: %v", err)
|
||||
}
|
||||
if screen == nil {
|
||||
return nil, fmt.Errorf("screen not found")
|
||||
}
|
||||
statePtr, err := remote.ResolveCurrentScreenStatePtr(ctx, ids.SessionId, ids.ScreenId, ids.Remote.RemotePtr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot resolve current screen stateptr: %v", err)
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(fmt.Sprintf(" %-15s %s\n", "screenid", screen.ScreenId))
|
||||
buf.WriteString(fmt.Sprintf(" %-15s %s\n", "name", screen.Name))
|
||||
buf.WriteString(fmt.Sprintf(" %-15s %d\n", "screenidx", screen.ScreenIdx))
|
||||
buf.WriteString(fmt.Sprintf(" %-15s %s\n", "tabcolor", screen.ScreenOpts.TabColor))
|
||||
buf.WriteString(fmt.Sprintf(" %-15s %s\n", "tabicon", screen.ScreenOpts.TabIcon))
|
||||
buf.WriteString(fmt.Sprintf(" %-15s %d\n", "selectedline", screen.SelectedLine))
|
||||
buf.WriteString(fmt.Sprintf(" %-15s %s\n", "curremote", GetFullRemoteDisplayName(&screen.CurRemote, &ids.Remote.RState)))
|
||||
buf.WriteString(fmt.Sprintf(" %-15s %s\n", "stateptr-base", statePtr.BaseHash))
|
||||
buf.WriteString(fmt.Sprintf(" %-15s %v\n", "stateptr-diff", statePtr.DiffHashArr))
|
||||
update := scbus.MakeUpdatePacket()
|
||||
update.AddUpdate(sstore.InfoMsgType{
|
||||
InfoTitle: "screen info",
|
||||
InfoLines: splitLinesForInfo(buf.String()),
|
||||
})
|
||||
return update, nil
|
||||
}
|
||||
|
||||
func SessionShowCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (scbus.UpdatePacket, error) {
|
||||
ids, err := resolveUiIds(ctx, pk, R_Session)
|
||||
if err != nil {
|
||||
@ -3600,6 +3636,33 @@ func RemoteResetCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (
|
||||
return update, nil
|
||||
}
|
||||
|
||||
func ResetCwdCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (scbus.UpdatePacket, error) {
|
||||
ids, err := resolveUiIds(ctx, pk, R_Session|R_Screen|R_Remote)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
statePtr, err := remote.ResolveCurrentScreenStatePtr(ctx, ids.SessionId, ids.ScreenId, ids.Remote.RemotePtr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stateDiff, err := sstore.GetCurStateDiffFromPtr(ctx, statePtr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
feState := ids.Remote.FeState
|
||||
feState["cwd"] = "~"
|
||||
stateDiff.Cwd = "~"
|
||||
stateDiff.GetHashVal(true)
|
||||
remoteInst, err := sstore.UpdateRemoteState(ctx, ids.SessionId, ids.ScreenId, ids.Remote.RemotePtr, feState, nil, stateDiff)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not update remote state: %v", err)
|
||||
}
|
||||
update := scbus.MakeUpdatePacket()
|
||||
update.AddUpdate(sstore.MakeSessionUpdateForRemote(ids.SessionId, remoteInst), sstore.InteractiveUpdate(pk.Interactive))
|
||||
update.AddUpdate(sstore.InfoMsgType{InfoMsg: "reset cwd to ~"})
|
||||
return update, nil
|
||||
}
|
||||
|
||||
func ClearCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (scbus.UpdatePacket, error) {
|
||||
ids, err := resolveUiIds(ctx, pk, R_Session|R_Screen)
|
||||
if err != nil {
|
||||
@ -3958,13 +4021,13 @@ func LineRestartCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (
|
||||
NoCreateCmdPtyFile: true,
|
||||
}
|
||||
cmd, callback, err := remote.RunCommand(ctx, rcOpts, runPacket)
|
||||
sstore.IncrementNumRunningCmds(cmd.ScreenId, 1)
|
||||
if callback != nil {
|
||||
defer callback()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sstore.IncrementNumRunningCmds(cmd.ScreenId, 1)
|
||||
newTs := time.Now().UnixMilli()
|
||||
err = sstore.UpdateCmdForRestart(ctx, runPacket.CK, newTs, cmd.CmdPid, cmd.RemotePid, convertTermOpts(runPacket.TermOpts))
|
||||
if err != nil {
|
||||
|
@ -1919,6 +1919,25 @@ func (msh *MShellProc) removePendingStateCmd(screenId string, rptr sstore.Remote
|
||||
}
|
||||
}
|
||||
|
||||
func ResolveCurrentScreenStatePtr(ctx context.Context, sessionId string, screenId string, remotePtr sstore.RemotePtrType) (*sstore.ShellStatePtr, error) {
|
||||
statePtr, err := sstore.GetRemoteStatePtr(ctx, sessionId, screenId, remotePtr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot get current connection stateptr: %w", err)
|
||||
}
|
||||
if statePtr == nil {
|
||||
msh := GetRemoteById(remotePtr.RemoteId)
|
||||
err := msh.EnsureShellType(ctx, msh.GetShellPref()) // make sure shellType is initialized
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
statePtr = msh.GetDefaultStatePtr(msh.GetShellPref())
|
||||
if statePtr == nil {
|
||||
return nil, fmt.Errorf("no valid default connection stateptr")
|
||||
}
|
||||
}
|
||||
return statePtr, nil
|
||||
}
|
||||
|
||||
type RunCommandOpts struct {
|
||||
SessionId string
|
||||
ScreenId string
|
||||
@ -1993,19 +2012,9 @@ func RunCommand(ctx context.Context, rcOpts RunCommandOpts, runPacket *packet.Ru
|
||||
statePtr = rcOpts.StatePtr
|
||||
} else {
|
||||
var err error
|
||||
statePtr, err = sstore.GetRemoteStatePtr(ctx, sessionId, screenId, remotePtr)
|
||||
statePtr, err = ResolveCurrentScreenStatePtr(ctx, sessionId, screenId, remotePtr)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("cannot get current connection stateptr: %w", err)
|
||||
}
|
||||
}
|
||||
if statePtr == nil { // can be null if there is no remote-instance (screen has unchanged state from default)
|
||||
err := msh.EnsureShellType(ctx, msh.GetShellPref()) // make sure shellType is initialized
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
statePtr = msh.GetDefaultStatePtr(msh.GetShellPref())
|
||||
if statePtr == nil {
|
||||
return nil, nil, fmt.Errorf("cannot run command, no valid connection stateptr")
|
||||
return nil, nil, fmt.Errorf("cannot run command: %w", err)
|
||||
}
|
||||
}
|
||||
currentState, err := sstore.GetFullState(ctx, *statePtr)
|
||||
@ -2049,7 +2058,7 @@ func RunCommand(ctx context.Context, rcOpts RunCommandOpts, runPacket *packet.Ru
|
||||
return nil, nil, fmt.Errorf("invalid response received from server for run packet: %s", packet.AsString(rtnPk))
|
||||
}
|
||||
if respPk.Error != "" {
|
||||
return nil, nil, errors.New(respPk.Error)
|
||||
return nil, nil, respPk.Err()
|
||||
}
|
||||
return nil, nil, fmt.Errorf("invalid response received from server for run packet: %s", packet.AsString(rtnPk))
|
||||
}
|
||||
|
@ -2023,6 +2023,71 @@ func StoreStateDiff(ctx context.Context, diff *packet.ShellStateDiff) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetStateBaseVersion(ctx context.Context, baseHash string) (string, error) {
|
||||
return WithTxRtn(ctx, func(tx *TxWrap) (string, error) {
|
||||
query := `SELECT version FROM state_base WHERE basehash = ?`
|
||||
rtn := tx.GetString(query, baseHash)
|
||||
return rtn, nil
|
||||
})
|
||||
}
|
||||
|
||||
func GetCurStateDiffFromPtr(ctx context.Context, ssPtr *ShellStatePtr) (*packet.ShellStateDiff, error) {
|
||||
if ssPtr == nil {
|
||||
return nil, fmt.Errorf("cannot resolve state, empty stateptr")
|
||||
}
|
||||
if len(ssPtr.DiffHashArr) == 0 {
|
||||
baseVersion, err := GetStateBaseVersion(ctx, ssPtr.BaseHash)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot get base version: %v", err)
|
||||
}
|
||||
// return an empty diff
|
||||
return &packet.ShellStateDiff{Version: baseVersion, BaseHash: ssPtr.BaseHash}, nil
|
||||
}
|
||||
lastDiffHash := ssPtr.DiffHashArr[len(ssPtr.DiffHashArr)-1]
|
||||
return GetStateDiff(ctx, lastDiffHash)
|
||||
}
|
||||
|
||||
func GetStateBase(ctx context.Context, baseHash string) (*packet.ShellState, error) {
|
||||
stateBase, txErr := WithTxRtn(ctx, func(tx *TxWrap) (*StateBase, error) {
|
||||
var stateBase StateBase
|
||||
query := `SELECT * FROM state_base WHERE basehash = ?`
|
||||
found := tx.Get(&stateBase, query, baseHash)
|
||||
if !found {
|
||||
return nil, fmt.Errorf("StateBase %s not found", baseHash)
|
||||
}
|
||||
return &stateBase, nil
|
||||
})
|
||||
if txErr != nil {
|
||||
return nil, txErr
|
||||
}
|
||||
state := &packet.ShellState{}
|
||||
err := state.DecodeShellState(stateBase.Data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return state, nil
|
||||
}
|
||||
|
||||
func GetStateDiff(ctx context.Context, diffHash string) (*packet.ShellStateDiff, error) {
|
||||
stateDiff, txErr := WithTxRtn(ctx, func(tx *TxWrap) (*StateDiff, error) {
|
||||
query := `SELECT * FROM state_diff WHERE diffhash = ?`
|
||||
stateDiff := dbutil.GetMapGen[*StateDiff](tx, query, diffHash)
|
||||
if stateDiff == nil {
|
||||
return nil, fmt.Errorf("StateDiff %s not found", diffHash)
|
||||
}
|
||||
return stateDiff, nil
|
||||
})
|
||||
if txErr != nil {
|
||||
return nil, txErr
|
||||
}
|
||||
state := &packet.ShellStateDiff{}
|
||||
err := state.DecodeShellStateDiff(stateDiff.Data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return state, nil
|
||||
}
|
||||
|
||||
// returns error when not found
|
||||
func GetFullState(ctx context.Context, ssPtr ShellStatePtr) (*packet.ShellState, error) {
|
||||
var state *packet.ShellState
|
||||
|
@ -48,6 +48,7 @@ func (CmdLineUpdate) GetType() string {
|
||||
type InfoMsgType struct {
|
||||
InfoTitle string `json:"infotitle"`
|
||||
InfoError string `json:"infoerror,omitempty"`
|
||||
InfoErrorCode string `json:"infoerrorcode,omitempty"`
|
||||
InfoMsg string `json:"infomsg,omitempty"`
|
||||
InfoMsgHtml bool `json:"infomsghtml,omitempty"`
|
||||
WebShareLink bool `json:"websharelink,omitempty"`
|
||||
|
Loading…
Reference in New Issue
Block a user