diff --git a/db/migrations/000014_simplifybookmarks.down.sql b/db/migrations/000014_simplifybookmarks.down.sql new file mode 100644 index 00000000..13119576 --- /dev/null +++ b/db/migrations/000014_simplifybookmarks.down.sql @@ -0,0 +1,9 @@ +CREATE TABLE IF NOT EXISTS "bookmark_cmd" ( + bookmarkid varchar(36) NOT NULL, + screenid varchar(36) NOT NULL, + cmdid varchar(36) NOT NULL, + PRIMARY KEY (bookmarkid, screenid, cmdid) +); + +ALTER TABLE line ADD COLUMN bookmarked boolean NOT NULL DEFAULT 0; + diff --git a/db/migrations/000014_simplifybookmarks.up.sql b/db/migrations/000014_simplifybookmarks.up.sql new file mode 100644 index 00000000..fa2e43eb --- /dev/null +++ b/db/migrations/000014_simplifybookmarks.up.sql @@ -0,0 +1,3 @@ +DROP TABLE bookmark_cmd; +ALTER TABLE line DROP COLUMN bookmarked; + diff --git a/db/migrations/000015_lineupdates.down.sql b/db/migrations/000015_lineupdates.down.sql new file mode 100644 index 00000000..2a4c19c8 --- /dev/null +++ b/db/migrations/000015_lineupdates.down.sql @@ -0,0 +1,4 @@ +DROP TABLE screenupdates; + +ALTER TABLE screen DROP COLUMN webshareopts; + diff --git a/db/migrations/000015_lineupdates.up.sql b/db/migrations/000015_lineupdates.up.sql new file mode 100644 index 00000000..619d55db --- /dev/null +++ b/db/migrations/000015_lineupdates.up.sql @@ -0,0 +1,10 @@ +CREATE TABLE screenupdates ( + updateid integer PRIMARY KEY, + screenid varchar(36) NOT NULL, + lineid varchar(36) NOT NULL, + updatetype varchar(50) NOT NULL, + updatets bigint NOT NULL +); + +ALTER TABLE screen ADD COLUMN webshareopts json NOT NULL DEFAULT 'null'; + diff --git a/db/schema.sql b/db/schema.sql index 9ebfb32d..0b9ba237 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -128,12 +128,6 @@ CREATE TABLE cloud_update ( updatetype varchar(50) NOT NULL, updatekeys json NOT NULL ); -CREATE TABLE IF NOT EXISTS "bookmark_cmd" ( - bookmarkid varchar(36) NOT NULL, - screenid varchar(36) NOT NULL, - cmdid varchar(36) NOT NULL, - PRIMARY KEY (bookmarkid, screenid, cmdid) -); CREATE TABLE cmd_migrate ( sessionid varchar(36) NOT NULL, screenid varchar(36) NOT NULL, @@ -155,7 +149,7 @@ CREATE TABLE IF NOT EXISTS "screen" ( anchor json NOT NULL, focustype varchar(12) NOT NULL, archived boolean NOT NULL, - archivedts bigint NOT NULL, + archivedts bigint NOT NULL, webshareopts json NOT NULL DEFAULT 'null', PRIMARY KEY (screenid) ); CREATE TABLE IF NOT EXISTS "line" ( @@ -174,7 +168,6 @@ CREATE TABLE IF NOT EXISTS "line" ( star int NOT NULL, archived boolean NOT NULL, renderer varchar(50) NOT NULL, - bookmarked boolean NOT NULL, PRIMARY KEY (screenid, lineid) ); CREATE TABLE IF NOT EXISTS "cmd" ( @@ -199,3 +192,10 @@ CREATE TABLE IF NOT EXISTS "cmd" ( rtndiffhasharr json NOT NULL, PRIMARY KEY (screenid, cmdid) ); +CREATE TABLE screenupdates ( + updateid integer PRIMARY KEY, + screenid varchar(36) NOT NULL, + lineid varchar(36) NOT NULL, + updatetype varchar(50) NOT NULL, + updatets bigint NOT NULL +); diff --git a/pkg/cmdrunner/cmdrunner.go b/pkg/cmdrunner/cmdrunner.go index 16eaeb56..7bbbe3ff 100644 --- a/pkg/cmdrunner/cmdrunner.go +++ b/pkg/cmdrunner/cmdrunner.go @@ -3,6 +3,8 @@ package cmdrunner import ( "bytes" "context" + "crypto/rand" + "encoding/base64" "fmt" "log" "os" @@ -12,6 +14,7 @@ import ( "strings" "syscall" "time" + "unicode" "github.com/alessio/shellescape" "github.com/google/uuid" @@ -40,6 +43,7 @@ func init() { const DefaultUserId = "sawka" const MaxNameLen = 50 +const MaxShareNameLen = 150 const MaxRendererLen = 50 const MaxRemoteAliasLen = 50 const PasswordUnchangedSentinel = "--unchanged--" @@ -131,7 +135,6 @@ func init() { registerCmdFn("session:showall", SessionShowAllCommand) registerCmdFn("session:show", SessionShowCommand) registerCmdFn("session:openshared", SessionOpenSharedCommand) - registerCmdFn("session:opencloud", SessionOpenCloudCommand) registerCmdFn("screen", ScreenCommand) registerCmdFn("screen:archive", ScreenArchiveCommand) @@ -141,6 +144,7 @@ func init() { registerCmdFn("screen:set", ScreenSetCommand) registerCmdFn("screen:showall", ScreenShowAllCommand) registerCmdFn("screen:reset", ScreenResetCommand) + registerCmdFn("screen:webshare", ScreenWebShareCommand) registerCmdAlias("remote", RemoteCommand) registerCmdFn("remote:show", RemoteShowCommand) @@ -1561,6 +1565,18 @@ func validateName(name string, typeStr string) error { return nil } +func validateShareName(name string) error { + if len(name) > MaxShareNameLen { + return fmt.Errorf("share name too long, max length is %d", MaxShareNameLen) + } + for _, ch := range name { + if !unicode.IsPrint(ch) { + return fmt.Errorf("invalid character %q in share name", string(ch)) + } + } + return nil +} + func validateRenderer(renderer string) error { if renderer == "" { return nil @@ -1601,22 +1617,6 @@ func SessionOpenSharedCommand(ctx context.Context, pk *scpacket.FeCommandPacketT return nil, fmt.Errorf("shared sessions are not available in this version of prompt (stay tuned)") } -func SessionOpenCloudCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { - activate := resolveBool(pk.Kwargs["activate"], true) - newName := pk.Kwargs["name"] - if newName != "" { - err := validateName(newName, "session") - if err != nil { - return nil, err - } - } - update, err := sstore.InsertSessionWithName(ctx, newName, sstore.ShareModeShared, activate) - if err != nil { - return nil, err - } - return update, nil -} - func SessionOpenCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { activate := resolveBool(pk.Kwargs["activate"], true) newName := pk.Kwargs["name"] @@ -1633,6 +1633,53 @@ func SessionOpenCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) ( return update, nil } +func ScreenWebShareCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { + ids, err := resolveUiIds(ctx, pk, R_Screen) + if err != nil { + return nil, err + } + shouldShare := true + if len(pk.Args) > 0 { + shouldShare = resolveBool(pk.Args[0], true) + } + shareName := pk.Kwargs["sharename"] + if err := validateShareName(shareName); err != nil { + return nil, err + } + var infoMsg string + if shouldShare { + viewKeyBytes := make([]byte, 9) + _, err = rand.Read(viewKeyBytes) + if err != nil { + return nil, fmt.Errorf("cannot create viewkey: %v", err) + } + webShareOpts := sstore.ScreenWebShareOpts{ShareName: shareName, ViewKey: base64.RawURLEncoding.EncodeToString(viewKeyBytes)} + err = sstore.ScreenWebShareStart(ctx, ids.ScreenId, webShareOpts) + if err != nil { + return nil, fmt.Errorf("cannot web-share screen: %v", err) + } + pcloud.NotifyUpdateWriter() + infoMsg = fmt.Sprintf("screen is now shared to the web at %s", "[screen-share-url]") + } else { + err = sstore.ScreenWebShareStop(ctx, ids.ScreenId) + if err != nil { + return nil, fmt.Errorf("cannot stop web-sharing screen: %v", err) + } + infoMsg = fmt.Sprintf("screen is no longer web shared") + } + screen, err := sstore.GetScreenById(ctx, ids.ScreenId) + if err != nil { + return nil, fmt.Errorf("cannot get updated screen: %v", err) + } + update := sstore.ModelUpdate{ + Screens: []*sstore.ScreenType{screen}, + Info: &sstore.InfoMsgType{ + InfoMsg: infoMsg, + }, + } + return update, nil +} + func SessionDeleteCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { ids, err := resolveUiIds(ctx, pk, R_Session) if err != nil { @@ -2339,36 +2386,42 @@ func LineBookmarkCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) if lineId == "" { return nil, fmt.Errorf("line %q not found", lineArg) } - lineObj, cmdObj, err := sstore.GetLineCmdByLineId(ctx, ids.ScreenId, lineId) + _, cmdObj, err := sstore.GetLineCmdByLineId(ctx, ids.ScreenId, lineId) if err != nil { return nil, fmt.Errorf("/line:bookmark error getting line: %v", err) } if cmdObj == nil { return nil, fmt.Errorf("cannot bookmark non-cmd line") } - ck := base.MakeCommandKey(lineObj.ScreenId, cmdObj.CmdId) - bm := &sstore.BookmarkType{ - BookmarkId: uuid.New().String(), - CreatedTs: time.Now().UnixMilli(), - CmdStr: cmdObj.CmdStr, - Alias: "", - Tags: nil, - Description: "", - Cmds: []base.CommandKey{ck}, - } - err = sstore.InsertBookmark(ctx, bm) + existingBmIds, err := sstore.GetBookmarkIdsByCmdStr(ctx, cmdObj.CmdStr) if err != nil { - return nil, fmt.Errorf("cannot insert bookmark: %v", err) + return nil, fmt.Errorf("error trying to retrieve current boookmarks: %v", err) } - newLineObj, err := sstore.GetLineById(ctx, ids.ScreenId, lineId) - if err != nil { - return nil, fmt.Errorf("/line:bookmark error getting line: %v", err) + var newBmId string + if len(existingBmIds) > 0 { + newBmId = existingBmIds[0] + } else { + newBm := &sstore.BookmarkType{ + BookmarkId: uuid.New().String(), + CreatedTs: time.Now().UnixMilli(), + CmdStr: cmdObj.CmdStr, + Alias: "", + Tags: nil, + Description: "", + } + err = sstore.InsertBookmark(ctx, newBm) + if err != nil { + return nil, fmt.Errorf("cannot insert bookmark: %v", err) + } + newBmId = newBm.BookmarkId } - if newLineObj == nil { - // no line (which is strange given we checked for it above). just return a nop. - return nil, nil + bms, err := sstore.GetBookmarks(ctx, "") + update := sstore.ModelUpdate{ + MainView: sstore.MainViewBookmarks, + Bookmarks: bms, + SelectedBookmark: newBmId, } - return sstore.ModelUpdate{Line: newLineObj}, nil + return update, nil } func LinePinCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { diff --git a/pkg/pcloud/pcloud.go b/pkg/pcloud/pcloud.go index 388433bd..915892c9 100644 --- a/pkg/pcloud/pcloud.go +++ b/pkg/pcloud/pcloud.go @@ -209,3 +209,6 @@ func CreateCloudSession(ctx context.Context) error { } return nil } + +func NotifyUpdateWriter() { +} diff --git a/pkg/sstore/dbops.go b/pkg/sstore/dbops.go index 64f30068..fcc509ed 100644 --- a/pkg/sstore/dbops.go +++ b/pkg/sstore/dbops.go @@ -663,7 +663,8 @@ func InsertScreen(ctx context.Context, sessionId string, origScreenName string, Archived: false, ArchivedTs: 0, } - query = `INSERT INTO screen (sessionid, screenid, name, screenidx, screenopts, ownerid, sharemode, curremoteownerid, curremoteid, curremotename, nextlinenum, selectedline, anchor, focustype, archived, archivedts) VALUES (:sessionid,:screenid,:name,:screenidx,:screenopts,:ownerid,:sharemode,:curremoteownerid,:curremoteid,:curremotename,:nextlinenum,:selectedline,:anchor,:focustype,:archived,:archivedts)` + query = `INSERT INTO screen ( sessionid, screenid, name, screenidx, screenopts, ownerid, sharemode, webshareopts, curremoteownerid, curremoteid, curremotename, nextlinenum, selectedline, anchor, focustype, archived, archivedts) + VALUES (:sessionid,:screenid,:name,:screenidx,:screenopts,:ownerid,:sharemode,:webshareopts,:curremoteownerid,:curremoteid,:curremotename,:nextlinenum,:selectedline,:anchor,:focustype,:archived,:archivedts)` tx.NamedExec(query, screen.ToMap()) if activate { query = `UPDATE session SET activescreenid = ? WHERE sessionid = ?` @@ -775,8 +776,8 @@ func InsertLine(ctx context.Context, line *LineType, cmd *CmdType) error { query = `SELECT nextlinenum FROM screen WHERE screenid = ?` nextLineNum := tx.GetInt(query, line.ScreenId) line.LineNum = int64(nextLineNum) - query = `INSERT INTO line ( screenid, userid, lineid, ts, linenum, linenumtemp, linelocal, linetype, text, cmdid, renderer, ephemeral, contentheight, star, archived, bookmarked) - VALUES (:screenid,:userid,:lineid,:ts,:linenum,:linenumtemp,:linelocal,:linetype,:text,:cmdid,:renderer,:ephemeral,:contentheight,:star,:archived,:bookmarked)` + query = `INSERT INTO line ( screenid, userid, lineid, ts, linenum, linenumtemp, linelocal, linetype, text, cmdid, renderer, ephemeral, contentheight, star, archived) + VALUES (:screenid,:userid,:lineid,:ts,:linenum,:linenumtemp,:linelocal,:linetype,:text,:cmdid,:renderer,:ephemeral,:contentheight,:star,:archived)` tx.NamedExec(query, line) query = `UPDATE screen SET nextlinenum = ? WHERE screenid = ?` tx.Exec(query, nextLineNum+1, line.ScreenId) @@ -943,8 +944,6 @@ func cleanScreenCmds(ctx context.Context, screenId string) error { removedCmds = tx.SelectStrings(query, screenId, screenId) query = `DELETE FROM cmd WHERE screenid = ? AND cmdid NOT IN (SELECT cmdid FROM line WHERE screenid = ?)` tx.Exec(query, screenId, screenId) - query = `DELETE FROM bookmark_cmd WHERE screenid = ? AND cmdid NOT IN (SELECT cmdid FROM cmd WHERE screenid = ?)` - tx.Exec(query, screenId, screenId) return nil }) if txErr != nil { @@ -2039,12 +2038,6 @@ type bookmarkOrderType struct { OrderIdx int64 } -type bookmarkCmdType struct { - BookmarkId string - ScreenId string - CmdId string -} - func GetBookmarks(ctx context.Context, tag string) ([]*BookmarkType, error) { var bms []*BookmarkType txErr := WithTx(ctx, func(tx *TxWrap) error { @@ -2066,15 +2059,6 @@ func GetBookmarks(ctx context.Context, tag string) ([]*BookmarkType, error) { bm.OrderIdx = bmOrder.OrderIdx } } - var cmds []bookmarkCmdType - query = `SELECT bookmarkid, screenid, cmdid FROM bookmark_cmd` - tx.Select(&cmds, query) - for _, cmd := range cmds { - bm := bmMap[cmd.BookmarkId] - if bm != nil { - bm.Cmds = append(bm.Cmds, base.MakeCommandKey(cmd.ScreenId, cmd.CmdId)) - } - } return nil }) if txErr != nil { @@ -2094,12 +2078,6 @@ func GetBookmarkById(ctx context.Context, bookmarkId string, tag string) (*Bookm query = `SELECT orderidx FROM bookmark_order WHERE bookmarkid = ? AND tag = ?` orderIdx := tx.GetInt(query, bookmarkId, tag) rtn.OrderIdx = int64(orderIdx) - query = `SELECT bookmarkid, screenid, cmdid FROM bookmark_cmd WHERE bookmarkid = ?` - var cmds []bookmarkCmdType - tx.Select(&cmds, query, bookmarkId) - for _, cmd := range cmds { - rtn.Cmds = append(rtn.Cmds, base.MakeCommandKey(cmd.ScreenId, cmd.CmdId)) - } return nil }) if txErr != nil { @@ -2126,6 +2104,14 @@ func GetBookmarkIdByArg(ctx context.Context, bookmarkArg string) (string, error) return rtnId, nil } +func GetBookmarkIdsByCmdStr(ctx context.Context, cmdStr string) ([]string, error) { + return WithTxRtn(ctx, func(tx *TxWrap) ([]string, error) { + query := `SELECT bookmarkid FROM bookmark WHERE cmdstr = ?` + bmIds := tx.SelectStrings(query, cmdStr) + return bmIds, nil + }) +} + // ignores OrderIdx field func InsertBookmark(ctx context.Context, bm *BookmarkType) error { if bm == nil || bm.BookmarkId == "" { @@ -2145,14 +2131,6 @@ func InsertBookmark(ctx context.Context, bm *BookmarkType) error { query = `INSERT INTO bookmark_order (tag, bookmarkid, orderidx) VALUES (?, ?, ?)` tx.Exec(query, tag, bm.BookmarkId, maxOrder+1) } - query = `INSERT INTO bookmark_cmd (bookmarkid, screenid, cmdid) VALUES (?, ?, ?)` - for _, ck := range bm.Cmds { - tx.Exec(query, bm.BookmarkId, ck.GetGroupId(), ck.GetCmdId()) - } - query = `UPDATE line SET bookmarked = 1 WHERE screenid = ? AND cmdid = ?` - for _, ck := range bm.Cmds { - tx.Exec(query, ck.GetGroupId(), ck.GetCmdId()) - } return nil }) return txErr @@ -2205,10 +2183,6 @@ func DeleteBookmark(ctx context.Context, bookmarkId string) error { tx.Exec(query, bookmarkId) query = `DELETE FROM bookmark_order WHERE bookmarkid = ?` tx.Exec(query, bookmarkId) - query = `UPDATE line SET bookmarked = 0 WHERE bookmarked AND cmdid <> '' AND (screenid||cmdid) IN (SELECT screenid||cmdid FROM bookmark_cmd WHERE bookmarkid = ?) ` - tx.Exec(query, bookmarkId) - query = `DELETE FROM bookmark_cmd WHERE bookmarkid = ?` - tx.Exec(query, bookmarkId) fixupBookmarkOrder(tx) return nil }) @@ -2343,3 +2317,47 @@ func PurgeHistoryByIds(ctx context.Context, historyIds []string) ([]*HistoryItem return rtn, 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 = ?` + if !tx.Exists(query, screenId) { + return fmt.Errorf("screen does not exist") + } + shareMode := tx.GetString(`SELECT sharemode FROM screen WHERE screenid = ?`, screenId) + if shareMode == ShareModeWeb { + return fmt.Errorf("screen is already shared to web") + } + if shareMode != ShareModeLocal { + return fmt.Errorf("screen cannot be shared, invalid current share mode %q (must be local)", shareMode) + } + query = `UPDATE screen SET sharemode = ?, webshareopts = ? WHERE screenid = ?` + tx.Exec(query, ShareModeWeb, quickJson(shareOpts), screenId) + query = `INSERT INTO screenupdates (screenid, lineid, updatetype, updatets) + SELECT screenid, lineid, ?, ? FROM line WHERE screenid = ? ORDER BY linenum` + tx.Exec(query, UpdateType_LineNew, time.Now().UnixMilli(), screenId) + return nil + }) +} + +func ScreenWebShareStop(ctx context.Context, screenId string) error { + return WithTx(ctx, func(tx *TxWrap) error { + query := `SELECT screenid FROM screen WHERE screenid = ?` + if !tx.Exists(query, screenId) { + return fmt.Errorf("screen does not exist") + } + shareMode := tx.GetString(`SELECT sharemode FROM screen WHERE screenid = ?`, screenId) + if shareMode != ShareModeWeb { + return fmt.Errorf("screen is not currently shared to the web") + } + query = `UPDATE screen SET sharemode = ?, webshareopts = ? WHERE screenid = ?` + tx.Exec(query, ShareModeLocal, "null", screenId) + query = `DELETE FROM screenupdates WHERE screenid = ?` + tx.Exec(query, screenId) + return nil + }) +} + +func isWebShare(tx *TxWrap, screenId string) bool { + return tx.Exists(`SELECT screenid FROM screen WHERE screenid = ? AND sharemode = ?`, screenId, ShareModeWeb) +} diff --git a/pkg/sstore/migrate.go b/pkg/sstore/migrate.go index 61300692..2eaf8ace 100644 --- a/pkg/sstore/migrate.go +++ b/pkg/sstore/migrate.go @@ -17,7 +17,7 @@ import ( "github.com/golang-migrate/migrate/v4" ) -const MaxMigration = 13 +const MaxMigration = 15 const MigratePrimaryScreenVersion = 9 func MakeMigrate() (*migrate.Migrate, error) { diff --git a/pkg/sstore/quick.go b/pkg/sstore/quick.go index e92c81f5..116b2499 100644 --- a/pkg/sstore/quick.go +++ b/pkg/sstore/quick.go @@ -80,6 +80,21 @@ func quickSetJson(ptr interface{}, m map[string]interface{}, name string) { json.Unmarshal([]byte(str), ptr) } +func quickSetNullableJson(ptr interface{}, m map[string]interface{}, name string) { + v, ok := m[name] + if !ok { + return + } + str, ok := v.(string) + if !ok { + return + } + if str == "" { + str = "null" + } + json.Unmarshal([]byte(str), ptr) +} + func quickSetJsonArr(ptr interface{}, m map[string]interface{}, name string) { v, ok := m[name] if !ok { @@ -95,6 +110,14 @@ func quickSetJsonArr(ptr interface{}, m map[string]interface{}, name string) { json.Unmarshal([]byte(str), ptr) } +func quickNullableJson(v interface{}) string { + if v == nil { + return "null" + } + barr, _ := json.Marshal(v) + return string(barr) +} + func quickJson(v interface{}) string { if v == nil { return "{}" diff --git a/pkg/sstore/sstore.go b/pkg/sstore/sstore.go index 6d52f815..28fb3b84 100644 --- a/pkg/sstore/sstore.go +++ b/pkg/sstore/sstore.go @@ -54,10 +54,8 @@ const ( ) const ( - ShareModeLocal = "local" - ShareModePrivate = "private" - ShareModeView = "view" - ShareModeShared = "shared" + ShareModeLocal = "local" + ShareModeWeb = "web" ) const ( @@ -81,6 +79,19 @@ const ( CmdStoreTypeScreen = "screen" ) +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" +) + const MaxTzNameLen = 50 var globalDBLock = &sync.Mutex{} @@ -212,6 +223,7 @@ type ClientData struct { FeOpts FeOptsType `json:"feopts"` CmdStoreType string `json:"cmdstoretype"` Migration *ClientMigrationData `json:"migration,omitempty" dbmap:"-"` + DBVersion int `json:"dbversion" dbmap:"-"` } func (ClientData) UseDBMap() {} @@ -401,21 +413,27 @@ type ScreenLinesType struct { func (ScreenLinesType) UseDBMap() {} +type ScreenWebShareOpts struct { + ShareName string `json:"sharename"` + ViewKey string `json:"viewkey"` +} + type ScreenType struct { - SessionId string `json:"sessionid"` - ScreenId string `json:"screenid"` - Name string `json:"name"` - ScreenIdx int64 `json:"screenidx"` - ScreenOpts ScreenOptsType `json:"screenopts"` - OwnerId string `json:"ownerid"` - ShareMode string `json:"sharemode"` - CurRemote RemotePtrType `json:"curremote"` - NextLineNum int64 `json:"nextlinenum"` - SelectedLine int64 `json:"selectedline"` - Anchor ScreenAnchorType `json:"anchor"` - FocusType string `json:"focustype"` - Archived bool `json:"archived,omitempty"` - ArchivedTs int64 `json:"archivedts,omitempty"` + SessionId string `json:"sessionid"` + ScreenId string `json:"screenid"` + Name string `json:"name"` + ScreenIdx int64 `json:"screenidx"` + ScreenOpts ScreenOptsType `json:"screenopts"` + OwnerId string `json:"ownerid"` + ShareMode string `json:"sharemode"` + WebShareOpts *ScreenWebShareOpts `json:"webshareopts,omitempty"` + CurRemote RemotePtrType `json:"curremote"` + NextLineNum int64 `json:"nextlinenum"` + SelectedLine int64 `json:"selectedline"` + Anchor ScreenAnchorType `json:"anchor"` + FocusType string `json:"focustype"` + Archived bool `json:"archived,omitempty"` + ArchivedTs int64 `json:"archivedts,omitempty"` // only for updates Full bool `json:"full,omitempty"` @@ -431,6 +449,7 @@ func (s *ScreenType) ToMap() map[string]interface{} { rtn["screenopts"] = quickJson(s.ScreenOpts) rtn["ownerid"] = s.OwnerId rtn["sharemode"] = s.ShareMode + rtn["webshareopts"] = quickNullableJson(s.WebShareOpts) rtn["curremoteownerid"] = s.CurRemote.OwnerId rtn["curremoteid"] = s.CurRemote.RemoteId rtn["curremotename"] = s.CurRemote.Name @@ -451,6 +470,7 @@ func (s *ScreenType) FromMap(m map[string]interface{}) bool { quickSetJson(&s.ScreenOpts, m, "screenopts") quickSetStr(&s.OwnerId, m, "ownerid") quickSetStr(&s.ShareMode, m, "sharemode") + quickSetNullableJson(&s.WebShareOpts, m, "webshareopts") quickSetStr(&s.CurRemote.OwnerId, m, "curremoteownerid") quickSetStr(&s.CurRemote.RemoteId, m, "curremoteid") quickSetStr(&s.CurRemote.Name, m, "curremotename") @@ -654,6 +674,16 @@ func (ri *RemoteInstance) ToMap() map[string]interface{} { return rtn } +type ScreenUpdateType struct { + UpdateId int64 `json:"updateid"` + ScreenId string `json:"screenid"` + LineId string `json:"lineid"` + UpdateType string `json:"updatetype"` + UpdateTs int64 `json:"updatets"` +} + +func (ScreenUpdateType) UseDBMap() {} + type LineType struct { ScreenId string `json:"screenid"` UserId string `json:"userid"` @@ -669,7 +699,6 @@ type LineType struct { Ephemeral bool `json:"ephemeral,omitempty"` ContentHeight int64 `json:"contentheight,omitempty"` Star bool `json:"star,omitempty"` - Bookmarked bool `json:"bookmarked,omitempty"` Archived bool `json:"archived,omitempty"` Remove bool `json:"remove,omitempty"` } @@ -747,15 +776,14 @@ type PlaybookEntry struct { } type BookmarkType struct { - BookmarkId string `json:"bookmarkid"` - CreatedTs int64 `json:"createdts"` - CmdStr string `json:"cmdstr"` - Alias string `json:"alias,omitempty"` - Tags []string `json:"tags"` - Description string `json:"description"` - Cmds []base.CommandKey `json:"cmds"` - OrderIdx int64 `json:"orderidx"` - Remove bool `json:"remove,omitempty"` + BookmarkId string `json:"bookmarkid"` + CreatedTs int64 `json:"createdts"` + CmdStr string `json:"cmdstr"` + Alias string `json:"alias,omitempty"` + Tags []string `json:"tags"` + Description string `json:"description"` + OrderIdx int64 `json:"orderidx"` + Remove bool `json:"remove,omitempty"` } func (bm *BookmarkType) GetSimpleKey() string { @@ -1127,6 +1155,8 @@ func EnsureClientData(ctx context.Context) (*ClientData, error) { if cdata == nil { return nil, fmt.Errorf("no client data found") } + dbVersion := tx.GetInt(`SELECT version FROM schema_migrations`) + cdata.DBVersion = dbVersion return cdata, nil }) if err != nil { diff --git a/pkg/sstore/updatebus.go b/pkg/sstore/updatebus.go index 657c8750..add29e74 100644 --- a/pkg/sstore/updatebus.go +++ b/pkg/sstore/updatebus.go @@ -30,24 +30,25 @@ func (PtyDataUpdate) UpdateType() string { } type ModelUpdate struct { - Sessions []*SessionType `json:"sessions,omitempty"` - ActiveSessionId string `json:"activesessionid,omitempty"` - Screens []*ScreenType `json:"screens,omitempty"` - ScreenLines *ScreenLinesType `json:"screenlines,omitempty"` - Line *LineType `json:"line,omitempty"` - Lines []*LineType `json:"lines,omitempty"` - Cmd *CmdType `json:"cmd,omitempty"` - CmdLine *CmdLineType `json:"cmdline,omitempty"` - Info *InfoMsgType `json:"info,omitempty"` - ClearInfo bool `json:"clearinfo,omitempty"` - Remotes []interface{} `json:"remotes,omitempty"` // []*remote.RemoteState - History *HistoryInfoType `json:"history,omitempty"` - Interactive bool `json:"interactive"` - Connect bool `json:"connect,omitempty"` - MainView string `json:"mainview,omitempty"` - Bookmarks []*BookmarkType `json:"bookmarks,omitempty"` - HistoryViewData *HistoryViewData `json:"historyviewdata,omitempty"` - ClientData *ClientData `json:"clientdata,omitempty"` + Sessions []*SessionType `json:"sessions,omitempty"` + ActiveSessionId string `json:"activesessionid,omitempty"` + Screens []*ScreenType `json:"screens,omitempty"` + ScreenLines *ScreenLinesType `json:"screenlines,omitempty"` + Line *LineType `json:"line,omitempty"` + Lines []*LineType `json:"lines,omitempty"` + Cmd *CmdType `json:"cmd,omitempty"` + CmdLine *CmdLineType `json:"cmdline,omitempty"` + Info *InfoMsgType `json:"info,omitempty"` + ClearInfo bool `json:"clearinfo,omitempty"` + Remotes []interface{} `json:"remotes,omitempty"` // []*remote.RemoteState + History *HistoryInfoType `json:"history,omitempty"` + Interactive bool `json:"interactive"` + Connect bool `json:"connect,omitempty"` + MainView string `json:"mainview,omitempty"` + Bookmarks []*BookmarkType `json:"bookmarks,omitempty"` + SelectedBookmark string `json:"selectedbookmark,omitempty"` + HistoryViewData *HistoryViewData `json:"historyviewdata,omitempty"` + ClientData *ClientData `json:"clientdata,omitempty"` } func (ModelUpdate) UpdateType() string {