webshare selected line, sync webupdate for screen:new, fix line resolver for hidden items

This commit is contained in:
sawka 2023-03-30 18:08:35 -07:00
parent 652c820844
commit 274697039a
6 changed files with 151 additions and 42 deletions

View File

@ -7,6 +7,7 @@ import (
"encoding/base64"
"fmt"
"log"
"net/url"
"os"
"regexp"
"sort"
@ -1641,6 +1642,10 @@ func SessionOpenCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (
return update, nil
}
func makeExternLink(urlStr string) string {
return fmt.Sprintf(`https://extern?%s`, url.QueryEscape(urlStr))
}
func ScreenWebShareCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
ids, err := resolveUiIds(ctx, pk, R_Screen)
if err != nil {
@ -1655,6 +1660,7 @@ func ScreenWebShareCommand(ctx context.Context, pk *scpacket.FeCommandPacketType
return nil, err
}
var infoMsg string
var infoWebShareLink bool
if shouldShare {
viewKeyBytes := make([]byte, 9)
_, err = rand.Read(viewKeyBytes)
@ -1663,11 +1669,24 @@ func ScreenWebShareCommand(ctx context.Context, pk *scpacket.FeCommandPacketType
}
viewKey := base64.RawURLEncoding.EncodeToString(viewKeyBytes)
webShareOpts := sstore.ScreenWebShareOpts{ShareName: shareName, ViewKey: viewKey}
screen, err := sstore.GetScreenById(ctx, ids.ScreenId)
if err != nil {
return nil, fmt.Errorf("cannot get screen: %v", err)
}
err = sstore.CanScreenWebShare(screen)
if err != nil {
return nil, err
}
webUpdate := pcloud.MakeScreenNewUpdate(screen, webShareOpts)
err = pcloud.DoSyncWebUpdate(webUpdate)
if err != nil {
return nil, fmt.Errorf("error starting webshare, error contacting prompt cloud server: %v", err)
}
err = sstore.ScreenWebShareStart(ctx, ids.ScreenId, webShareOpts)
if err != nil {
return nil, fmt.Errorf("cannot web-share screen: %v", err)
return nil, fmt.Errorf("cannot webshare screen, error updating: %v", err)
}
infoMsg = fmt.Sprintf("screen is now shared to the web at %s", GetWebShareUrl(ids.ScreenId, viewKey))
infoWebShareLink = true
} else {
err = sstore.ScreenWebShareStop(ctx, ids.ScreenId)
if err != nil {
@ -1683,6 +1702,7 @@ func ScreenWebShareCommand(ctx context.Context, pk *scpacket.FeCommandPacketType
Screens: []*sstore.ScreenType{screen},
Info: &sstore.InfoMsgType{
InfoMsg: infoMsg,
WebShareLink: infoWebShareLink,
},
}
return update, nil

View File

@ -186,6 +186,22 @@ func defaultError(err error, estr string) error {
return errors.New(estr)
}
func MakeScreenNewUpdate(screen *sstore.ScreenType, webShareOpts sstore.ScreenWebShareOpts) *WebShareUpdateType {
rtn := &WebShareUpdateType{
ScreenId: screen.ScreenId,
UpdateId: -1,
UpdateType: sstore.UpdateType_ScreenNew,
UpdateTs: time.Now().UnixMilli(),
}
rtn.Screen = &WebShareScreenType{
ScreenId: screen.ScreenId,
SelectedLine: int(screen.SelectedLine),
ShareName: webShareOpts.ShareName,
ViewKey: webShareOpts.ViewKey,
}
return rtn
}
func makeWebShareUpdate(ctx context.Context, update *sstore.ScreenUpdateType) (*WebShareUpdateType, error) {
rtn := &WebShareUpdateType{
ScreenId: update.ScreenId,
@ -208,15 +224,19 @@ func makeWebShareUpdate(ctx context.Context, update *sstore.ScreenUpdateType) (*
case sstore.UpdateType_ScreenDel:
break
case sstore.UpdateType_ScreenName:
case sstore.UpdateType_ScreenName, sstore.UpdateType_ScreenSelectedLine:
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)")
if screen == nil || screen.WebShareOpts == nil {
return nil, fmt.Errorf("invalid screen, not webshared (makeWebScreenUpdate)")
}
if update.UpdateType == sstore.UpdateType_ScreenName {
rtn.SVal = screen.WebShareOpts.ShareName
} else if update.UpdateType == sstore.UpdateType_ScreenSelectedLine {
rtn.IVal = int64(screen.SelectedLine)
}
case sstore.UpdateType_LineNew:
line, cmd, err := sstore.GetLineCmdByLineId(ctx, update.ScreenId, update.LineId)
@ -332,7 +352,7 @@ type webShareResponseType struct {
Data []*WebShareUpdateResponseType `json:"data"`
}
func DoWebScreenUpdates(authInfo AuthInfo, updateArr []*sstore.ScreenUpdateType) error {
func convertUpdates(updateArr []*sstore.ScreenUpdateType) []*WebShareUpdateType {
var webUpdates []*WebShareUpdateType
for _, update := range updateArr {
webUpdate, err := makeWebShareUpdate(context.Background(), update)
@ -352,9 +372,43 @@ func DoWebScreenUpdates(authInfo AuthInfo, updateArr []*sstore.ScreenUpdateType)
}
webUpdates = append(webUpdates, webUpdate)
}
return webUpdates
}
func DoSyncWebUpdate(webUpdate *WebShareUpdateType) error {
authInfo, err := getAuthInfo(context.Background())
if err != nil {
return fmt.Errorf("could not get authinfo for request: %v", err)
}
ctx, cancelFn := context.WithTimeout(context.Background(), PCloudDefaultTimeout)
defer cancelFn()
req, err := makeAuthPostReq(ctx, WebShareUpdateUrl, authInfo, []*WebShareUpdateType{webUpdate})
if err != nil {
return fmt.Errorf("cannot create auth-post-req for %s: %v", WebShareUpdateUrl, err)
}
var resp webShareResponseType
_, err = doRequest(req, &resp)
if err != nil {
return err
}
if len(resp.Data) == 0 {
return fmt.Errorf("invalid response received from server")
}
urt := resp.Data[0]
if urt.Error != "" {
return errors.New(urt.Error)
}
return nil
}
func DoWebUpdates(webUpdates []*WebShareUpdateType) error {
if len(webUpdates) == 0 {
return nil
}
authInfo, err := getAuthInfo(context.Background())
if err != nil {
return fmt.Errorf("could not get authinfo for request: %v", err)
}
ctx, cancelFn := context.WithTimeout(context.Background(), PCloudDefaultTimeout)
defer cancelFn()
req, err := makeAuthPostReq(ctx, WebShareUpdateUrl, authInfo, webUpdates)
@ -377,7 +431,9 @@ func DoWebScreenUpdates(authInfo AuthInfo, updateArr []*sstore.ScreenUpdateType)
if resp == nil {
resp = &WebShareUpdateResponseType{Success: false, Error: "resp not found"}
}
log.Printf("[pcloud] updateid:%d, type:%s %s/%s success:%v err:%v\n", update.UpdateId, update.UpdateType, update.ScreenId, update.LineId, resp.Success, resp.Error)
if resp.Error != "" {
log.Printf("[pcloud] error updateid:%d, type:%s %s/%s err:%v\n", update.UpdateId, update.UpdateType, update.ScreenId, update.LineId, resp.Error)
}
}
return nil
}
@ -447,8 +503,8 @@ func runWebShareUpdateWriter() {
continue
}
numErrors = 0
authInfo, err := getAuthInfo(context.Background())
err = DoWebScreenUpdates(authInfo, updateArr)
webUpdates := convertUpdates(updateArr)
err = DoWebUpdates(webUpdates)
if err != nil {
numSendErrors++
backoffTime := computeBackoff(numSendErrors)

View File

@ -34,6 +34,7 @@ type WebShareUpdateType struct {
Cmd *WebShareCmdType `json:"cmd,omitempty"`
PtyData *WebSharePtyData `json:"ptydata,omitempty"`
SVal string `json:"sval,omitempty"`
IVal int64 `json:"ival,omitempty"`
BVal bool `json:"bval,omitempty"`
DoneInfo *sstore.CmdDoneInfo `json:"doneinfo,omitempty"`
TermOpts *sstore.TermOpts `json:"termopts,omitempty"`
@ -62,6 +63,7 @@ type WebShareScreenType struct {
ScreenId string `json:"screenid"`
ShareName string `json:"sharename"`
ViewKey string `json:"viewkey"`
SelectedLine int `json:"selectedline"`
}
func webRemoteFromRemote(rptr sstore.RemotePtrType, r *sstore.RemoteType) *WebShareRemote {
@ -91,7 +93,7 @@ func webScreenFromScreen(s *sstore.ScreenType) (*WebShareScreenType, error) {
} else {
shareName = s.Name
}
return &WebShareScreenType{ScreenId: s.ScreenId, ShareName: shareName, ViewKey: s.WebShareOpts.ViewKey}, nil
return &WebShareScreenType{ScreenId: s.ScreenId, ShareName: shareName, ViewKey: s.WebShareOpts.ViewKey, SelectedLine: int(s.SelectedLine)}, nil
}
type WebShareLineType struct {

View File

@ -1114,6 +1114,7 @@ func PurgeScreen(ctx context.Context, screenId string, sessionDel bool) (UpdateP
tx.Exec(query, screenId)
query = `DELETE FROM line WHERE screenid = ?`
tx.Exec(query, screenId)
insertScreenDelUpdate(tx, screenId)
return nil
})
if txErr != nil {
@ -1715,6 +1716,9 @@ func UpdateScreen(ctx context.Context, screenId string, editMap map[string]inter
if sline, found := editMap[ScreenField_SelectedLine]; found {
query = `UPDATE screen SET selectedline = ? WHERE screenid = ?`
tx.Exec(query, sline, screenId)
if isWebShare(tx, screenId) {
insertScreenUpdate(tx, screenId, UpdateType_ScreenSelectedLine)
}
}
if focusType, found := editMap[ScreenField_Focus]; found {
query = `UPDATE screen SET focustype = ? WHERE screenid = ?`
@ -1743,7 +1747,7 @@ func UpdateScreen(ctx context.Context, screenId string, editMap map[string]inter
func GetLineResolveItems(ctx context.Context, screenId string) ([]ResolveItem, error) {
var rtn []ResolveItem
txErr := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT lineid as id, linenum as num FROM line WHERE screenid = ? ORDER BY linenum`
query := `SELECT lineid as id, linenum as num, archived as hidden FROM line WHERE screenid = ? ORDER BY linenum`
tx.Select(&rtn, query, screenId)
return nil
})
@ -2411,6 +2415,19 @@ func PurgeHistoryByIds(ctx context.Context, historyIds []string) ([]*HistoryItem
})
}
func CanScreenWebShare(screen *ScreenType) error {
if screen == nil {
return fmt.Errorf("cannot share screen, not found")
}
if screen.ShareMode == ShareModeWeb {
return fmt.Errorf("screen is already shared to web")
}
if screen.ShareMode != ShareModeLocal {
return fmt.Errorf("screen cannot be shared, invalid current share mode %q (must be local)", screen.ShareMode)
}
return nil
}
func ScreenWebShareStart(ctx context.Context, screenId string, shareOpts ScreenWebShareOpts) error {
return WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT screenid FROM screen WHERE screenid = ?`
@ -2424,17 +2441,9 @@ func ScreenWebShareStart(ctx context.Context, screenId string, shareOpts ScreenW
if shareMode != ShareModeLocal {
return fmt.Errorf("screen cannot be shared, invalid current share mode %q (must be local)", shareMode)
}
nowTs := time.Now().UnixMilli()
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 = ? AND NOT archived ORDER BY linenum`
tx.Exec(query, UpdateType_LineNew, nowTs, screenId)
query = `INSERT INTO screenupdate (screenid, lineid, updatetype, updatets)
SELECT c.screenid, l.lineid, ?, ? FROM cmd c, line l WHERE c.screenid = ? AND l.cmdid = c.cmdid AND NOT l.archived`
tx.Exec(query, UpdateType_PtyPos, nowTs, screenId)
NotifyUpdateWriter()
insertScreenNewUpdate(tx, screenId)
return nil
})
}
@ -2451,11 +2460,7 @@ func ScreenWebShareStop(ctx context.Context, screenId string) error {
}
query = `UPDATE screen SET sharemode = ?, webshareopts = ? WHERE screenid = ?`
tx.Exec(query, ShareModeLocal, "null", screenId)
query = `DELETE FROM screenupdate WHERE screenid = ?`
tx.Exec(query, screenId)
query = `DELETE FROM webptypos WHERE screenid = ?`
tx.Exec(query, screenId)
insertScreenUpdate(tx, screenId, UpdateType_ScreenDel)
insertScreenDelUpdate(tx, screenId)
return nil
})
}
@ -2469,11 +2474,31 @@ func insertScreenUpdate(tx *TxWrap, screenId string, updateType string) {
tx.SetErr(errors.New("invalid screen-update, screenid is empty"))
return
}
nowTs := time.Now().UnixMilli()
query := `INSERT INTO screenupdate (screenid, lineid, updatetype, updatets) VALUES (?, ?, ?, ?)`
tx.Exec(query, screenId, "", updateType, time.Now().UnixMilli())
tx.Exec(query, screenId, "", updateType, nowTs)
NotifyUpdateWriter()
}
func insertScreenNewUpdate(tx *TxWrap, screenId string) {
nowTs := time.Now().UnixMilli()
query := `INSERT INTO screenupdate (screenid, lineid, updatetype, updatets)
SELECT screenid, lineid, ?, ? FROM line WHERE screenid = ? AND NOT archived ORDER BY linenum`
tx.Exec(query, UpdateType_LineNew, nowTs, screenId)
query = `INSERT INTO screenupdate (screenid, lineid, updatetype, updatets)
SELECT c.screenid, l.lineid, ?, ? FROM cmd c, line l WHERE c.screenid = ? AND l.cmdid = c.cmdid AND NOT l.archived`
tx.Exec(query, UpdateType_PtyPos, nowTs, screenId)
NotifyUpdateWriter()
}
func insertScreenDelUpdate(tx *TxWrap, screenId string) {
query := `DELETE FROM screenupdate WHERE screenid = ?`
tx.Exec(query, screenId)
query = `DELETE FROM webptypos WHERE screenid = ?`
tx.Exec(query, screenId)
insertScreenUpdate(tx, screenId, UpdateType_ScreenDel)
}
func insertScreenLineUpdate(tx *TxWrap, screenId string, lineId string, updateType string) {
if screenId == "" {
tx.SetErr(errors.New("invalid screen-update, screenid is empty"))
@ -2521,6 +2546,9 @@ func GetScreenUpdates(ctx context.Context, maxNum int) ([]*ScreenUpdateType, err
}
func RemoveScreenUpdate(ctx context.Context, updateId int64) error {
if updateId < 0 {
return nil // in-memory updates (not from DB)
}
return WithTx(ctx, func(tx *TxWrap) error {
query := `DELETE FROM screenupdate WHERE updateid = ?`
tx.Exec(query, updateId)

View File

@ -83,6 +83,7 @@ const (
const (
UpdateType_ScreenNew = "screen:new"
UpdateType_ScreenDel = "screen:del"
UpdateType_ScreenSelectedLine = "screen:selectedline"
UpdateType_ScreenName = "screen:sharename"
UpdateType_LineNew = "line:new"
UpdateType_LineDel = "line:del"

View File

@ -100,6 +100,8 @@ type InfoMsgType struct {
InfoTitle string `json:"infotitle"`
InfoError string `json:"infoerror,omitempty"`
InfoMsg string `json:"infomsg,omitempty"`
InfoMsgHtml bool `json:"infomsghtml,omitempty"`
WebShareLink bool `json:"websharelink,omitempty"`
InfoComps []string `json:"infocomps,omitempty"`
InfoCompsMore bool `json:"infocompssmore,omitempty"`
InfoLines []string `json:"infolines,omitempty"`