mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-09 19:48:45 +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,
|
HistoryViewDataType,
|
||||||
AlertMessageType,
|
AlertMessageType,
|
||||||
HistorySearchParams,
|
HistorySearchParams,
|
||||||
|
UserInputRequest,
|
||||||
|
UserInputResponse,
|
||||||
|
UserInputResponsePacket,
|
||||||
FocusTypeStrs,
|
FocusTypeStrs,
|
||||||
ScreenLinesType,
|
ScreenLinesType,
|
||||||
HistoryTypeStrs,
|
HistoryTypeStrs,
|
||||||
@ -4110,6 +4113,19 @@ class Model {
|
|||||||
update.screennumrunningcommands.num
|
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 {
|
updateRemotes(remotes: RemoteType[]): void {
|
||||||
|
@ -328,6 +328,7 @@ type ModelUpdateType = {
|
|||||||
alertmessage?: AlertMessageType;
|
alertmessage?: AlertMessageType;
|
||||||
screenstatusindicator?: ScreenStatusIndicatorUpdateType;
|
screenstatusindicator?: ScreenStatusIndicatorUpdateType;
|
||||||
screennumrunningcommands?: ScreenNumRunningCommandsUpdateType;
|
screennumrunningcommands?: ScreenNumRunningCommandsUpdateType;
|
||||||
|
userinputrequest?: UserInputRequest;
|
||||||
};
|
};
|
||||||
|
|
||||||
type HistoryViewDataType = {
|
type HistoryViewDataType = {
|
||||||
@ -586,6 +587,24 @@ type HistorySearchParams = {
|
|||||||
filterCmds?: boolean;
|
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 RenderModeType = "normal" | "collapsed" | "expanded";
|
||||||
|
|
||||||
type WebScreen = {
|
type WebScreen = {
|
||||||
@ -771,6 +790,9 @@ export type {
|
|||||||
RenderModeType,
|
RenderModeType,
|
||||||
AlertMessageType,
|
AlertMessageType,
|
||||||
HistorySearchParams,
|
HistorySearchParams,
|
||||||
|
UserInputRequest,
|
||||||
|
UserInputResponse,
|
||||||
|
UserInputResponsePacket,
|
||||||
ScreenLinesType,
|
ScreenLinesType,
|
||||||
FocusTypeStrs,
|
FocusTypeStrs,
|
||||||
HistoryTypeStrs,
|
HistoryTypeStrs,
|
||||||
|
@ -40,7 +40,7 @@ import (
|
|||||||
"golang.org/x/mod/semver"
|
"golang.org/x/mod/semver"
|
||||||
)
|
)
|
||||||
|
|
||||||
const UseSshLibrary = false
|
const UseSshLibrary = true
|
||||||
|
|
||||||
const RemoteTypeMShell = "mshell"
|
const RemoteTypeMShell = "mshell"
|
||||||
const DefaultTerm = "xterm-256color"
|
const DefaultTerm = "xterm-256color"
|
||||||
|
@ -6,6 +6,7 @@ package remote
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -60,6 +61,14 @@ func ConnectToClient(opts *sstore.SSHOpts) (*ssh.Client, error) {
|
|||||||
identityFile = configIdentity
|
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()
|
hostKeyCallback := ssh.InsecureIgnoreHostKey()
|
||||||
var authMethods []ssh.AuthMethod
|
var authMethods []ssh.AuthMethod
|
||||||
publicKeyAuth, err := createPublicKeyAuth(identityFile, opts.SSHPassword)
|
publicKeyAuth, err := createPublicKeyAuth(identityFile, opts.SSHPassword)
|
||||||
|
@ -20,6 +20,7 @@ const WatchScreenPacketStr = "watchscreen"
|
|||||||
const FeInputPacketStr = "feinput"
|
const FeInputPacketStr = "feinput"
|
||||||
const RemoteInputPacketStr = "remoteinput"
|
const RemoteInputPacketStr = "remoteinput"
|
||||||
const CmdInputTextPacketStr = "cmdinputtext"
|
const CmdInputTextPacketStr = "cmdinputtext"
|
||||||
|
const UserInputResponsePacketStr = "userinputresp"
|
||||||
|
|
||||||
type FeCommandPacketType struct {
|
type FeCommandPacketType struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
@ -92,12 +93,19 @@ type CmdInputTextPacketType struct {
|
|||||||
Text utilfn.StrWithPos `json:"text"`
|
Text utilfn.StrWithPos `json:"text"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UserInputResponsePacketType struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
RequestId string `json:"requestid"`
|
||||||
|
Response *sstore.UserInputResponseType `json:"response"`
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
packet.RegisterPacketType(FeCommandPacketStr, reflect.TypeOf(FeCommandPacketType{}))
|
packet.RegisterPacketType(FeCommandPacketStr, reflect.TypeOf(FeCommandPacketType{}))
|
||||||
packet.RegisterPacketType(WatchScreenPacketStr, reflect.TypeOf(WatchScreenPacketType{}))
|
packet.RegisterPacketType(WatchScreenPacketStr, reflect.TypeOf(WatchScreenPacketType{}))
|
||||||
packet.RegisterPacketType(FeInputPacketStr, reflect.TypeOf(FeInputPacketType{}))
|
packet.RegisterPacketType(FeInputPacketStr, reflect.TypeOf(FeInputPacketType{}))
|
||||||
packet.RegisterPacketType(RemoteInputPacketStr, reflect.TypeOf(RemoteInputPacketType{}))
|
packet.RegisterPacketType(RemoteInputPacketStr, reflect.TypeOf(RemoteInputPacketType{}))
|
||||||
packet.RegisterPacketType(CmdInputTextPacketStr, reflect.TypeOf(CmdInputTextPacketType{}))
|
packet.RegisterPacketType(CmdInputTextPacketStr, reflect.TypeOf(CmdInputTextPacketType{}))
|
||||||
|
packet.RegisterPacketType(UserInputResponsePacketStr, reflect.TypeOf(UserInputResponsePacketType{}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*CmdInputTextPacketType) GetType() string {
|
func (*CmdInputTextPacketType) GetType() string {
|
||||||
@ -139,3 +147,7 @@ func MakeRemoteInputPacket() *RemoteInputPacketType {
|
|||||||
func (*RemoteInputPacketType) GetType() string {
|
func (*RemoteInputPacketType) GetType() string {
|
||||||
return RemoteInputPacketStr
|
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)
|
sstore.ScreenMemSetCmdInputText(cmdInputPk.ScreenId, cmdInputPk.Text, cmdInputPk.SeqNum)
|
||||||
return nil
|
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())
|
return fmt.Errorf("got ws bad message: %v", pk.GetType())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,10 +4,13 @@
|
|||||||
package sstore
|
package sstore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/wavetermdev/waveterm/waveshell/pkg/packet"
|
"github.com/wavetermdev/waveterm/waveshell/pkg/packet"
|
||||||
"github.com/wavetermdev/waveterm/waveshell/pkg/utilfn"
|
"github.com/wavetermdev/waveterm/waveshell/pkg/utilfn"
|
||||||
)
|
)
|
||||||
@ -65,6 +68,7 @@ type ModelUpdate struct {
|
|||||||
AlertMessage *AlertMessageType `json:"alertmessage,omitempty"`
|
AlertMessage *AlertMessageType `json:"alertmessage,omitempty"`
|
||||||
ScreenStatusIndicator *ScreenStatusIndicatorType `json:"screenstatusindicator,omitempty"`
|
ScreenStatusIndicator *ScreenStatusIndicatorType `json:"screenstatusindicator,omitempty"`
|
||||||
ScreenNumRunningCommands *ScreenNumRunningCommandsType `json:"screennumrunningcommands,omitempty"`
|
ScreenNumRunningCommands *ScreenNumRunningCommandsType `json:"screennumrunningcommands,omitempty"`
|
||||||
|
UserInputRequest *UserInputRequestType `json:"userinputrequest,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*ModelUpdate) UpdateType() string {
|
func (*ModelUpdate) UpdateType() string {
|
||||||
@ -160,6 +164,18 @@ type HistoryInfoType struct {
|
|||||||
Show bool `json:"show"`
|
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 {
|
type UpdateChannel struct {
|
||||||
ScreenId string
|
ScreenId string
|
||||||
ClientId string
|
ClientId string
|
||||||
@ -176,12 +192,14 @@ func (uch UpdateChannel) Match(screenId string) bool {
|
|||||||
type UpdateBus struct {
|
type UpdateBus struct {
|
||||||
Lock *sync.Mutex
|
Lock *sync.Mutex
|
||||||
Channels map[string]UpdateChannel
|
Channels map[string]UpdateChannel
|
||||||
|
UserInputCh map[string](chan *UserInputResponseType)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MakeUpdateBus() *UpdateBus {
|
func MakeUpdateBus() *UpdateBus {
|
||||||
return &UpdateBus{
|
return &UpdateBus{
|
||||||
Lock: &sync.Mutex{},
|
Lock: &sync.Mutex{},
|
||||||
Channels: make(map[string]UpdateChannel),
|
Channels: make(map[string]UpdateChannel),
|
||||||
|
UserInputCh: make(map[string](chan *UserInputResponseType)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,3 +291,46 @@ type ScreenNumRunningCommandsType struct {
|
|||||||
ScreenId string `json:"screenid"`
|
ScreenId string `json:"screenid"`
|
||||||
Num int `json:"num"`
|
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