now calling promptcentral web updates

This commit is contained in:
sawka
2023-03-26 18:48:43 -07:00
parent aed5a4db1d
commit 150c8cfaee
5 changed files with 202 additions and 14 deletions
+4
View File
@@ -565,6 +565,10 @@ func main() {
go telemetryLoop()
go stdinReadWatch()
go runWebSocketServer()
go func() {
time.Sleep(10 * time.Second)
pcloud.StartUpdateWriter()
}()
go sstore.RunCmdScreenMigration()
gr := mux.NewRouter()
gr.HandleFunc("/api/ptyout", AuthKeyWrap(HandleGetPtyOut))
+136 -9
View File
@@ -12,6 +12,8 @@ import (
"os"
"strconv"
"strings"
"sync"
"time"
"github.com/scripthaus-dev/sh2-server/pkg/rtnstate"
"github.com/scripthaus-dev/sh2-server/pkg/scbase"
@@ -23,12 +25,18 @@ const PCloudEndpointVarName = "PCLOUD_ENDPOINT"
const APIVersion = 1
const MaxPtyUpdateSize = (128 * 1024) + 1
const MaxUpdatesPerReq = 10
const MaxUpdateWriterErrors = 3
const PCloudDefaultTimeout = 5 * time.Second
const TelemetryUrl = "/telemetry"
const NoTelemetryUrl = "/no-telemetry"
const CreateWebScreenUrl = "/auth/create-web-screen"
const WebShareUpdateUrl = "/auth/web-share-update"
var updateWriterCVar = sync.NewCond(&sync.Mutex{})
var updateWriterMoreData = false
var updateWriterRunning = false
type AuthInfo struct {
UserId string `json:"userid"`
ClientId string `json:"clientid"`
@@ -183,6 +191,7 @@ func makeWebShareUpdate(ctx context.Context, update *sstore.ScreenUpdateType) (*
rtn := &WebShareUpdateType{
ScreenId: update.ScreenId,
LineId: update.LineId,
UpdateId: update.UpdateId,
UpdateType: update.UpdateType,
}
switch update.UpdateType {
@@ -219,7 +228,7 @@ func makeWebShareUpdate(ctx context.Context, update *sstore.ScreenUpdateType) (*
return nil, fmt.Errorf("error converting line to web-line: %v", err)
}
if cmd != nil {
rtn.Cmd, err = webCmdFromCmd(cmd)
rtn.Cmd, err = webCmdFromCmd(update.LineId, cmd)
if err != nil {
return nil, fmt.Errorf("error converting cmd to web-cmd: %v", err)
}
@@ -249,6 +258,13 @@ func makeWebShareUpdate(ctx context.Context, update *sstore.ScreenUpdateType) (*
}
rtn.SVal = cmd.Status
case sstore.UpdateType_CmdTermOpts:
_, cmd, err := sstore.GetLineCmdByLineId(ctx, update.ScreenId, update.LineId)
if err != nil || cmd == nil {
return nil, fmt.Errorf("error getting cmd: %v", defaultError(err, "not found"))
}
rtn.TermOpts = &cmd.TermOpts
case sstore.UpdateType_CmdDoneInfo:
_, cmd, err := sstore.GetLineCmdByLineId(ctx, update.ScreenId, update.LineId)
if err != nil || cmd == nil {
@@ -288,24 +304,24 @@ func makeWebShareUpdate(ctx context.Context, update *sstore.ScreenUpdateType) (*
return rtn, nil
}
func finalizeWebScreenUpdate(ctx context.Context, screenUpdate *sstore.ScreenUpdateType, webUpdate *WebShareUpdateType) error {
switch screenUpdate.UpdateType {
func finalizeWebScreenUpdate(ctx context.Context, webUpdate *WebShareUpdateType) error {
switch webUpdate.UpdateType {
case sstore.UpdateType_PtyPos:
dataEof := len(webUpdate.PtyData.Data) < MaxPtyUpdateSize
newPos := webUpdate.PtyData.PtyPos + int64(len(webUpdate.PtyData.Data))
if dataEof {
err := sstore.RemoveScreenUpdate(ctx, screenUpdate.UpdateType)
err := sstore.RemoveScreenUpdate(ctx, webUpdate.UpdateId)
if err != nil {
return err
}
}
err := sstore.SetWebPtyPos(ctx, screenUpdate.ScreenId, screenUpdate.LineId, newPos)
err := sstore.SetWebPtyPos(ctx, webUpdate.ScreenId, webUpdate.LineId, newPos)
if err != nil {
return err
}
default:
err := sstore.RemoveScreenUpdate(ctx, screenUpdate.UpdateType)
err := sstore.RemoveScreenUpdate(ctx, webUpdate.UpdateId)
if err != nil {
// this is not great, this *should* never fail and is not easy to recover from
return err
@@ -314,18 +330,27 @@ func finalizeWebScreenUpdate(ctx context.Context, screenUpdate *sstore.ScreenUpd
return nil
}
func DoWebScreenUpdates(ctx context.Context, authInfo AuthInfo, updateArr []*sstore.ScreenUpdateType) error {
func DoWebScreenUpdates(authInfo AuthInfo, updateArr []*sstore.ScreenUpdateType) error {
var webUpdates []*WebShareUpdateType
for _, update := range updateArr {
webUpdate, err := makeWebShareUpdate(ctx, update)
webUpdate, err := makeWebShareUpdate(context.Background(), update)
if err != nil {
return fmt.Errorf("error create web-share update updateid:%d: %v", update.UpdateId, err)
// log error, remove update, and continue
log.Printf("[pcloud] error create web-share update updateid:%d: %v", update.UpdateId, err)
err = sstore.RemoveScreenUpdate(context.Background(), update.UpdateId)
if err != nil {
// ignore this error too (although this is really problematic, there is nothing to do)
log.Printf("[pcloud] error removing screen update updateid:%d: %v", update.UpdateId, err)
}
continue
}
if webUpdate == nil {
continue
}
webUpdates = append(webUpdates, webUpdate)
}
ctx, cancelFn := context.WithTimeout(context.Background(), PCloudDefaultTimeout)
defer cancelFn()
req, err := makeAuthPostReq(ctx, WebShareUpdateUrl, authInfo, webUpdates)
if err != nil {
return fmt.Errorf("cannot create auth-post-req for %s: %v", WebShareUpdateUrl, err)
@@ -334,6 +359,13 @@ func DoWebScreenUpdates(ctx context.Context, authInfo AuthInfo, updateArr []*sst
if err != nil {
return err
}
for _, update := range webUpdates {
err = finalizeWebScreenUpdate(context.Background(), update)
if err != nil {
// ignore this error (nothing to do)
log.Printf("[pcloud] error finalizing web-update: %v\n", err)
}
}
return nil
}
@@ -353,5 +385,100 @@ func CreateWebScreen(ctx context.Context, screen *WebShareScreenType) error {
return nil
}
func updateWriterCheckMoreData() {
updateWriterCVar.L.Lock()
defer updateWriterCVar.L.Unlock()
for {
if updateWriterMoreData {
updateWriterMoreData = false
break
}
updateWriterCVar.Wait()
}
}
func setUpdateWriterRunning(running bool) {
updateWriterCVar.L.Lock()
defer updateWriterCVar.L.Unlock()
updateWriterRunning = running
}
func GetUpdateWriterRunning() bool {
updateWriterCVar.L.Lock()
defer updateWriterCVar.L.Unlock()
return updateWriterRunning
}
func StartUpdateWriter() {
updateWriterCVar.L.Lock()
defer updateWriterCVar.L.Unlock()
if updateWriterRunning {
return
}
updateWriterRunning = true
go runWebShareUpdateWriter()
}
func computeBackoff(numFailures int) time.Duration {
// TODO remove once API implemented
return time.Hour
switch numFailures {
case 1:
return 100 * time.Millisecond
case 2:
return 1 * time.Second
case 3:
return 5 * time.Second
case 4:
return time.Minute
case 5:
return 5 * time.Minute
case 6:
return time.Hour
default:
return time.Hour
}
}
func runWebShareUpdateWriter() {
defer func() {
setUpdateWriterRunning(false)
}()
log.Printf("[pcloud] starting update writer\n")
numErrors := 0
numSendErrors := 0
for {
updateArr, err := sstore.GetScreenUpdates(context.Background(), MaxUpdatesPerReq)
if err != nil {
log.Printf("[pcloud] error retrieving updates: %v", err)
time.Sleep(1 * time.Second)
numErrors++
if numErrors > MaxUpdateWriterErrors {
log.Printf("[pcloud] update-writer, too many read errors, exiting\n")
break
}
}
if len(updateArr) == 0 {
updateWriterCheckMoreData()
}
numErrors = 0
authInfo, err := getAuthInfo(context.Background())
err = DoWebScreenUpdates(authInfo, updateArr)
if err != nil {
numSendErrors++
backoffTime := computeBackoff(numSendErrors)
log.Printf("[pcloud] error processing web-updates (backoff=%v): %v\n", backoffTime, err)
time.Sleep(backoffTime)
continue
}
numSendErrors = 0
}
}
func NotifyUpdateWriter() {
updateWriterCVar.L.Lock()
defer updateWriterCVar.L.Unlock()
updateWriterMoreData = true
updateWriterCVar.Signal()
}
+50 -4
View File
@@ -1,9 +1,12 @@
package pcloud
import (
"context"
"fmt"
"github.com/scripthaus-dev/mshell/pkg/packet"
"github.com/scripthaus-dev/sh2-server/pkg/remote"
"github.com/scripthaus-dev/sh2-server/pkg/rtnstate"
"github.com/scripthaus-dev/sh2-server/pkg/sstore"
)
@@ -22,6 +25,7 @@ type TelemetryInputType struct {
type WebShareUpdateType struct {
ScreenId string `json:"screenid"`
LineId string `json:"lineid"`
UpdateId int64 `json:"-"` // just for internal use
UpdateType string `json:"updatetype"`
Screen *WebShareScreenType `json:"screen,omitempty"`
@@ -31,6 +35,7 @@ type WebShareUpdateType struct {
SVal string `json:"sval,omitempty"`
BVal bool `json:"bval,omitempty"`
DoneInfo *sstore.CmdDoneInfo `json:"doneinfo,omitempty"`
TermOpts *sstore.TermOpts `json:"termopts,omitempty"`
}
type WebShareRemotePtr struct {
@@ -39,6 +44,18 @@ type WebShareRemotePtr struct {
Name string `json:"name,omitempty"`
}
func webRemoteFromRemotePtr(rptr sstore.RemotePtrType) *WebShareRemotePtr {
if rptr.RemoteId == "" {
return nil
}
rcopy := remote.GetRemoteById(rptr.RemoteId).GetRemoteCopy()
return &WebShareRemotePtr{
Alias: rcopy.RemoteAlias,
CanonicalName: rcopy.RemoteCanonicalName,
Name: rptr.Name,
}
}
type WebShareScreenType struct {
ScreenId string `json:"screenid"`
ShareName string `json:"sharename"`
@@ -73,14 +90,24 @@ type WebShareLineType struct {
}
func webLineFromLine(line *sstore.LineType) (*WebShareLineType, error) {
return nil, nil
rtn := &WebShareLineType{
LineId: line.LineId,
Ts: line.Ts,
LineNum: line.LineNum,
LineType: line.LineType,
Renderer: line.Renderer,
Text: line.Text,
CmdId: line.CmdId,
Archived: line.Archived,
}
return rtn, nil
}
type WebShareCmdType struct {
LineId string `json:"lineid"`
CmdStr string `json:"cmdstr"`
RawCmdStr string `json:"rawcmdstr"`
Remote WebShareRemotePtr `json:"remote"`
Remote *WebShareRemotePtr `json:"remote"`
FeState sstore.FeStateType `json:"festate"`
TermOpts sstore.TermOpts `json:"termopts"`
Status string `json:"status"`
@@ -90,8 +117,27 @@ type WebShareCmdType struct {
RtnStateStr string `json:"rtnstatestr,omitempty"`
}
func webCmdFromCmd(cmd *sstore.CmdType) (*WebShareCmdType, error) {
return nil, nil
func webCmdFromCmd(lineId string, cmd *sstore.CmdType) (*WebShareCmdType, error) {
rtn := &WebShareCmdType{
LineId: lineId,
CmdStr: cmd.CmdStr,
RawCmdStr: cmd.RawCmdStr,
Remote: webRemoteFromRemotePtr(cmd.Remote),
FeState: cmd.FeState,
TermOpts: cmd.TermOpts,
Status: cmd.Status,
StartPk: cmd.StartPk,
DoneInfo: cmd.DoneInfo,
RtnState: cmd.RtnState,
}
if cmd.RtnState {
barr, err := rtnstate.GetRtnStateDiff(context.Background(), cmd.ScreenId, cmd.CmdId)
if err != nil {
return nil, fmt.Errorf("error creating rtnstate diff for cmd:%s: %v", cmd.CmdId, err)
}
rtn.RtnStateStr = string(barr)
}
return rtn, nil
}
type WebSharePtyData struct {
+11 -1
View File
@@ -1399,6 +1399,7 @@ func UpdateCmdTermOpts(ctx context.Context, screenId string, cmdId string, termO
txErr := WithTx(ctx, func(tx *TxWrap) error {
query := `UPDATE cmd SET termopts = ? WHERE screenid = ? AND cmdid = ?`
tx.Exec(query, termOpts, screenId, cmdId)
insertScreenCmdUpdate(tx, screenId, cmdId, UpdateType_CmdTermOpts)
return nil
})
return txErr
@@ -2447,7 +2448,16 @@ func insertScreenCmdUpdate(tx *TxWrap, screenId string, cmdId string, updateType
}
}
func RemoveScreenUpdate(ctx context.Context, updateId string) error {
func GetScreenUpdates(ctx context.Context, maxNum int) ([]*ScreenUpdateType, error) {
return WithTxRtn(ctx, func(tx *TxWrap) ([]*ScreenUpdateType, error) {
var updates []*ScreenUpdateType
query := `SELECT * FROM screenupdate ORDER BY updateid LIMIT ?`
tx.Select(&updates, query, maxNum)
return updates, nil
})
}
func RemoveScreenUpdate(ctx context.Context, updateId int64) error {
return WithTx(ctx, func(tx *TxWrap) error {
query := `DELETE FROM screenupdate WHERE updateid = ?`
tx.Exec(query, updateId)
+1
View File
@@ -88,6 +88,7 @@ const (
UpdateType_LineArchived = "line:archived"
UpdateType_LineRenderer = "line:renderer"
UpdateType_CmdStatus = "cmd:status"
UpdateType_CmdTermOpts = "cmd:termopts"
UpdateType_CmdDoneInfo = "cmd:doneinfo"
UpdateType_CmdRtnState = "cmd:rtnstate"
UpdateType_PtyPos = "pty:pos"