mirror of
https://github.com/wavetermdev/backup.git
synced 2026-08-05 13:57:07 -07:00
working on webshareupdates
This commit is contained in:
+2
-1
@@ -23,6 +23,7 @@ import (
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/cmdrunner"
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/pcloud"
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/remote"
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/rtnstate"
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/scbase"
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/scpacket"
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/scws"
|
||||
@@ -246,7 +247,7 @@ func HandleRtnState(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte(fmt.Sprintf("invalid cmdid: %v", err)))
|
||||
return
|
||||
}
|
||||
data, err := cmdrunner.GetRtnStateDiff(r.Context(), screenId, cmdId)
|
||||
data, err := rtnstate.GetRtnStateDiff(r.Context(), screenId, cmdId)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(fmt.Sprintf("cannot get rtnstate diff: %v", err)))
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
DROP TABLE webptypos;
|
||||
|
||||
DROP INDEX idx_screenupdate_ids;
|
||||
@@ -0,0 +1,8 @@
|
||||
CREATE TABLE webptypos (
|
||||
screenid varchar(36) NOT NULL,
|
||||
lineid varchar(36) NOT NULL,
|
||||
ptypos bigint NOT NULL,
|
||||
PRIMARY KEY (screenid, cmdid)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_screenupdate_ids ON screenupdate (screenid, lineid);
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/alessio/shellescape"
|
||||
"github.com/google/uuid"
|
||||
"github.com/scripthaus-dev/mshell/pkg/base"
|
||||
"github.com/scripthaus-dev/mshell/pkg/packet"
|
||||
@@ -2951,94 +2950,6 @@ func formatTextTable(totalCols int, data [][]string, colMeta []ColMeta) []string
|
||||
return rtn
|
||||
}
|
||||
|
||||
const MaxDiffKeyLen = 40
|
||||
const MaxDiffValLen = 50
|
||||
|
||||
func displayStateUpdateDiff(buf *bytes.Buffer, oldState packet.ShellState, newState packet.ShellState) {
|
||||
if newState.Cwd != oldState.Cwd {
|
||||
buf.WriteString(fmt.Sprintf("cwd %s\n", newState.Cwd))
|
||||
}
|
||||
if !bytes.Equal(newState.ShellVars, oldState.ShellVars) {
|
||||
newEnvMap := shexec.DeclMapFromState(&newState)
|
||||
oldEnvMap := shexec.DeclMapFromState(&oldState)
|
||||
for key, newVal := range newEnvMap {
|
||||
oldVal, found := oldEnvMap[key]
|
||||
if !found || !shexec.DeclsEqual(false, oldVal, newVal) {
|
||||
var exportStr string
|
||||
if newVal.IsExport() {
|
||||
exportStr = "export "
|
||||
}
|
||||
buf.WriteString(fmt.Sprintf("%s%s=%s\n", exportStr, utilfn.EllipsisStr(key, MaxDiffKeyLen), utilfn.EllipsisStr(newVal.Value, MaxDiffValLen)))
|
||||
}
|
||||
}
|
||||
for key, _ := range oldEnvMap {
|
||||
_, found := newEnvMap[key]
|
||||
if !found {
|
||||
buf.WriteString(fmt.Sprintf("unset %s\n", utilfn.EllipsisStr(key, MaxDiffKeyLen)))
|
||||
}
|
||||
}
|
||||
}
|
||||
if newState.Aliases != oldState.Aliases {
|
||||
newAliasMap, _ := ParseAliases(newState.Aliases)
|
||||
oldAliasMap, _ := ParseAliases(oldState.Aliases)
|
||||
for aliasName, newAliasVal := range newAliasMap {
|
||||
oldAliasVal, found := oldAliasMap[aliasName]
|
||||
if !found || newAliasVal != oldAliasVal {
|
||||
buf.WriteString(fmt.Sprintf("alias %s\n", utilfn.EllipsisStr(shellescape.Quote(aliasName), MaxDiffKeyLen)))
|
||||
}
|
||||
}
|
||||
for aliasName, _ := range oldAliasMap {
|
||||
_, found := newAliasMap[aliasName]
|
||||
if !found {
|
||||
buf.WriteString(fmt.Sprintf("unalias %s\n", utilfn.EllipsisStr(shellescape.Quote(aliasName), MaxDiffKeyLen)))
|
||||
}
|
||||
}
|
||||
}
|
||||
if newState.Funcs != oldState.Funcs {
|
||||
newFuncMap, _ := ParseFuncs(newState.Funcs)
|
||||
oldFuncMap, _ := ParseFuncs(oldState.Funcs)
|
||||
for funcName, newFuncVal := range newFuncMap {
|
||||
oldFuncVal, found := oldFuncMap[funcName]
|
||||
if !found || newFuncVal != oldFuncVal {
|
||||
buf.WriteString(fmt.Sprintf("function %s\n", utilfn.EllipsisStr(shellescape.Quote(funcName), MaxDiffKeyLen)))
|
||||
}
|
||||
}
|
||||
for funcName, _ := range oldFuncMap {
|
||||
_, found := newFuncMap[funcName]
|
||||
if !found {
|
||||
buf.WriteString(fmt.Sprintf("unset -f %s\n", utilfn.EllipsisStr(shellescape.Quote(funcName), MaxDiffKeyLen)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func GetRtnStateDiff(ctx context.Context, screenId string, cmdId string) ([]byte, error) {
|
||||
cmd, err := sstore.GetCmdByScreenId(ctx, screenId, cmdId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cmd == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if !cmd.RtnState {
|
||||
return nil, nil
|
||||
}
|
||||
if cmd.RtnStatePtr.IsEmpty() {
|
||||
return nil, nil
|
||||
}
|
||||
var outputBytes bytes.Buffer
|
||||
initialState, err := sstore.GetFullState(ctx, cmd.StatePtr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting initial full state: %v", err)
|
||||
}
|
||||
rtnState, err := sstore.GetFullState(ctx, cmd.RtnStatePtr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting rtn full state: %v", err)
|
||||
}
|
||||
displayStateUpdateDiff(&outputBytes, *initialState, *rtnState)
|
||||
return outputBytes.Bytes(), nil
|
||||
}
|
||||
|
||||
func isValidInScope(scopeName string, varName string) bool {
|
||||
for _, varScope := range SetVarScopes {
|
||||
if varScope.ScopeName == scopeName {
|
||||
|
||||
@@ -269,87 +269,3 @@ func EvalMetaCommand(ctx context.Context, origPk *scpacket.FeCommandPacketType)
|
||||
}
|
||||
return rtnPk, nil
|
||||
}
|
||||
|
||||
func parseAliasStmt(stmt *syntax.Stmt, sourceStr string) (string, string, error) {
|
||||
cmd := stmt.Cmd
|
||||
callExpr, ok := cmd.(*syntax.CallExpr)
|
||||
if !ok {
|
||||
return "", "", fmt.Errorf("wrong cmd type for alias")
|
||||
}
|
||||
if len(callExpr.Args) != 2 {
|
||||
return "", "", fmt.Errorf("wrong number of words in alias expr wordslen=%d", len(callExpr.Args))
|
||||
}
|
||||
firstWord := callExpr.Args[0]
|
||||
if firstWord.Lit() != "alias" {
|
||||
return "", "", fmt.Errorf("invalid alias cmd word (not 'alias')")
|
||||
}
|
||||
secondWord := callExpr.Args[1]
|
||||
var ectx simpleexpand.SimpleExpandContext // no homedir, do not want ~ expansion
|
||||
val, _ := simpleexpand.SimpleExpandWord(ectx, secondWord, sourceStr)
|
||||
eqIdx := strings.Index(val, "=")
|
||||
if eqIdx == -1 {
|
||||
return "", "", fmt.Errorf("no '=' in alias definition")
|
||||
}
|
||||
return val[0:eqIdx], val[eqIdx+1:], nil
|
||||
}
|
||||
|
||||
func ParseAliases(aliases string) (map[string]string, error) {
|
||||
r := strings.NewReader(aliases)
|
||||
parser := syntax.NewParser(syntax.Variant(syntax.LangBash))
|
||||
file, err := parser.Parse(r, "aliases")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rtn := make(map[string]string)
|
||||
for _, stmt := range file.Stmts {
|
||||
aliasName, aliasVal, err := parseAliasStmt(stmt, aliases)
|
||||
if err != nil {
|
||||
// fmt.Printf("stmt-err: %v\n", err)
|
||||
continue
|
||||
}
|
||||
if aliasName != "" {
|
||||
rtn[aliasName] = aliasVal
|
||||
}
|
||||
}
|
||||
return rtn, nil
|
||||
}
|
||||
|
||||
func parseFuncStmt(stmt *syntax.Stmt, source string) (string, string, error) {
|
||||
cmd := stmt.Cmd
|
||||
funcDecl, ok := cmd.(*syntax.FuncDecl)
|
||||
if !ok {
|
||||
return "", "", fmt.Errorf("cmd not FuncDecl")
|
||||
}
|
||||
name := funcDecl.Name.Value
|
||||
// fmt.Printf("func: [%s]\n", name)
|
||||
funcBody := funcDecl.Body
|
||||
// fmt.Printf(" %d:%d\n", funcBody.Cmd.Pos().Offset(), funcBody.Cmd.End().Offset())
|
||||
bodyStr := source[funcBody.Cmd.Pos().Offset():funcBody.Cmd.End().Offset()]
|
||||
// fmt.Printf("<<<\n%s\n>>>\n", bodyStr)
|
||||
// fmt.Printf("\n")
|
||||
return name, bodyStr, nil
|
||||
}
|
||||
|
||||
func ParseFuncs(funcs string) (map[string]string, error) {
|
||||
r := strings.NewReader(funcs)
|
||||
parser := syntax.NewParser(syntax.Variant(syntax.LangBash))
|
||||
file, err := parser.Parse(r, "funcs")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rtn := make(map[string]string)
|
||||
for _, stmt := range file.Stmts {
|
||||
funcName, funcVal, err := parseFuncStmt(stmt, funcs)
|
||||
if err != nil {
|
||||
// TODO where to put parse errors
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(funcName, "_mshell_") {
|
||||
continue
|
||||
}
|
||||
if funcName != "" {
|
||||
rtn[funcName] = funcVal
|
||||
}
|
||||
}
|
||||
return rtn, nil
|
||||
}
|
||||
|
||||
+152
-30
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
@@ -12,6 +13,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/rtnstate"
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/scbase"
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/sstore"
|
||||
)
|
||||
@@ -19,37 +21,11 @@ import (
|
||||
const PCloudEndpoint = "https://api.getprompt.dev/central"
|
||||
const PCloudEndpointVarName = "PCLOUD_ENDPOINT"
|
||||
const APIVersion = 1
|
||||
const MaxPtyUpdateSize = (128 * 1024) + 1
|
||||
|
||||
const TelemetryUrl = "/telemetry"
|
||||
const NoTelemetryUrl = "/no-telemetry"
|
||||
const CreateCloudSessionUrl = "/auth/create-cloud-session"
|
||||
|
||||
type NoTelemetryInputType struct {
|
||||
ClientId string `json:"clientid"`
|
||||
Value bool `json:"value"`
|
||||
}
|
||||
|
||||
type TelemetryInputType struct {
|
||||
UserId string `json:"userid"`
|
||||
ClientId string `json:"clientid"`
|
||||
CurDay string `json:"curday"`
|
||||
Activity []*sstore.ActivityType `json:"activity"`
|
||||
}
|
||||
|
||||
type CloudSession struct {
|
||||
SessionId string `json:"sessionid"`
|
||||
ViewKey string `json:"viewkey"`
|
||||
WriteKey string `json:"writekey"`
|
||||
EncType string `json:"enctype"`
|
||||
UpdateVTS int64 `json:"updatevts"`
|
||||
|
||||
EncSessionData []byte `json:"enc_sessiondata" enc:"*"`
|
||||
Name string `json:"-" enc:"name"`
|
||||
}
|
||||
|
||||
func (cs *CloudSession) GetOData() string {
|
||||
return fmt.Sprintf("session:%s", cs.SessionId)
|
||||
}
|
||||
const CreateWebScreenUrl = "/auth/create-web-screen"
|
||||
|
||||
type AuthInfo struct {
|
||||
UserId string `json:"userid"`
|
||||
@@ -194,12 +170,158 @@ func getAuthInfo(ctx context.Context) (AuthInfo, error) {
|
||||
return AuthInfo{UserId: clientData.UserId, ClientId: clientData.ClientId}, nil
|
||||
}
|
||||
|
||||
func CreateCloudSession(ctx context.Context) error {
|
||||
func defaultError(err error, estr string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return errors.New(estr)
|
||||
}
|
||||
|
||||
func makeWebScreenUpdate(ctx context.Context, update sstore.ScreenUpdateType) (*WebShareUpdateType, error) {
|
||||
rtn := &WebShareUpdateType{
|
||||
ScreenId: update.ScreenId,
|
||||
LineId: update.LineId,
|
||||
UpdateType: update.UpdateType,
|
||||
}
|
||||
switch update.UpdateType {
|
||||
case sstore.UpdateType_ScreenNew:
|
||||
screen, err := sstore.GetScreenById(ctx, update.ScreenId)
|
||||
if err != nil || screen == nil {
|
||||
return nil, fmt.Errorf("error getting screen: %v", defaultError(err, "not found"))
|
||||
}
|
||||
rtn.Screen, err = webScreenFromScreen(screen)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error converting screen to web-screen: %v", err)
|
||||
}
|
||||
|
||||
case sstore.UpdateType_ScreenDel:
|
||||
break
|
||||
|
||||
case sstore.UpdateType_ScreenName:
|
||||
screen, err := sstore.GetScreenById(ctx, update.ScreenId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting screen: %v", err)
|
||||
}
|
||||
if screen == nil || screen.WebShareOpts == nil || screen.WebShareOpts.ShareName == "" {
|
||||
return nil, fmt.Errorf("invalid screen sharename (makeWebScreenUpdate)")
|
||||
}
|
||||
rtn.SVal = screen.WebShareOpts.ShareName
|
||||
|
||||
case sstore.UpdateType_LineNew:
|
||||
line, cmd, err := sstore.GetLineCmdByLineId(ctx, update.ScreenId, update.LineId)
|
||||
if err != nil || line == nil {
|
||||
return nil, fmt.Errorf("error getting line/cmd: %v", defaultError(err, "not found"))
|
||||
}
|
||||
rtn.Line, err = webLineFromLine(line)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error converting line to web-line: %v", err)
|
||||
}
|
||||
if cmd != nil {
|
||||
rtn.Cmd, err = webCmdFromCmd(cmd)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error converting cmd to web-cmd: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
case sstore.UpdateType_LineDel:
|
||||
break
|
||||
|
||||
case sstore.UpdateType_LineArchived:
|
||||
line, err := sstore.GetLineById(ctx, update.ScreenId, update.LineId)
|
||||
if err != nil || line == nil {
|
||||
return nil, fmt.Errorf("error getting line: %v", defaultError(err, "not found"))
|
||||
}
|
||||
rtn.BVal = line.Archived
|
||||
|
||||
case sstore.UpdateType_LineRenderer:
|
||||
line, err := sstore.GetLineById(ctx, update.ScreenId, update.LineId)
|
||||
if err != nil || line == nil {
|
||||
return nil, fmt.Errorf("error getting line: %v", defaultError(err, "not found"))
|
||||
}
|
||||
rtn.SVal = line.Renderer
|
||||
|
||||
case sstore.UpdateType_CmdStatus:
|
||||
_, 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.SVal = cmd.Status
|
||||
|
||||
case sstore.UpdateType_CmdDoneInfo:
|
||||
_, 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.DoneInfo = cmd.DoneInfo
|
||||
|
||||
case sstore.UpdateType_CmdRtnState:
|
||||
_, 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"))
|
||||
}
|
||||
data, err := rtnstate.GetRtnStateDiff(ctx, update.ScreenId, cmd.CmdId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot compute rtnstate: %v", err)
|
||||
}
|
||||
rtn.SVal = string(data)
|
||||
|
||||
case sstore.UpdateType_PtyPos:
|
||||
cmdId, err := sstore.GetCmdIdFromLineId(ctx, update.ScreenId, update.LineId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting cmdid: %v", err)
|
||||
}
|
||||
ptyPos, err := sstore.GetWebPtyPos(ctx, update.ScreenId, update.LineId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting ptypos: %v", err)
|
||||
}
|
||||
realOffset, data, err := sstore.ReadPtyOutFile(ctx, update.ScreenId, cmdId, ptyPos, MaxPtyUpdateSize)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting ptydata: %v", err)
|
||||
}
|
||||
rtn.PtyData = &WebSharePtyData{PtyPos: realOffset, Data: data}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported update type (pcloud/makeWebScreenUpdate): %s\n", update.UpdateType)
|
||||
}
|
||||
return rtn, nil
|
||||
}
|
||||
|
||||
func finalizeWebScreenUpdate(ctx context.Context, screenUpdate sstore.ScreenUpdateType, webUpdate *WebShareUpdateType) error {
|
||||
switch screenUpdate.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)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err := sstore.SetWebPtyPos(ctx, screenUpdate.ScreenId, screenUpdate.LineId, newPos)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
default:
|
||||
err := sstore.RemoveScreenUpdate(ctx, screenUpdate.UpdateType)
|
||||
if err != nil {
|
||||
// this is not great, this *should* never fail and is not easy to recover from
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DoWebScreenUpdate(ctx context.Context, update sstore.ScreenUpdateType) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateWebScreen(ctx context.Context, screen *WebShareScreenType) error {
|
||||
authInfo, err := getAuthInfo(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req, err := makeAuthPostReq(ctx, CreateCloudSessionUrl, authInfo, nil)
|
||||
req, err := makeAuthPostReq(ctx, CreateWebScreenUrl, authInfo, screen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
package pcloud
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/scripthaus-dev/mshell/pkg/packet"
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/sstore"
|
||||
)
|
||||
|
||||
type NoTelemetryInputType struct {
|
||||
ClientId string `json:"clientid"`
|
||||
Value bool `json:"value"`
|
||||
}
|
||||
|
||||
type TelemetryInputType struct {
|
||||
UserId string `json:"userid"`
|
||||
ClientId string `json:"clientid"`
|
||||
CurDay string `json:"curday"`
|
||||
Activity []*sstore.ActivityType `json:"activity"`
|
||||
}
|
||||
|
||||
type WebShareUpdateType struct {
|
||||
ScreenId string `json:"screenid"`
|
||||
LineId string `json:"lineid"`
|
||||
UpdateType string `json:"updatetype"`
|
||||
|
||||
Screen *WebShareScreenType `json:"screen,omitempty"`
|
||||
Line *WebShareLineType `json:"line,omitempty"`
|
||||
Cmd *WebShareCmdType `json:"cmd,omitempty"`
|
||||
PtyData *WebSharePtyData `json:"ptydata,omitempty"`
|
||||
SVal string `json:"sval,omitempty"`
|
||||
BVal bool `json:"bval,omitempty"`
|
||||
DoneInfo *sstore.CmdDoneInfo `json:"doneinfo,omitempty"`
|
||||
}
|
||||
|
||||
type WebShareRemotePtr struct {
|
||||
Alias string `json:"remotealias,omitempty"`
|
||||
CanonicalName string `json:"remotecanonicalname"`
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
type WebShareScreenType struct {
|
||||
ScreenId string `json:"screenid"`
|
||||
ShareName string `json:"sharename"`
|
||||
ViewKey string `json:"viewkey"`
|
||||
}
|
||||
|
||||
func webScreenFromScreen(s *sstore.ScreenType) (*WebShareScreenType, error) {
|
||||
if s == nil || s.ScreenId == "" {
|
||||
return nil, fmt.Errorf("invalid nil screen")
|
||||
}
|
||||
if s.WebShareOpts == nil {
|
||||
return nil, fmt.Errorf("invalid screen, no WebShareOpts")
|
||||
}
|
||||
if s.WebShareOpts.ViewKey == "" {
|
||||
return nil, fmt.Errorf("invalid screen, no ViewKey")
|
||||
}
|
||||
if s.WebShareOpts.ShareName == "" {
|
||||
return nil, fmt.Errorf("invalid screen, no ShareName")
|
||||
}
|
||||
return &WebShareScreenType{ScreenId: s.ScreenId, ShareName: s.WebShareOpts.ShareName, ViewKey: s.WebShareOpts.ViewKey}, nil
|
||||
}
|
||||
|
||||
type WebShareLineType struct {
|
||||
LineId string `json:"lineid"`
|
||||
Ts int64 `json:"ts"`
|
||||
LineNum int64 `json:"linenum"`
|
||||
LineType string `json:"linetype"`
|
||||
Renderer string `json:"renderer,omitempty"`
|
||||
Text string `json:"text,omitempty"`
|
||||
CmdId string `json:"cmdid,omitempty"`
|
||||
Archived bool `json:"archived,omitempty"`
|
||||
}
|
||||
|
||||
func webLineFromLine(line *sstore.LineType) (*WebShareLineType, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type WebShareCmdType struct {
|
||||
LineId string `json:"lineid"`
|
||||
CmdStr string `json:"cmdstr"`
|
||||
RawCmdStr string `json:"rawcmdstr"`
|
||||
Remote WebShareRemotePtr `json:"remote"`
|
||||
FeState sstore.FeStateType `json:"festate"`
|
||||
TermOpts sstore.TermOpts `json:"termopts"`
|
||||
Status string `json:"status"`
|
||||
StartPk *packet.CmdStartPacketType `json:"startpk,omitempty"`
|
||||
DoneInfo *sstore.CmdDoneInfo `json:"doneinfo,omitempty"`
|
||||
RtnState bool `json:"rtnstate,omitempty"`
|
||||
RtnStateStr string `json:"rtnstatestr,omitempty"`
|
||||
}
|
||||
|
||||
func webCmdFromCmd(cmd *sstore.CmdType) (*WebShareCmdType, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type WebSharePtyData struct {
|
||||
PtyPos int64 `json:"ptypos,omitempty"`
|
||||
Data []byte `json:"data,omitempty"`
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
package rtnstate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/alessio/shellescape"
|
||||
"github.com/scripthaus-dev/mshell/pkg/packet"
|
||||
"github.com/scripthaus-dev/mshell/pkg/shexec"
|
||||
"github.com/scripthaus-dev/mshell/pkg/simpleexpand"
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/sstore"
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/utilfn"
|
||||
"mvdan.cc/sh/v3/syntax"
|
||||
)
|
||||
|
||||
func parseAliasStmt(stmt *syntax.Stmt, sourceStr string) (string, string, error) {
|
||||
cmd := stmt.Cmd
|
||||
callExpr, ok := cmd.(*syntax.CallExpr)
|
||||
if !ok {
|
||||
return "", "", fmt.Errorf("wrong cmd type for alias")
|
||||
}
|
||||
if len(callExpr.Args) != 2 {
|
||||
return "", "", fmt.Errorf("wrong number of words in alias expr wordslen=%d", len(callExpr.Args))
|
||||
}
|
||||
firstWord := callExpr.Args[0]
|
||||
if firstWord.Lit() != "alias" {
|
||||
return "", "", fmt.Errorf("invalid alias cmd word (not 'alias')")
|
||||
}
|
||||
secondWord := callExpr.Args[1]
|
||||
var ectx simpleexpand.SimpleExpandContext // no homedir, do not want ~ expansion
|
||||
val, _ := simpleexpand.SimpleExpandWord(ectx, secondWord, sourceStr)
|
||||
eqIdx := strings.Index(val, "=")
|
||||
if eqIdx == -1 {
|
||||
return "", "", fmt.Errorf("no '=' in alias definition")
|
||||
}
|
||||
return val[0:eqIdx], val[eqIdx+1:], nil
|
||||
}
|
||||
|
||||
func ParseAliases(aliases string) (map[string]string, error) {
|
||||
r := strings.NewReader(aliases)
|
||||
parser := syntax.NewParser(syntax.Variant(syntax.LangBash))
|
||||
file, err := parser.Parse(r, "aliases")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rtn := make(map[string]string)
|
||||
for _, stmt := range file.Stmts {
|
||||
aliasName, aliasVal, err := parseAliasStmt(stmt, aliases)
|
||||
if err != nil {
|
||||
// fmt.Printf("stmt-err: %v\n", err)
|
||||
continue
|
||||
}
|
||||
if aliasName != "" {
|
||||
rtn[aliasName] = aliasVal
|
||||
}
|
||||
}
|
||||
return rtn, nil
|
||||
}
|
||||
|
||||
func parseFuncStmt(stmt *syntax.Stmt, source string) (string, string, error) {
|
||||
cmd := stmt.Cmd
|
||||
funcDecl, ok := cmd.(*syntax.FuncDecl)
|
||||
if !ok {
|
||||
return "", "", fmt.Errorf("cmd not FuncDecl")
|
||||
}
|
||||
name := funcDecl.Name.Value
|
||||
// fmt.Printf("func: [%s]\n", name)
|
||||
funcBody := funcDecl.Body
|
||||
// fmt.Printf(" %d:%d\n", funcBody.Cmd.Pos().Offset(), funcBody.Cmd.End().Offset())
|
||||
bodyStr := source[funcBody.Cmd.Pos().Offset():funcBody.Cmd.End().Offset()]
|
||||
// fmt.Printf("<<<\n%s\n>>>\n", bodyStr)
|
||||
// fmt.Printf("\n")
|
||||
return name, bodyStr, nil
|
||||
}
|
||||
|
||||
func ParseFuncs(funcs string) (map[string]string, error) {
|
||||
r := strings.NewReader(funcs)
|
||||
parser := syntax.NewParser(syntax.Variant(syntax.LangBash))
|
||||
file, err := parser.Parse(r, "funcs")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rtn := make(map[string]string)
|
||||
for _, stmt := range file.Stmts {
|
||||
funcName, funcVal, err := parseFuncStmt(stmt, funcs)
|
||||
if err != nil {
|
||||
// TODO where to put parse errors
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(funcName, "_mshell_") {
|
||||
continue
|
||||
}
|
||||
if funcName != "" {
|
||||
rtn[funcName] = funcVal
|
||||
}
|
||||
}
|
||||
return rtn, nil
|
||||
}
|
||||
|
||||
const MaxDiffKeyLen = 40
|
||||
const MaxDiffValLen = 50
|
||||
|
||||
func displayStateUpdateDiff(buf *bytes.Buffer, oldState packet.ShellState, newState packet.ShellState) {
|
||||
if newState.Cwd != oldState.Cwd {
|
||||
buf.WriteString(fmt.Sprintf("cwd %s\n", newState.Cwd))
|
||||
}
|
||||
if !bytes.Equal(newState.ShellVars, oldState.ShellVars) {
|
||||
newEnvMap := shexec.DeclMapFromState(&newState)
|
||||
oldEnvMap := shexec.DeclMapFromState(&oldState)
|
||||
for key, newVal := range newEnvMap {
|
||||
oldVal, found := oldEnvMap[key]
|
||||
if !found || !shexec.DeclsEqual(false, oldVal, newVal) {
|
||||
var exportStr string
|
||||
if newVal.IsExport() {
|
||||
exportStr = "export "
|
||||
}
|
||||
buf.WriteString(fmt.Sprintf("%s%s=%s\n", exportStr, utilfn.EllipsisStr(key, MaxDiffKeyLen), utilfn.EllipsisStr(newVal.Value, MaxDiffValLen)))
|
||||
}
|
||||
}
|
||||
for key, _ := range oldEnvMap {
|
||||
_, found := newEnvMap[key]
|
||||
if !found {
|
||||
buf.WriteString(fmt.Sprintf("unset %s\n", utilfn.EllipsisStr(key, MaxDiffKeyLen)))
|
||||
}
|
||||
}
|
||||
}
|
||||
if newState.Aliases != oldState.Aliases {
|
||||
newAliasMap, _ := ParseAliases(newState.Aliases)
|
||||
oldAliasMap, _ := ParseAliases(oldState.Aliases)
|
||||
for aliasName, newAliasVal := range newAliasMap {
|
||||
oldAliasVal, found := oldAliasMap[aliasName]
|
||||
if !found || newAliasVal != oldAliasVal {
|
||||
buf.WriteString(fmt.Sprintf("alias %s\n", utilfn.EllipsisStr(shellescape.Quote(aliasName), MaxDiffKeyLen)))
|
||||
}
|
||||
}
|
||||
for aliasName, _ := range oldAliasMap {
|
||||
_, found := newAliasMap[aliasName]
|
||||
if !found {
|
||||
buf.WriteString(fmt.Sprintf("unalias %s\n", utilfn.EllipsisStr(shellescape.Quote(aliasName), MaxDiffKeyLen)))
|
||||
}
|
||||
}
|
||||
}
|
||||
if newState.Funcs != oldState.Funcs {
|
||||
newFuncMap, _ := ParseFuncs(newState.Funcs)
|
||||
oldFuncMap, _ := ParseFuncs(oldState.Funcs)
|
||||
for funcName, newFuncVal := range newFuncMap {
|
||||
oldFuncVal, found := oldFuncMap[funcName]
|
||||
if !found || newFuncVal != oldFuncVal {
|
||||
buf.WriteString(fmt.Sprintf("function %s\n", utilfn.EllipsisStr(shellescape.Quote(funcName), MaxDiffKeyLen)))
|
||||
}
|
||||
}
|
||||
for funcName, _ := range oldFuncMap {
|
||||
_, found := newFuncMap[funcName]
|
||||
if !found {
|
||||
buf.WriteString(fmt.Sprintf("unset -f %s\n", utilfn.EllipsisStr(shellescape.Quote(funcName), MaxDiffKeyLen)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func GetRtnStateDiff(ctx context.Context, screenId string, cmdId string) ([]byte, error) {
|
||||
cmd, err := sstore.GetCmdByScreenId(ctx, screenId, cmdId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cmd == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if !cmd.RtnState {
|
||||
return nil, nil
|
||||
}
|
||||
if cmd.RtnStatePtr.IsEmpty() {
|
||||
return nil, nil
|
||||
}
|
||||
var outputBytes bytes.Buffer
|
||||
initialState, err := sstore.GetFullState(ctx, cmd.StatePtr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting initial full state: %v", err)
|
||||
}
|
||||
rtnState, err := sstore.GetFullState(ctx, cmd.RtnStatePtr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting rtn full state: %v", err)
|
||||
}
|
||||
displayStateUpdateDiff(&outputBytes, *initialState, *rtnState)
|
||||
return outputBytes.Bytes(), nil
|
||||
}
|
||||
+89
-15
@@ -725,6 +725,14 @@ func FindLineIdByArg(ctx context.Context, screenId string, lineArg string) (stri
|
||||
return lineId, nil
|
||||
}
|
||||
|
||||
func GetCmdIdFromLineId(ctx context.Context, screenId string, lineId string) (string, error) {
|
||||
return WithTxRtn(ctx, func(tx *TxWrap) (string, error) {
|
||||
query := `SELECT cmdid FROM line WHERE screenid = ? AND lineid = ?`
|
||||
cmdId := tx.GetString(query, screenId, lineId)
|
||||
return cmdId, nil
|
||||
})
|
||||
}
|
||||
|
||||
func GetLineCmdByLineId(ctx context.Context, screenId string, lineId string) (*LineType, *CmdType, error) {
|
||||
return WithTxRtn3(ctx, func(tx *TxWrap) (*LineType, *CmdType, error) {
|
||||
var lineVal LineType
|
||||
@@ -792,7 +800,7 @@ INSERT INTO cmd ( screenid, cmdid, remoteownerid, remoteid, remotename, cmdstr,
|
||||
tx.NamedExec(query, cmdMap)
|
||||
}
|
||||
if isWebShare(tx, line.ScreenId) {
|
||||
insertScreenUpdate(tx, line.ScreenId, line.LineId, UpdateType_LineNew)
|
||||
insertScreenLineUpdate(tx, line.ScreenId, line.LineId, UpdateType_LineNew)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@@ -829,8 +837,8 @@ func UpdateCmdDoneInfo(ctx context.Context, ck base.CommandKey, doneInfo *CmdDon
|
||||
return err
|
||||
}
|
||||
if isWebShare(tx, screenId) {
|
||||
insertScreenUpdateByCmdId(tx, screenId, ck.GetCmdId(), UpdateType_CmdDoneInfo)
|
||||
insertScreenUpdateByCmdId(tx, screenId, ck.GetCmdId(), UpdateType_CmdStatus)
|
||||
insertScreenCmdUpdate(tx, screenId, ck.GetCmdId(), UpdateType_CmdDoneInfo)
|
||||
insertScreenCmdUpdate(tx, screenId, ck.GetCmdId(), UpdateType_CmdStatus)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@@ -852,7 +860,7 @@ func UpdateCmdRtnState(ctx context.Context, ck base.CommandKey, statePtr ShellSt
|
||||
query := `UPDATE cmd SET rtnbasehash = ?, rtndiffhasharr = ? WHERE screenid = ? AND cmdid = ?`
|
||||
tx.Exec(query, statePtr.BaseHash, quickJsonArr(statePtr.DiffHashArr), screenId, ck.GetCmdId())
|
||||
if isWebShare(tx, screenId) {
|
||||
insertScreenUpdateByCmdId(tx, screenId, ck.GetCmdId(), UpdateType_CmdRtnState)
|
||||
insertScreenCmdUpdate(tx, screenId, ck.GetCmdId(), UpdateType_CmdRtnState)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@@ -870,9 +878,6 @@ func AppendCmdErrorPk(ctx context.Context, errPk *packet.CmdErrorPacketType) err
|
||||
return WithTx(ctx, func(tx *TxWrap) error {
|
||||
query := `UPDATE cmd SET runout = json_insert(runout, '$[#]', ?) WHERE screenid = ? AND cmdid = ?`
|
||||
tx.Exec(query, quickJson(errPk), screenId, errPk.CK.GetCmdId())
|
||||
if isWebShare(tx, screenId) {
|
||||
insertScreenUpdateByCmdId(tx, screenId, errPk.CK.GetCmdId(), UpdateType_CmdRunOut)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -887,16 +892,28 @@ func ReInitFocus(ctx context.Context) error {
|
||||
|
||||
func HangupAllRunningCmds(ctx context.Context) error {
|
||||
return WithTx(ctx, func(tx *TxWrap) error {
|
||||
query := `UPDATE cmd SET status = ? WHERE status = ?`
|
||||
var cmdPtrs []CmdPtr
|
||||
query := `SELECT screenid, cmdid FROM cmd WHERE status = ?`
|
||||
tx.Select(&cmdPtrs, query, CmdStatusRunning)
|
||||
query = `UPDATE cmd SET status = ? WHERE status = ?`
|
||||
tx.Exec(query, CmdStatusHangup, CmdStatusRunning)
|
||||
for _, cmdPtr := range cmdPtrs {
|
||||
insertScreenCmdUpdate(tx, cmdPtr.ScreenId, cmdPtr.CmdId, UpdateType_CmdStatus)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func HangupRunningCmdsByRemoteId(ctx context.Context, remoteId string) error {
|
||||
return WithTx(ctx, func(tx *TxWrap) error {
|
||||
query := `UPDATE cmd SET status = ? WHERE status = ? AND remoteid = ?`
|
||||
var cmdPtrs []CmdPtr
|
||||
query := `SELECT screenid, cmdid FROM cmd WHERE status = ? AND remoteid = ?`
|
||||
tx.Select(&cmdPtrs, query, CmdStatusRunning, remoteId)
|
||||
query = `UPDATE cmd SET status = ? WHERE status = ? AND remoteid = ?`
|
||||
tx.Exec(query, CmdStatusHangup, CmdStatusRunning, remoteId)
|
||||
for _, cmdPtr := range cmdPtrs {
|
||||
insertScreenCmdUpdate(tx, cmdPtr.ScreenId, cmdPtr.CmdId, UpdateType_CmdStatus)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -905,6 +922,7 @@ func HangupCmd(ctx context.Context, ck base.CommandKey) error {
|
||||
return WithTx(ctx, func(tx *TxWrap) error {
|
||||
query := `UPDATE cmd SET status = ? WHERE screenid = ? AND cmdid = ?`
|
||||
tx.Exec(query, CmdStatusHangup, ck.GetGroupId(), ck.GetCmdId())
|
||||
insertScreenCmdUpdate(tx, ck.GetGroupId(), ck.GetCmdId(), UpdateType_CmdStatus)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -1838,7 +1856,7 @@ func UpdateLineRenderer(ctx context.Context, screenId string, lineId string, ren
|
||||
query := `UPDATE line SET renderer = ? WHERE lineid = ?`
|
||||
tx.Exec(query, renderer, lineId)
|
||||
if isWebShare(tx, screenId) {
|
||||
insertScreenUpdate(tx, screenId, lineId, UpdateType_LineRenderer)
|
||||
insertScreenLineUpdate(tx, screenId, lineId, UpdateType_LineRenderer)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@@ -1867,7 +1885,7 @@ func SetLineArchivedById(ctx context.Context, screenId string, lineId string, ar
|
||||
query := `UPDATE line SET archived = ? WHERE lineid = ?`
|
||||
tx.Exec(query, archived, lineId)
|
||||
if isWebShare(tx, screenId) {
|
||||
insertScreenUpdate(tx, screenId, lineId, UpdateType_LineArchived)
|
||||
insertScreenLineUpdate(tx, screenId, lineId, UpdateType_LineArchived)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@@ -1900,7 +1918,7 @@ func PurgeLinesByIds(ctx context.Context, screenId string, lineIds []string) err
|
||||
}
|
||||
}
|
||||
if isWS {
|
||||
insertScreenUpdate(tx, screenId, lineId, UpdateType_LineDel)
|
||||
insertScreenLineUpdate(tx, screenId, lineId, UpdateType_LineDel)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -2360,6 +2378,7 @@ func ScreenWebShareStart(ctx context.Context, screenId string, shareOpts ScreenW
|
||||
}
|
||||
query = `UPDATE screen SET sharemode = ?, webshareopts = ? WHERE screenid = ?`
|
||||
tx.Exec(query, ShareModeWeb, quickJson(shareOpts), screenId)
|
||||
insertScreenUpdate(tx, screenId, UpdateType_ScreenNew)
|
||||
query = `INSERT INTO screenupdate (screenid, lineid, updatetype, updatets)
|
||||
SELECT screenid, lineid, ?, ? FROM line WHERE screenid = ? ORDER BY linenum`
|
||||
tx.Exec(query, UpdateType_LineNew, time.Now().UnixMilli(), screenId)
|
||||
@@ -2381,6 +2400,7 @@ func ScreenWebShareStop(ctx context.Context, screenId string) error {
|
||||
tx.Exec(query, ShareModeLocal, "null", screenId)
|
||||
query = `DELETE FROM screenupdate WHERE screenid = ?`
|
||||
tx.Exec(query, screenId)
|
||||
insertScreenUpdate(tx, screenId, UpdateType_ScreenDel)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -2389,7 +2409,16 @@ func isWebShare(tx *TxWrap, screenId string) bool {
|
||||
return tx.Exists(`SELECT screenid FROM screen WHERE screenid = ? AND sharemode = ?`, screenId, ShareModeWeb)
|
||||
}
|
||||
|
||||
func insertScreenUpdate(tx *TxWrap, screenId string, lineId string, updateType string) {
|
||||
func insertScreenUpdate(tx *TxWrap, screenId string, updateType string) {
|
||||
if screenId == "" {
|
||||
tx.SetErr(errors.New("invalid screen-update, screenid is empty"))
|
||||
return
|
||||
}
|
||||
query := `INSERT INTO screenupdate (screenid, lineid, updatetype, updatets) VALUES (?, ?, ?, ?)`
|
||||
tx.Exec(query, screenId, "", updateType, time.Now().UnixMilli())
|
||||
}
|
||||
|
||||
func insertScreenLineUpdate(tx *TxWrap, screenId string, lineId string, updateType string) {
|
||||
if screenId == "" {
|
||||
tx.SetErr(errors.New("invalid screen-update, screenid is empty"))
|
||||
return
|
||||
@@ -2402,7 +2431,7 @@ func insertScreenUpdate(tx *TxWrap, screenId string, lineId string, updateType s
|
||||
tx.Exec(query, screenId, lineId, updateType, time.Now().UnixMilli())
|
||||
}
|
||||
|
||||
func insertScreenUpdateByCmdId(tx *TxWrap, screenId string, cmdId string, updateType string) {
|
||||
func insertScreenCmdUpdate(tx *TxWrap, screenId string, cmdId string, updateType string) {
|
||||
if screenId == "" {
|
||||
tx.SetErr(errors.New("invalid screen-update, screenid is empty"))
|
||||
return
|
||||
@@ -2414,6 +2443,51 @@ func insertScreenUpdateByCmdId(tx *TxWrap, screenId string, cmdId string, update
|
||||
query := `SELECT lineid FROM line WHERE screenid = ? AND cmdid = ?`
|
||||
lineId := tx.GetString(query, screenId, cmdId)
|
||||
if lineId != "" {
|
||||
insertScreenUpdate(tx, screenId, lineId, updateType)
|
||||
insertScreenLineUpdate(tx, screenId, lineId, updateType)
|
||||
}
|
||||
}
|
||||
|
||||
func RemoveScreenUpdate(ctx context.Context, updateId string) error {
|
||||
return WithTx(ctx, func(tx *TxWrap) error {
|
||||
query := `DELETE FROM screenupdate WHERE updateid = ?`
|
||||
tx.Exec(query, updateId)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func InsertPtyPosUpdate(ctx context.Context, screenId string, cmdId string) error {
|
||||
return WithTx(ctx, func(tx *TxWrap) error {
|
||||
query := `SELECT lineid FROM line WHERE screenid = ? AND cmdid = ?`
|
||||
lineId := tx.GetString(query, screenId, cmdId)
|
||||
if lineId == "" {
|
||||
return fmt.Errorf("invalid ptypos update, no lineid found for %s/%s", screenId, cmdId)
|
||||
}
|
||||
query = `SELECT updateid FROM screenupdate WHERE screenid = ? AND lineid = ? AND updatetype = ?`
|
||||
if !tx.Exists(query, screenId, lineId, UpdateType_PtyPos) {
|
||||
insertScreenLineUpdate(tx, screenId, lineId, UpdateType_PtyPos)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func GetWebPtyPos(ctx context.Context, screenId string, lineId string) (int64, error) {
|
||||
return WithTxRtn(ctx, func(tx *TxWrap) (int64, error) {
|
||||
query := `SELECT ptypos FROM webptypos WHERE screenid = ? AND lineid = ?`
|
||||
ptyPos := tx.GetInt(query, screenId, lineId)
|
||||
return int64(ptyPos), nil
|
||||
})
|
||||
}
|
||||
|
||||
func SetWebPtyPos(ctx context.Context, screenId string, lineId string, ptyPos int64) error {
|
||||
return WithTx(ctx, func(tx *TxWrap) error {
|
||||
query := `SELECT screenid FROM webptypos WHERE screenid = ? AND lineid = ?`
|
||||
if tx.Exists(query, screenId, lineId) {
|
||||
query = `UPDATE webptypos SET ptypos = ? WHERE screenid = ? AND lineid = ?`
|
||||
tx.Exec(query, ptyPos, screenId, lineId)
|
||||
} else {
|
||||
query = `INSERT INTO webptypos (screenid, lineid, ptypos) VALUES (?, ?, ?)`
|
||||
tx.Exec(query, screenId, lineId, ptyPos)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
+21
-1
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
@@ -60,10 +61,15 @@ func AppendToCmdPtyBlob(ctx context.Context, screenId string, cmdId string, data
|
||||
PtyData64: data64,
|
||||
PtyDataLen: int64(len(data)),
|
||||
}
|
||||
err = InsertPtyPosUpdate(ctx, screenId, cmdId)
|
||||
if err != nil {
|
||||
// just log
|
||||
log.Printf("error inserting ptypos update %s/%s: %v\n", screenId, cmdId, err)
|
||||
}
|
||||
return update, nil
|
||||
}
|
||||
|
||||
// returns (offset, data, err)
|
||||
// returns (real-offset, data, err)
|
||||
func ReadFullPtyOutFile(ctx context.Context, screenId string, cmdId string) (int64, []byte, error) {
|
||||
ptyOutFileName, err := scbase.PtyOutFile(screenId, cmdId)
|
||||
if err != nil {
|
||||
@@ -77,6 +83,20 @@ func ReadFullPtyOutFile(ctx context.Context, screenId string, cmdId string) (int
|
||||
return f.ReadAll(ctx)
|
||||
}
|
||||
|
||||
// returns (real-offset, data, err)
|
||||
func ReadPtyOutFile(ctx context.Context, screenId string, cmdId string, offset int64, maxSize int64) (int64, []byte, error) {
|
||||
ptyOutFileName, err := scbase.PtyOutFile(screenId, cmdId)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
f, err := cirfile.OpenCirFile(ptyOutFileName)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
return f.ReadAtWithMax(ctx, offset, maxSize)
|
||||
}
|
||||
|
||||
type SessionDiskSizeType struct {
|
||||
NumFiles int
|
||||
TotalSize int64
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"github.com/golang-migrate/migrate/v4"
|
||||
)
|
||||
|
||||
const MaxMigration = 15
|
||||
const MaxMigration = 16
|
||||
const MigratePrimaryScreenVersion = 9
|
||||
|
||||
func MakeMigrate() (*migrate.Migrate, error) {
|
||||
|
||||
+16
-55
@@ -80,16 +80,17 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
UpdateType_ScreenName = "screen:sharename"
|
||||
UpdateType_ScreenCurRemote = "screen:curremote"
|
||||
UpdateType_LineNew = "line:new"
|
||||
UpdateType_LineDel = "line:del"
|
||||
UpdateType_LineArchived = "line:archived"
|
||||
UpdateType_LineRenderer = "line:renderer"
|
||||
UpdateType_CmdStatus = "cmd:status"
|
||||
UpdateType_CmdDoneInfo = "cmd:doneinfo"
|
||||
UpdateType_CmdRunOut = "cmd:runout"
|
||||
UpdateType_CmdRtnState = "cmd:rtnstate"
|
||||
UpdateType_ScreenNew = "screen:new"
|
||||
UpdateType_ScreenDel = "screen:del"
|
||||
UpdateType_ScreenName = "screen:sharename"
|
||||
UpdateType_LineNew = "line:new"
|
||||
UpdateType_LineDel = "line:del"
|
||||
UpdateType_LineArchived = "line:archived"
|
||||
UpdateType_LineRenderer = "line:renderer"
|
||||
UpdateType_CmdStatus = "cmd:status"
|
||||
UpdateType_CmdDoneInfo = "cmd:doneinfo"
|
||||
UpdateType_CmdRtnState = "cmd:rtnstate"
|
||||
UpdateType_PtyPos = "pty:pos"
|
||||
)
|
||||
|
||||
const MaxTzNameLen = 50
|
||||
@@ -144,6 +145,11 @@ func CloseDB() {
|
||||
globalDB = nil
|
||||
}
|
||||
|
||||
type CmdPtr struct {
|
||||
ScreenId string
|
||||
CmdId string
|
||||
}
|
||||
|
||||
type ClientWinSizeType struct {
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
@@ -228,11 +234,6 @@ type ClientData struct {
|
||||
|
||||
func (ClientData) UseDBMap() {}
|
||||
|
||||
type CloudAclType struct {
|
||||
UserId string `json:"userid"`
|
||||
Role string `json:"role"`
|
||||
}
|
||||
|
||||
type SessionType struct {
|
||||
SessionId string `json:"sessionid"`
|
||||
Name string `json:"name"`
|
||||
@@ -249,46 +250,6 @@ type SessionType struct {
|
||||
Full bool `json:"full,omitempty"`
|
||||
}
|
||||
|
||||
type CloudSessionType struct {
|
||||
SessionId string
|
||||
ViewKey string
|
||||
WriteKey string
|
||||
EncKey string
|
||||
EncType string
|
||||
Vts int64
|
||||
Acl []*CloudAclType
|
||||
}
|
||||
|
||||
func (cs *CloudSessionType) ToMap() map[string]any {
|
||||
m := make(map[string]any)
|
||||
m["sessionid"] = cs.SessionId
|
||||
m["viewkey"] = cs.ViewKey
|
||||
m["writekey"] = cs.WriteKey
|
||||
m["enckey"] = cs.EncKey
|
||||
m["enctype"] = cs.EncType
|
||||
m["vts"] = cs.Vts
|
||||
m["acl"] = quickJsonArr(cs.Acl)
|
||||
return m
|
||||
}
|
||||
|
||||
func (cs *CloudSessionType) FromMap(m map[string]interface{}) bool {
|
||||
quickSetStr(&cs.SessionId, m, "sessionid")
|
||||
quickSetStr(&cs.ViewKey, m, "viewkey")
|
||||
quickSetStr(&cs.WriteKey, m, "writekey")
|
||||
quickSetStr(&cs.EncKey, m, "enckey")
|
||||
quickSetStr(&cs.EncType, m, "enctype")
|
||||
quickSetInt64(&cs.Vts, m, "vts")
|
||||
quickSetJsonArr(&cs.Acl, m, "acl")
|
||||
return true
|
||||
}
|
||||
|
||||
type CloudUpdate struct {
|
||||
UpdateId string
|
||||
Ts int64
|
||||
UpdateType string
|
||||
UpdateKeys []string
|
||||
}
|
||||
|
||||
type SessionStatsType struct {
|
||||
SessionId string `json:"sessionid"`
|
||||
NumScreens int `json:"numscreens"`
|
||||
|
||||
Reference in New Issue
Block a user