mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-08 19:38:51 +01:00
feat: create backend for user input requests
This is the first part of a change that allows the backend to request user input from the frontend. Essentially, the backend will send a request for the user to answer some query, and the frontend will send that answer back. It is blocking, so it needs to be used within a goroutine. There is some placeholder code in the frontend that will be updated in future commits. Similarly, there is some debug code in the backend remote.go file.
This commit is contained in:
parent
1a51d93a54
commit
98f6b84d3e
@ -50,6 +50,9 @@ import {
|
||||
HistoryViewDataType,
|
||||
AlertMessageType,
|
||||
HistorySearchParams,
|
||||
UserInputRequest,
|
||||
UserInputResponse,
|
||||
UserInputResponsePacket,
|
||||
FocusTypeStrs,
|
||||
ScreenLinesType,
|
||||
HistoryTypeStrs,
|
||||
@ -4110,6 +4113,19 @@ class Model {
|
||||
update.screennumrunningcommands.num
|
||||
);
|
||||
}
|
||||
if ("userinputrequest" in update) {
|
||||
let userInputRequest: UserInputRequest = update.userinputrequest;
|
||||
let userInputResponse: UserInputResponse = {
|
||||
type: "text",
|
||||
text: "what wonderful weather we're having",
|
||||
};
|
||||
let userInputResponsePacket: UserInputResponsePacket = {
|
||||
type: "userinputresp",
|
||||
requestid: userInputRequest.requestid,
|
||||
response: userInputResponse,
|
||||
};
|
||||
this.ws.pushMessage(userInputResponsePacket);
|
||||
}
|
||||
}
|
||||
|
||||
updateRemotes(remotes: RemoteType[]): void {
|
||||
|
@ -328,6 +328,7 @@ type ModelUpdateType = {
|
||||
alertmessage?: AlertMessageType;
|
||||
screenstatusindicator?: ScreenStatusIndicatorUpdateType;
|
||||
screennumrunningcommands?: ScreenNumRunningCommandsUpdateType;
|
||||
userinputrequest?: UserInputRequest;
|
||||
};
|
||||
|
||||
type HistoryViewDataType = {
|
||||
@ -586,6 +587,24 @@ type HistorySearchParams = {
|
||||
filterCmds?: boolean;
|
||||
};
|
||||
|
||||
type UserInputRequest = {
|
||||
requestid: string;
|
||||
querytext: string;
|
||||
responsetype: string;
|
||||
};
|
||||
|
||||
type UserInputResponse = {
|
||||
type: string;
|
||||
text?: string;
|
||||
confirm?: boolean;
|
||||
};
|
||||
|
||||
type UserInputResponsePacket = {
|
||||
type: string;
|
||||
requestid: string;
|
||||
response: UserInputResponse;
|
||||
};
|
||||
|
||||
type RenderModeType = "normal" | "collapsed" | "expanded";
|
||||
|
||||
type WebScreen = {
|
||||
@ -771,6 +790,9 @@ export type {
|
||||
RenderModeType,
|
||||
AlertMessageType,
|
||||
HistorySearchParams,
|
||||
UserInputRequest,
|
||||
UserInputResponse,
|
||||
UserInputResponsePacket,
|
||||
ScreenLinesType,
|
||||
FocusTypeStrs,
|
||||
HistoryTypeStrs,
|
||||
|
@ -40,7 +40,7 @@ import (
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
const UseSshLibrary = false
|
||||
const UseSshLibrary = true
|
||||
|
||||
const RemoteTypeMShell = "mshell"
|
||||
const DefaultTerm = "xterm-256color"
|
||||
|
@ -6,6 +6,7 @@ package remote
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/user"
|
||||
"strconv"
|
||||
@ -60,6 +61,14 @@ func ConnectToClient(opts *sstore.SSHOpts) (*ssh.Client, error) {
|
||||
identityFile = configIdentity
|
||||
}
|
||||
|
||||
// test code
|
||||
request := &sstore.UserInputRequestType{
|
||||
ResponseType: "text",
|
||||
QueryText: "unused",
|
||||
}
|
||||
response, _ := sstore.MainBus.GetUserInput(request)
|
||||
log.Printf("response: %s\n", response.Text)
|
||||
|
||||
hostKeyCallback := ssh.InsecureIgnoreHostKey()
|
||||
var authMethods []ssh.AuthMethod
|
||||
publicKeyAuth, err := createPublicKeyAuth(identityFile, opts.SSHPassword)
|
||||
|
@ -20,6 +20,7 @@ const WatchScreenPacketStr = "watchscreen"
|
||||
const FeInputPacketStr = "feinput"
|
||||
const RemoteInputPacketStr = "remoteinput"
|
||||
const CmdInputTextPacketStr = "cmdinputtext"
|
||||
const UserInputResponsePacketStr = "userinputresp"
|
||||
|
||||
type FeCommandPacketType struct {
|
||||
Type string `json:"type"`
|
||||
@ -92,12 +93,19 @@ type CmdInputTextPacketType struct {
|
||||
Text utilfn.StrWithPos `json:"text"`
|
||||
}
|
||||
|
||||
type UserInputResponsePacketType struct {
|
||||
Type string `json:"type"`
|
||||
RequestId string `json:"requestid"`
|
||||
Response *sstore.UserInputResponseType `json:"response"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
packet.RegisterPacketType(FeCommandPacketStr, reflect.TypeOf(FeCommandPacketType{}))
|
||||
packet.RegisterPacketType(WatchScreenPacketStr, reflect.TypeOf(WatchScreenPacketType{}))
|
||||
packet.RegisterPacketType(FeInputPacketStr, reflect.TypeOf(FeInputPacketType{}))
|
||||
packet.RegisterPacketType(RemoteInputPacketStr, reflect.TypeOf(RemoteInputPacketType{}))
|
||||
packet.RegisterPacketType(CmdInputTextPacketStr, reflect.TypeOf(CmdInputTextPacketType{}))
|
||||
packet.RegisterPacketType(UserInputResponsePacketStr, reflect.TypeOf(UserInputResponsePacketType{}))
|
||||
}
|
||||
|
||||
func (*CmdInputTextPacketType) GetType() string {
|
||||
@ -139,3 +147,7 @@ func MakeRemoteInputPacket() *RemoteInputPacketType {
|
||||
func (*RemoteInputPacketType) GetType() string {
|
||||
return RemoteInputPacketStr
|
||||
}
|
||||
|
||||
func (*UserInputResponsePacketType) GetType() string {
|
||||
return UserInputResponsePacketStr
|
||||
}
|
||||
|
@ -278,6 +278,15 @@ func (ws *WSState) processMessage(msgBytes []byte) error {
|
||||
sstore.ScreenMemSetCmdInputText(cmdInputPk.ScreenId, cmdInputPk.Text, cmdInputPk.SeqNum)
|
||||
return nil
|
||||
}
|
||||
if pk.GetType() == scpacket.UserInputResponsePacketStr {
|
||||
userInputRespPk := pk.(*scpacket.UserInputResponsePacketType)
|
||||
uich, ok := sstore.MainBus.UserInputCh[userInputRespPk.RequestId]
|
||||
if !ok {
|
||||
return fmt.Errorf("received User Input Response with invalid Id (%s): %v\n", userInputRespPk.RequestId, err)
|
||||
}
|
||||
uich <- userInputRespPk.Response
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("got ws bad message: %v", pk.GetType())
|
||||
}
|
||||
|
||||
|
@ -4,10 +4,13 @@
|
||||
package sstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/wavetermdev/waveterm/waveshell/pkg/packet"
|
||||
"github.com/wavetermdev/waveterm/waveshell/pkg/utilfn"
|
||||
)
|
||||
@ -65,6 +68,7 @@ type ModelUpdate struct {
|
||||
AlertMessage *AlertMessageType `json:"alertmessage,omitempty"`
|
||||
ScreenStatusIndicator *ScreenStatusIndicatorType `json:"screenstatusindicator,omitempty"`
|
||||
ScreenNumRunningCommands *ScreenNumRunningCommandsType `json:"screennumrunningcommands,omitempty"`
|
||||
UserInputRequest *UserInputRequestType `json:"userinputrequest,omitempty"`
|
||||
}
|
||||
|
||||
func (*ModelUpdate) UpdateType() string {
|
||||
@ -160,6 +164,18 @@ type HistoryInfoType struct {
|
||||
Show bool `json:"show"`
|
||||
}
|
||||
|
||||
type UserInputRequestType struct {
|
||||
RequestId string `json:"requestid"`
|
||||
QueryText string `json:"querytext"`
|
||||
ResponseType string `json:"responsetype"`
|
||||
}
|
||||
|
||||
type UserInputResponseType struct {
|
||||
Type string `json:"type"`
|
||||
Text string `json:"text,omitempty"`
|
||||
Confirm bool `json:"confirm,omitempty"`
|
||||
}
|
||||
|
||||
type UpdateChannel struct {
|
||||
ScreenId string
|
||||
ClientId string
|
||||
@ -176,12 +192,14 @@ func (uch UpdateChannel) Match(screenId string) bool {
|
||||
type UpdateBus struct {
|
||||
Lock *sync.Mutex
|
||||
Channels map[string]UpdateChannel
|
||||
UserInputCh map[string](chan *UserInputResponseType)
|
||||
}
|
||||
|
||||
func MakeUpdateBus() *UpdateBus {
|
||||
return &UpdateBus{
|
||||
Lock: &sync.Mutex{},
|
||||
Channels: make(map[string]UpdateChannel),
|
||||
UserInputCh: make(map[string](chan *UserInputResponseType)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -273,3 +291,46 @@ type ScreenNumRunningCommandsType struct {
|
||||
ScreenId string `json:"screenid"`
|
||||
Num int `json:"num"`
|
||||
}
|
||||
|
||||
func (bus *UpdateBus) registerUserInputChannel() (string, chan *UserInputResponseType) {
|
||||
bus.Lock.Lock()
|
||||
defer bus.Lock.Unlock()
|
||||
|
||||
id := uuid.New().String()
|
||||
uich := make(chan *UserInputResponseType)
|
||||
|
||||
bus.UserInputCh[id] = uich
|
||||
return id, uich
|
||||
}
|
||||
|
||||
func (bus *UpdateBus) unregisterUserInputChannel(id string) {
|
||||
bus.Lock.Lock()
|
||||
defer bus.Lock.Unlock()
|
||||
|
||||
delete(bus.UserInputCh, id)
|
||||
}
|
||||
|
||||
func (bus *UpdateBus) GetUserInput(userInputRequest *UserInputRequestType) (*UserInputResponseType, error) {
|
||||
// create context for timeout
|
||||
ctx, cancelFn := context.WithTimeout(context.Background(), 1*time.Minute)
|
||||
defer cancelFn()
|
||||
|
||||
id, uich := bus.registerUserInputChannel()
|
||||
|
||||
userInputRequest.RequestId = id
|
||||
update := &ModelUpdate{UserInputRequest: userInputRequest}
|
||||
bus.SendUpdate(update)
|
||||
|
||||
var response *UserInputResponseType
|
||||
var err error
|
||||
// prepare to receive response
|
||||
select {
|
||||
case resp := <-uich:
|
||||
response = resp
|
||||
case <-ctx.Done():
|
||||
err = fmt.Errorf("timed out waiting for user input")
|
||||
}
|
||||
|
||||
bus.unregisterUserInputChannel(id)
|
||||
return response, err
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user