2023-10-17 12:31:13 +08:00
// Copyright 2023, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
2022-07-01 14:07:13 -07:00
package sstore
import (
"context"
2023-03-25 12:54:56 -07:00
"errors"
2022-07-01 14:07:13 -07:00
"fmt"
2023-03-31 18:15:51 -07:00
"log"
2022-09-20 17:37:49 -07:00
"strconv"
2022-07-12 21:51:17 -07:00
"strings"
2023-02-14 16:17:54 -08:00
"sync"
2022-11-28 00:13:00 -08:00
"time"
2022-07-01 14:07:13 -07:00
2023-10-25 22:07:00 -07:00
"github.com/jmoiron/sqlx"
"github.com/sawka/txwrap"
2023-10-16 13:30:10 -07:00
"github.com/wavetermdev/waveterm/waveshell/pkg/base"
"github.com/wavetermdev/waveterm/waveshell/pkg/packet"
2024-01-16 16:11:04 -08:00
"github.com/wavetermdev/waveterm/waveshell/pkg/shellapi"
"github.com/wavetermdev/waveterm/waveshell/pkg/utilfn"
2023-10-16 13:30:10 -07:00
"github.com/wavetermdev/waveterm/wavesrv/pkg/dbutil"
"github.com/wavetermdev/waveterm/wavesrv/pkg/scbase"
2024-02-15 16:45:47 -08:00
"github.com/wavetermdev/waveterm/wavesrv/pkg/scbus"
2022-07-01 14:07:13 -07:00
)
2023-03-26 23:07:30 -07:00
var updateWriterCVar = sync . NewCond ( & sync . Mutex {})
2023-03-28 00:24:37 -07:00
var WebScreenPtyPosLock = & sync . Mutex {}
var WebScreenPtyPosDelIntent = make ( map [ string ] bool ) // map[screenid + ":" + lineid] -> bool
2023-03-26 23:07:30 -07:00
2023-02-14 16:17:54 -08:00
type SingleConnDBGetter struct {
SingleConnLock * sync . Mutex
}
2023-04-11 23:54:18 -07:00
type FeStateType map [ string ] string
2023-02-14 16:17:54 -08:00
type TxWrap = txwrap . TxWrap
var dbWrap * SingleConnDBGetter
func init () {
dbWrap = & SingleConnDBGetter { SingleConnLock : & sync . Mutex {}}
}
func ( dbg * SingleConnDBGetter ) GetDB ( ctx context . Context ) ( * sqlx . DB , error ) {
db , err := GetDB ( ctx )
if err != nil {
return nil , err
}
dbg . SingleConnLock . Lock ()
return db , nil
}
func ( dbg * SingleConnDBGetter ) ReleaseDB ( db * sqlx . DB ) {
dbg . SingleConnLock . Unlock ()
}
func WithTx ( ctx context . Context , fn func ( tx * TxWrap ) error ) error {
return txwrap . DBGWithTx ( ctx , dbWrap , fn )
}
2023-03-26 23:07:30 -07:00
func NotifyUpdateWriter () {
2023-03-31 18:15:51 -07:00
// must happen in a goroutine to prevent deadlock.
// update-writer holds this lock while reading from the DB. we can't be holding the DB lock while calling this!
go func () {
updateWriterCVar . L . Lock ()
defer updateWriterCVar . L . Unlock ()
updateWriterCVar . Signal ()
}()
2023-03-26 23:07:30 -07:00
}
func UpdateWriterCheckMoreData () {
updateWriterCVar . L . Lock ()
defer updateWriterCVar . L . Unlock ()
for {
2023-03-31 18:15:51 -07:00
updateCount , err := CountScreenUpdates ( context . Background ())
if err != nil {
log . Printf ( "ERROR getting screen update count (sleeping): %v" , err )
// will just lead to a Wait()
}
if updateCount > 0 {
2023-03-26 23:07:30 -07:00
break
}
updateWriterCVar . Wait ()
}
}
2022-07-01 14:07:13 -07:00
func NumSessions ( ctx context . Context ) ( int , error ) {
2022-10-10 17:30:48 -07:00
var numSessions int
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := "SELECT count(*) FROM session"
numSessions = tx . GetInt ( query )
return nil
})
return numSessions , txErr
2022-07-01 14:07:13 -07:00
}
2024-04-23 17:40:14 -07:00
func NumScreens ( ctx context . Context ) ( int , error ) {
var numScreens int
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := "SELECT count(*) FROM screen"
numScreens = tx . GetInt ( query )
return nil
})
return numScreens , txErr
}
2022-07-01 17:38:36 -07:00
func GetAllRemotes ( ctx context . Context ) ([] * RemoteType , error ) {
2022-07-07 13:26:46 -07:00
var rtn [] * RemoteType
err := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-09-14 12:06:55 -07:00
query := `SELECT * FROM remote ORDER BY remoteidx`
2022-07-07 13:26:46 -07:00
marr := tx . SelectMaps ( query )
for _ , m := range marr {
2023-03-27 14:11:02 -07:00
rtn = append ( rtn , dbutil . FromMap [ * RemoteType ]( m ))
2022-07-07 13:26:46 -07:00
}
return nil
})
2022-07-01 17:38:36 -07:00
if err != nil {
return nil , err
}
2022-07-07 13:26:46 -07:00
return rtn , nil
2022-07-01 17:38:36 -07:00
}
2023-12-28 11:09:41 -08:00
func GetAllImportedRemotes ( ctx context . Context ) ( map [ string ] * RemoteType , error ) {
rtn := make ( map [ string ] * RemoteType )
err := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT * FROM remote
WHERE sshconfigsrc = "sshconfig-import"
ORDER BY remoteidx`
marr := tx . SelectMaps ( query )
for _ , m := range marr {
remote := dbutil . FromMap [ * RemoteType ]( m )
rtn [ remote . RemoteCanonicalName ] = remote
}
return nil
})
if err != nil {
return nil , err
}
return rtn , nil
}
2022-08-17 12:24:09 -07:00
func GetRemoteByAlias ( ctx context . Context , alias string ) ( * RemoteType , error ) {
var remote * RemoteType
err := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT * FROM remote WHERE remotealias = ?`
m := tx . GetMap ( query , alias )
2023-03-27 14:11:02 -07:00
remote = dbutil . FromMap [ * RemoteType ]( m )
2022-08-17 12:24:09 -07:00
return nil
})
if err != nil {
return nil , err
}
return remote , nil
}
2022-08-16 15:08:28 -07:00
func GetRemoteById ( ctx context . Context , remoteId string ) ( * RemoteType , error ) {
2022-07-07 13:26:46 -07:00
var remote * RemoteType
err := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-08-16 15:08:28 -07:00
query := `SELECT * FROM remote WHERE remoteid = ?`
m := tx . GetMap ( query , remoteId )
2023-03-27 14:11:02 -07:00
remote = dbutil . FromMap [ * RemoteType ]( m )
2022-07-07 13:26:46 -07:00
return nil
})
2022-07-01 14:07:13 -07:00
if err != nil {
return nil , err
}
2022-07-07 13:26:46 -07:00
return remote , nil
2022-07-01 14:07:13 -07:00
}
2022-10-04 11:45:24 -07:00
func GetLocalRemote ( ctx context . Context ) ( * RemoteType , error ) {
var remote * RemoteType
err := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT * FROM remote WHERE local`
m := tx . GetMap ( query )
2023-03-27 14:11:02 -07:00
remote = dbutil . FromMap [ * RemoteType ]( m )
2022-10-04 11:45:24 -07:00
return nil
})
if err != nil {
return nil , err
}
return remote , nil
}
2022-09-13 17:11:36 -07:00
func GetRemoteByCanonicalName ( ctx context . Context , cname string ) ( * RemoteType , error ) {
var remote * RemoteType
err := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT * FROM remote WHERE remotecanonicalname = ?`
2023-03-27 14:11:02 -07:00
remote = dbutil . GetMapGen [ * RemoteType ]( tx , query , cname )
2022-09-13 17:11:36 -07:00
return nil
})
if err != nil {
return nil , err
}
return remote , nil
}
func UpsertRemote ( ctx context . Context , r * RemoteType ) error {
if r == nil {
2022-07-01 14:07:13 -07:00
return fmt . Errorf ( "cannot insert nil remote" )
}
2022-09-13 17:11:36 -07:00
if r . RemoteId == "" {
2022-09-01 12:47:10 -07:00
return fmt . Errorf ( "cannot insert remote without id" )
2022-07-01 14:07:13 -07:00
}
2022-09-13 17:11:36 -07:00
if r . RemoteCanonicalName == "" {
2022-09-01 12:47:10 -07:00
return fmt . Errorf ( "cannot insert remote with canonicalname" )
2022-07-01 14:07:13 -07:00
}
2022-09-13 17:11:36 -07:00
if r . RemoteType == "" {
2022-09-01 12:47:10 -07:00
return fmt . Errorf ( "cannot insert remote without type" )
}
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT remoteid FROM remote WHERE remoteid = ?`
2022-09-13 17:11:36 -07:00
if tx . Exists ( query , r . RemoteId ) {
2023-02-14 16:17:54 -08:00
tx . Exec ( `DELETE FROM remote WHERE remoteid = ?` , r . RemoteId )
2022-09-01 12:47:10 -07:00
}
2022-09-13 17:11:36 -07:00
query = `SELECT remoteid FROM remote WHERE remotecanonicalname = ?`
if tx . Exists ( query , r . RemoteCanonicalName ) {
return fmt . Errorf ( "remote has duplicate canonicalname '%s', cannot create" , r . RemoteCanonicalName )
2022-09-01 12:47:10 -07:00
}
2022-09-30 16:23:40 -07:00
query = `SELECT remoteid FROM remote WHERE remotealias = ?`
2022-09-30 16:05:48 -07:00
if r . RemoteAlias != "" && tx . Exists ( query , r . RemoteAlias ) {
return fmt . Errorf ( "remote has duplicate alias '%s', cannot create" , r . RemoteAlias )
}
2022-09-20 14:23:53 -07:00
query = `SELECT COALESCE(max(remoteidx), 0) FROM remote`
2022-09-14 12:06:55 -07:00
maxRemoteIdx := tx . GetInt ( query )
r . RemoteIdx = int64 ( maxRemoteIdx + 1 )
2022-09-01 12:47:10 -07:00
query = `INSERT INTO remote
2024-01-16 16:11:04 -08:00
( remoteid, remotetype, remotealias, remotecanonicalname, remoteuser, remotehost, connectmode, autoinstall, sshopts, remoteopts, lastconnectts, archived, remoteidx, local, statevars, sshconfigsrc, openaiopts, shellpref) VALUES
(:remoteid,:remotetype,:remotealias,:remotecanonicalname,:remoteuser,:remotehost,:connectmode,:autoinstall,:sshopts,:remoteopts,:lastconnectts,:archived,:remoteidx,:local,:statevars,:sshconfigsrc,:openaiopts,:shellpref)`
2023-02-14 16:17:54 -08:00
tx . NamedExec ( query , r . ToMap ())
2022-09-13 12:06:12 -07:00
return nil
})
return txErr
}
2023-03-29 12:42:04 -07:00
func UpdateRemoteStateVars ( ctx context . Context , remoteId string , stateVars map [ string ] string ) error {
return WithTx ( ctx , func ( tx * TxWrap ) error {
query := `UPDATE remote SET statevars = ? WHERE remoteid = ?`
tx . Exec ( query , quickJson ( stateVars ), remoteId )
return nil
})
}
2022-12-25 13:03:11 -08:00
// includes archived sessions
2022-08-08 16:21:46 -07:00
func GetBareSessions ( ctx context . Context ) ([] * SessionType , error ) {
var rtn [] * SessionType
err := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-12-26 16:09:21 -08:00
query := `SELECT * FROM session ORDER BY archived, sessionidx, archivedts`
2023-02-14 16:17:54 -08:00
tx . Select ( & rtn , query )
2022-08-08 16:21:46 -07:00
return nil
})
if err != nil {
return nil , err
}
return rtn , nil
}
2022-12-26 16:09:21 -08:00
// does not include archived, finds lowest sessionidx (for resetting active session)
func GetFirstSessionId ( ctx context . Context ) ( string , error ) {
2022-08-26 16:21:19 -07:00
var rtn [] string
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-12-25 13:03:11 -08:00
query := `SELECT sessionid from session WHERE NOT archived ORDER by sessionidx`
2022-08-26 16:21:19 -07:00
rtn = tx . SelectStrings ( query )
return nil
})
if txErr != nil {
2022-12-26 16:09:21 -08:00
return "" , txErr
2022-08-26 16:21:19 -07:00
}
2022-12-26 16:09:21 -08:00
if len ( rtn ) == 0 {
return "" , nil
}
return rtn [ 0 ], nil
2022-08-26 16:21:19 -07:00
}
2022-08-26 13:12:17 -07:00
func GetBareSessionById ( ctx context . Context , sessionId string ) ( * SessionType , error ) {
var rtn SessionType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT * FROM session WHERE sessionid = ?`
2023-02-14 16:17:54 -08:00
tx . Get ( & rtn , query , sessionId )
2022-08-26 13:12:17 -07:00
return nil
})
if txErr != nil {
return nil , txErr
}
if rtn . SessionId == "" {
return nil , nil
}
return & rtn , nil
}
2024-02-09 17:19:44 -08:00
const getAllSessionsQuery = `SELECT * FROM session ORDER BY archived, sessionidx, archivedts`
// Gets all sessions, including archived
func GetAllSessions ( ctx context . Context ) ([] * SessionType , error ) {
return WithTxRtn ( ctx , func ( tx * TxWrap ) ([] * SessionType , error ) {
rtn := [] * SessionType {}
tx . Select ( & rtn , getAllSessionsQuery )
return rtn , nil
})
}
// Get all sessions and screens, including remotes
func GetConnectUpdate ( ctx context . Context ) ( * ConnectUpdate , error ) {
return WithTxRtn ( ctx , func ( tx * TxWrap ) ( * ConnectUpdate , error ) {
update := & ConnectUpdate {}
sessions := [] * SessionType {}
tx . Select ( & sessions , getAllSessionsQuery )
2022-08-10 18:33:32 -07:00
sessionMap := make ( map [ string ] * SessionType )
2024-02-09 17:19:44 -08:00
for _ , session := range sessions {
2022-08-10 18:33:32 -07:00
sessionMap [ session . SessionId ] = session
2024-02-09 17:19:44 -08:00
update . Sessions = append ( update . Sessions , session )
2022-08-08 16:21:46 -07:00
}
2024-02-09 17:19:44 -08:00
query := `SELECT * FROM screen ORDER BY archived, screenidx, archivedts`
screens := dbutil . SelectMapsGen [ * ScreenType ]( tx , query )
for _ , screen := range screens {
update . Screens = append ( update . Screens , screen )
2023-03-13 12:10:23 -07:00
}
2022-08-24 02:14:16 -07:00
query = `SELECT * FROM remote_instance`
2023-03-27 14:11:02 -07:00
riArr := dbutil . SelectMapsGen [ * RemoteInstance ]( tx , query )
2023-03-09 18:44:01 -08:00
for _ , ri := range riArr {
2022-08-10 18:33:32 -07:00
s := sessionMap [ ri . SessionId ]
if s != nil {
s . Remotes = append ( s . Remotes , ri )
}
}
2022-08-26 16:21:19 -07:00
query = `SELECT activesessionid FROM client`
2023-03-13 10:50:29 -07:00
update . ActiveSessionId = tx . GetString ( query )
return update , nil
2022-07-12 13:50:44 -07:00
})
}
2023-03-13 10:50:29 -07:00
func GetScreenLinesById ( ctx context . Context , screenId string ) ( * ScreenLinesType , error ) {
return WithTxRtn ( ctx , func ( tx * TxWrap ) ( * ScreenLinesType , error ) {
2023-03-15 18:12:55 -07:00
query := `SELECT screenid FROM screen WHERE screenid = ?`
2023-03-27 14:11:02 -07:00
screen := dbutil . GetMappable [ * ScreenLinesType ]( tx , query , screenId )
2023-03-13 01:52:30 -07:00
if screen == nil {
return nil , nil
2022-07-12 13:50:44 -07:00
}
2023-03-20 19:20:57 -07:00
query = `SELECT * FROM line WHERE screenid = ? ORDER BY linenum`
2023-09-01 15:21:35 -07:00
screen . Lines = dbutil . SelectMappable [ * LineType ]( tx , query , screen . ScreenId )
2023-07-30 17:16:43 -07:00
query = `SELECT * FROM cmd WHERE screenid = ?`
2023-03-27 14:11:02 -07:00
screen . Cmds = dbutil . SelectMapsGen [ * CmdType ]( tx , query , screen . ScreenId )
2023-03-13 01:52:30 -07:00
return screen , nil
2022-07-12 13:50:44 -07:00
})
2022-07-08 13:23:45 -07:00
}
2023-03-14 16:37:22 -07:00
// includes archived screens
2023-03-13 12:10:23 -07:00
func GetSessionScreens ( ctx context . Context , sessionId string ) ([] * ScreenType , error ) {
return WithTxRtn ( ctx , func ( tx * TxWrap ) ([] * ScreenType , error ) {
2022-12-25 13:21:48 -08:00
query := `SELECT * FROM screen WHERE sessionid = ? ORDER BY archived, screenidx, archivedts`
2023-03-27 14:11:02 -07:00
rtn := dbutil . SelectMapsGen [ * ScreenType ]( tx , query , sessionId )
2023-03-13 12:10:23 -07:00
return rtn , nil
2022-12-23 15:56:29 -08:00
})
}
2022-07-01 14:07:13 -07:00
func GetSessionById ( ctx context . Context , id string ) ( * SessionType , error ) {
2024-02-09 17:19:44 -08:00
allSessions , err := GetAllSessions ( ctx )
2022-07-01 14:07:13 -07:00
if err != nil {
return nil , err
}
2022-08-08 16:21:46 -07:00
for _ , session := range allSessions {
if session . SessionId == id {
return session , nil
}
}
return nil , nil
2022-07-01 14:07:13 -07:00
}
2023-11-01 23:41:04 -07:00
// counts non-archived sessions
func GetSessionCount ( ctx context . Context ) ( int , error ) {
return WithTxRtn ( ctx , func ( tx * TxWrap ) ( int , error ) {
query := `SELECT COALESCE(count(*), 0) FROM session WHERE NOT archived`
numSessions := tx . GetInt ( query )
return numSessions , nil
})
}
2022-07-04 22:18:01 -07:00
func GetSessionByName ( ctx context . Context , name string ) ( * SessionType , error ) {
2022-10-10 17:30:48 -07:00
var session * SessionType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT sessionid FROM session WHERE name = ?`
sessionId := tx . GetString ( query , name )
if sessionId == "" {
return nil
2022-07-04 22:18:01 -07:00
}
2022-10-10 17:30:48 -07:00
var err error
session , err = GetSessionById ( tx . Context (), sessionId )
if err != nil {
return err
}
return nil
})
if txErr != nil {
return nil , txErr
2022-07-04 22:18:01 -07:00
}
2022-10-10 17:30:48 -07:00
return session , nil
2022-07-04 22:18:01 -07:00
}
2024-03-27 00:22:57 -07:00
// returns (update, newSessionId, newScreenId, error)
2022-07-08 13:23:45 -07:00
// if sessionName == "", it will be generated
2024-03-27 00:22:57 -07:00
func InsertSessionWithName ( ctx context . Context , sessionName string , activate bool ) ( * scbus . ModelUpdatePacketType , string , string , error ) {
2023-03-13 12:23:36 -07:00
var newScreen * ScreenType
2023-11-01 01:26:19 -07:00
newSessionId := scbase . GenWaveUUID ()
2022-07-08 13:23:45 -07:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-07-12 21:51:17 -07:00
names := tx . SelectStrings ( `SELECT name FROM session` )
2023-10-12 13:36:11 -07:00
sessionName = fmtUniqueName ( sessionName , "workspace-%d" , len ( names ) + 1 , names )
2022-07-12 21:51:17 -07:00
maxSessionIdx := tx . GetInt ( `SELECT COALESCE(max(sessionidx), 0) FROM session` )
2023-03-08 17:16:06 -08:00
query := `INSERT INTO session (sessionid, name, activescreenid, sessionidx, notifynum, archived, archivedts, sharemode)
2023-07-26 10:10:27 -07:00
VALUES (?, ?, '', ?, 0, 0, 0, ?)`
tx . Exec ( query , newSessionId , sessionName , maxSessionIdx + 1 , ShareModeLocal )
screenUpdate , err := InsertScreen ( tx . Context (), newSessionId , "" , ScreenCreateOpts {}, true )
2022-07-12 21:51:17 -07:00
if err != nil {
return err
2022-07-08 13:23:45 -07:00
}
2024-02-15 16:45:47 -08:00
screenUpdateItems := scbus . GetUpdateItems [ ScreenType ]( screenUpdate )
2024-02-09 17:19:44 -08:00
if len ( screenUpdateItems ) < 1 {
return fmt . Errorf ( "no screen update items" )
}
newScreen = screenUpdateItems [ 0 ]
2022-08-26 16:21:19 -07:00
if activate {
query = `UPDATE client SET activesessionid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , newSessionId )
2022-08-26 16:21:19 -07:00
}
2022-07-01 14:07:13 -07:00
return nil
})
2022-07-14 18:39:40 -07:00
if txErr != nil {
2024-03-27 00:22:57 -07:00
return nil , "" , "" , txErr
2022-07-14 18:39:40 -07:00
}
2022-08-08 16:21:46 -07:00
session , err := GetSessionById ( ctx , newSessionId )
if err != nil {
2024-03-27 00:22:57 -07:00
return nil , "" , "" , err
2022-08-08 16:21:46 -07:00
}
2024-02-15 16:45:47 -08:00
update := scbus . MakeUpdatePacket ()
update . AddUpdate ( * session )
update . AddUpdate ( * newScreen )
2022-08-08 16:21:46 -07:00
if activate {
2024-02-15 16:45:47 -08:00
update . AddUpdate ( ActiveSessionIdUpdate ( newSessionId ))
2022-08-08 16:21:46 -07:00
}
2024-03-27 00:22:57 -07:00
return update , newSessionId , newScreen . ScreenId , nil
2022-07-08 13:23:45 -07:00
}
2022-08-29 16:31:06 -07:00
func SetActiveSessionId ( ctx context . Context , sessionId string ) error {
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-12-26 16:09:21 -08:00
query := `SELECT sessionid FROM session WHERE sessionid = ?`
2022-08-29 16:31:06 -07:00
if ! tx . Exists ( query , sessionId ) {
return fmt . Errorf ( "cannot switch to session, not found" )
}
query = `UPDATE client SET activesessionid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , sessionId )
2022-08-29 16:31:06 -07:00
return nil
})
return txErr
}
2022-12-23 15:56:29 -08:00
func GetActiveSessionId ( ctx context . Context ) ( string , error ) {
var rtnId string
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT activesessionid FROM client`
rtnId = tx . GetString ( query )
return nil
})
return rtnId , txErr
}
2022-09-25 00:26:33 -07:00
func SetWinSize ( ctx context . Context , winSize ClientWinSizeType ) error {
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `UPDATE client SET winsize = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , quickJson ( winSize ))
2022-09-25 00:26:33 -07:00
return nil
})
return txErr
}
2023-02-26 14:33:01 -08:00
func UpdateClientFeOpts ( ctx context . Context , feOpts FeOptsType ) error {
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `UPDATE client SET feopts = ?`
tx . Exec ( query , quickJson ( feOpts ))
return nil
})
return txErr
}
2023-05-08 16:06:51 -07:00
func UpdateClientOpenAIOpts ( ctx context . Context , aiOpts OpenAIOptsType ) error {
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `UPDATE client SET openaiopts = ?`
tx . Exec ( query , quickJson ( aiOpts ))
return nil
})
return txErr
}
2022-07-08 13:23:45 -07:00
func containsStr ( strs [] string , testStr string ) bool {
for _ , s := range strs {
if s == testStr {
return true
}
}
return false
}
2022-07-12 21:51:17 -07:00
func fmtUniqueName ( name string , defaultFmtStr string , startIdx int , strs [] string ) string {
var fmtStr string
if name != "" {
if ! containsStr ( strs , name ) {
return name
}
fmtStr = name + "-%d"
startIdx = 2
} else {
fmtStr = defaultFmtStr
}
if strings . Index ( fmtStr , "%d" ) == - 1 {
panic ( "invalid fmtStr: " + fmtStr )
}
for {
testName := fmt . Sprintf ( fmtStr , startIdx )
if containsStr ( strs , testName ) {
startIdx ++
continue
}
return testName
}
}
2024-02-15 16:45:47 -08:00
func InsertScreen ( ctx context . Context , sessionId string , origScreenName string , opts ScreenCreateOpts , activate bool ) ( * scbus . ModelUpdatePacketType , error ) {
2022-07-12 16:10:46 -07:00
var newScreenId string
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-12-25 13:03:11 -08:00
query := `SELECT sessionid FROM session WHERE sessionid = ? AND NOT archived`
2022-07-12 21:51:17 -07:00
if ! tx . Exists ( query , sessionId ) {
2022-12-26 16:09:21 -08:00
return fmt . Errorf ( "cannot create screen, no session found (or session archived)" )
2022-07-12 21:51:17 -07:00
}
2023-03-13 01:52:30 -07:00
localRemoteId := tx . GetString ( `SELECT remoteid FROM remote WHERE remotealias = ?` , LocalRemoteAlias )
if localRemoteId == "" {
2022-08-24 02:14:16 -07:00
return fmt . Errorf ( "cannot create screen, no local remote found" )
}
2022-12-25 13:03:11 -08:00
maxScreenIdx := tx . GetInt ( `SELECT COALESCE(max(screenidx), 0) FROM screen WHERE sessionid = ? AND NOT archived` , sessionId )
2022-12-26 12:18:13 -08:00
var screenName string
if origScreenName == "" {
screenNames := tx . SelectStrings ( `SELECT name FROM screen WHERE sessionid = ? AND NOT archived` , sessionId )
screenName = fmtUniqueName ( "" , "s%d" , maxScreenIdx + 1 , screenNames )
} else {
screenName = origScreenName
}
2023-07-26 10:10:27 -07:00
var baseScreen * ScreenType
if opts . HasCopy () {
if opts . BaseScreenId == "" {
return fmt . Errorf ( "invalid screen create opts, copy option with no base screen specified" )
}
var err error
baseScreen , err = GetScreenById ( tx . Context (), opts . BaseScreenId )
if err != nil {
return err
}
if baseScreen == nil {
return fmt . Errorf ( "cannot create screen, base screen not found" )
}
}
2023-11-01 01:26:19 -07:00
newScreenId = scbase . GenWaveUUID ()
2023-03-13 01:52:30 -07:00
screen := & ScreenType {
SessionId : sessionId ,
ScreenId : newScreenId ,
Name : screenName ,
ScreenIdx : int64 ( maxScreenIdx ) + 1 ,
ScreenOpts : ScreenOptsType {},
OwnerId : "" ,
ShareMode : ShareModeLocal ,
CurRemote : RemotePtrType { RemoteId : localRemoteId },
NextLineNum : 1 ,
SelectedLine : 0 ,
Anchor : ScreenAnchorType {},
FocusType : ScreenFocusInput ,
Archived : false ,
ArchivedTs : 0 ,
}
2023-12-17 23:46:53 -08:00
query = `INSERT INTO screen ( sessionid, screenid, name, screenidx, screenopts, screenviewopts, ownerid, sharemode, webshareopts, curremoteownerid, curremoteid, curremotename, nextlinenum, selectedline, anchor, focustype, archived, archivedts)
VALUES (:sessionid,:screenid,:name,:screenidx,:screenopts,:screenviewopts,:ownerid,:sharemode,:webshareopts,:curremoteownerid,:curremoteid,:curremotename,:nextlinenum,:selectedline,:anchor,:focustype,:archived,:archivedts)`
2023-03-13 01:52:30 -07:00
tx . NamedExec ( query , screen . ToMap ())
2022-07-14 18:39:40 -07:00
if activate {
query = `UPDATE session SET activescreenid = ? WHERE sessionid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , newScreenId , sessionId )
2022-07-14 18:39:40 -07:00
}
2024-03-27 00:22:57 -07:00
if opts . RtnScreenId != nil {
* opts . RtnScreenId = newScreenId
}
2022-07-12 16:10:46 -07:00
return nil
})
2022-12-26 16:09:21 -08:00
if txErr != nil {
return nil , txErr
}
2023-03-13 01:52:30 -07:00
newScreen , err := GetScreenById ( ctx , newScreenId )
2022-07-15 01:57:45 -07:00
if err != nil {
2022-07-15 17:53:23 -07:00
return nil , err
2022-07-15 01:57:45 -07:00
}
2024-02-15 16:45:47 -08:00
update := scbus . MakeUpdatePacket ()
update . AddUpdate ( * newScreen )
2023-03-13 10:50:29 -07:00
if activate {
bareSession , err := GetBareSessionById ( ctx , sessionId )
if err != nil {
return nil , txErr
}
2024-02-15 16:45:47 -08:00
update . AddUpdate ( * bareSession )
2024-02-09 17:19:44 -08:00
UpdateWithCurrentOpenAICmdInfoChat ( newScreenId , update )
2022-07-15 01:57:45 -07:00
}
2023-03-13 10:50:29 -07:00
return update , nil
2022-07-12 16:10:46 -07:00
}
2023-03-13 01:52:30 -07:00
func GetScreenById ( ctx context . Context , screenId string ) ( * ScreenType , error ) {
return WithTxRtn ( ctx , func ( tx * TxWrap ) ( * ScreenType , error ) {
query := `SELECT * FROM screen WHERE screenid = ?`
2023-03-27 14:11:02 -07:00
screen := dbutil . GetMapGen [ * ScreenType ]( tx , query , screenId )
2023-03-13 01:52:30 -07:00
return screen , nil
2022-07-14 18:39:40 -07:00
})
2022-07-12 13:50:44 -07:00
}
2024-01-26 16:25:21 -08:00
// special "E" returns last unarchived line, "EA" returns last line (even if archived)
2023-03-20 19:20:57 -07:00
func FindLineIdByArg ( ctx context . Context , screenId string , lineArg string ) ( string , error ) {
2024-01-26 16:25:21 -08:00
return WithTxRtn ( ctx , func ( tx * TxWrap ) ( string , error ) {
if lineArg == "E" {
query := `SELECT lineid FROM line WHERE screenid = ? AND NOT archived ORDER BY linenum DESC LIMIT 1`
lineId := tx . GetString ( query , screenId )
return lineId , nil
}
if lineArg == "EA" {
query := `SELECT lineid FROM line WHERE screenid = ? ORDER BY linenum DESC LIMIT 1`
lineId := tx . GetString ( query , screenId )
return lineId , nil
}
2022-09-20 17:37:49 -07:00
lineNum , err := strconv . Atoi ( lineArg )
if err == nil {
// valid linenum
2023-03-20 19:20:57 -07:00
query := `SELECT lineid FROM line WHERE screenid = ? AND linenum = ?`
2024-01-26 16:25:21 -08:00
lineId := tx . GetString ( query , screenId , lineNum )
return lineId , nil
2022-09-20 17:37:49 -07:00
} else if len ( lineArg ) == 8 {
// prefix id string match
2023-03-20 19:20:57 -07:00
query := `SELECT lineid FROM line WHERE screenid = ? AND substr(lineid, 1, 8) = ?`
2024-01-26 16:25:21 -08:00
lineId := tx . GetString ( query , screenId , lineArg )
return lineId , nil
2022-09-20 17:37:49 -07:00
} else {
// id match
2023-03-20 19:20:57 -07:00
query := `SELECT lineid FROM line WHERE screenid = ? AND lineid = ?`
2024-01-26 16:25:21 -08:00
lineId := tx . GetString ( query , screenId , lineArg )
return lineId , nil
2022-09-20 17:37:49 -07:00
}
})
}
2023-03-20 19:20:57 -07:00
func GetLineCmdByLineId ( ctx context . Context , screenId string , lineId string ) ( * LineType , * CmdType , error ) {
2023-03-13 02:09:29 -07:00
return WithTxRtn3 ( ctx , func ( tx * TxWrap ) ( * LineType , * CmdType , error ) {
2023-03-20 19:20:57 -07:00
query := `SELECT * FROM line WHERE screenid = ? AND lineid = ?`
2023-09-01 15:21:35 -07:00
lineVal := dbutil . GetMappable [ * LineType ]( tx , query , screenId , lineId )
if lineVal == nil {
2023-03-13 02:09:29 -07:00
return nil , nil , nil
2022-09-20 17:37:49 -07:00
}
2023-03-13 02:09:29 -07:00
var cmdRtn * CmdType
2023-07-30 17:16:43 -07:00
query = `SELECT * FROM cmd WHERE screenid = ? AND lineid = ?`
cmdRtn = dbutil . GetMapGen [ * CmdType ]( tx , query , screenId , lineId )
2023-09-01 15:21:35 -07:00
return lineVal , cmdRtn , nil
2022-10-27 22:00:10 -07:00
})
}
2022-07-07 21:39:25 -07:00
func InsertLine ( ctx context . Context , line * LineType , cmd * CmdType ) error {
2022-07-02 13:31:56 -07:00
if line == nil {
return fmt . Errorf ( "line cannot be nil" )
}
2022-08-16 12:08:26 -07:00
if line . LineId == "" {
return fmt . Errorf ( "line must have lineid set" )
2022-07-02 13:31:56 -07:00
}
2022-09-20 17:01:25 -07:00
if line . LineNum != 0 {
return fmt . Errorf ( "line should not hage linenum set" )
}
2023-05-02 12:43:54 -07:00
if cmd != nil && cmd . ScreenId == "" {
2023-03-15 18:12:55 -07:00
return fmt . Errorf ( "cmd should have screenid set" )
}
2023-09-17 14:10:35 -07:00
qjs := dbutil . QuickJson ( line . LineState )
if len ( qjs ) > MaxLineStateSize {
return fmt . Errorf ( "linestate exceeds maxsize, size[%d] max[%d]" , len ( qjs ), MaxLineStateSize )
}
2022-07-02 13:31:56 -07:00
return WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-20 19:20:57 -07:00
query := `SELECT screenid FROM screen WHERE screenid = ?`
if ! tx . Exists ( query , line . ScreenId ) {
return fmt . Errorf ( "screen not found, cannot insert line[%s]" , line . ScreenId )
2022-07-02 13:31:56 -07:00
}
2023-03-20 19:20:57 -07:00
query = `SELECT nextlinenum FROM screen WHERE screenid = ?`
nextLineNum := tx . GetInt ( query , line . ScreenId )
2022-09-20 17:01:25 -07:00
line . LineNum = int64 ( nextLineNum )
2023-09-01 15:21:35 -07:00
query = `INSERT INTO line ( screenid, userid, lineid, ts, linenum, linenumtemp, linelocal, linetype, linestate, text, renderer, ephemeral, contentheight, star, archived)
VALUES (:screenid,:userid,:lineid,:ts,:linenum,:linenumtemp,:linelocal,:linetype,:linestate,:text,:renderer,:ephemeral,:contentheight,:star,:archived)`
tx . NamedExec ( query , dbutil . ToDBMap ( line , false ))
2023-03-20 19:20:57 -07:00
query = `UPDATE screen SET nextlinenum = ? WHERE screenid = ?`
tx . Exec ( query , nextLineNum + 1 , line . ScreenId )
2022-07-07 21:39:25 -07:00
if cmd != nil {
2022-10-06 18:33:54 -07:00
cmd . OrigTermOpts = cmd . TermOpts
2022-07-07 21:39:25 -07:00
cmdMap := cmd . ToMap ()
query = `
2024-01-26 16:25:21 -08:00
INSERT INTO cmd ( screenid, lineid, remoteownerid, remoteid, remotename, cmdstr, rawcmdstr, festate, statebasehash, statediffhasharr, termopts, origtermopts, status, cmdpid, remotepid, donets, restartts, exitcode, durationms, rtnstate, runout, rtnbasehash, rtndiffhasharr)
VALUES (:screenid,:lineid,:remoteownerid,:remoteid,:remotename,:cmdstr,:rawcmdstr,:festate,:statebasehash,:statediffhasharr,:termopts,:origtermopts,:status,:cmdpid,:remotepid,:donets,:restartts,:exitcode,:durationms,:rtnstate,:runout,:rtnbasehash,:rtndiffhasharr)
2022-07-07 00:10:37 -07:00
`
2023-02-14 16:17:54 -08:00
tx . NamedExec ( query , cmdMap )
2022-07-07 21:39:25 -07:00
}
2022-07-07 00:10:37 -07:00
return nil
})
}
2023-07-30 17:16:43 -07:00
func GetCmdByScreenId ( ctx context . Context , screenId string , lineId string ) ( * CmdType , error ) {
2023-12-27 13:11:53 -08:00
return WithTxRtn ( ctx , func ( tx * TxWrap ) ( * CmdType , error ) {
2023-07-30 17:16:43 -07:00
query := `SELECT * FROM cmd WHERE screenid = ? AND lineid = ?`
2023-12-27 13:11:53 -08:00
cmd := dbutil . GetMapGen [ * CmdType ]( tx , query , screenId , lineId )
return cmd , nil
2022-07-07 13:26:46 -07:00
})
2022-07-07 00:10:37 -07:00
}
2022-07-07 16:29:14 -07:00
2024-02-15 16:45:47 -08:00
func UpdateWithClearOpenAICmdInfo ( screenId string ) * scbus . ModelUpdatePacketType {
2024-01-11 17:34:23 -08:00
ScreenMemClearCmdInfoChat ( screenId )
2024-02-09 17:19:44 -08:00
return UpdateWithCurrentOpenAICmdInfoChat ( screenId , nil )
2024-01-11 17:34:23 -08:00
}
2024-02-15 16:45:47 -08:00
func UpdateWithAddNewOpenAICmdInfoPacket ( ctx context . Context , screenId string , pk * packet . OpenAICmdInfoChatMessage ) * scbus . ModelUpdatePacketType {
2024-01-11 17:34:23 -08:00
ScreenMemAddCmdInfoChatMessage ( screenId , pk )
2024-02-09 17:19:44 -08:00
return UpdateWithCurrentOpenAICmdInfoChat ( screenId , nil )
2024-01-11 17:34:23 -08:00
}
2024-02-15 16:45:47 -08:00
func UpdateWithCurrentOpenAICmdInfoChat ( screenId string , update * scbus . ModelUpdatePacketType ) * scbus . ModelUpdatePacketType {
if update == nil {
update = scbus . MakeUpdatePacket ()
}
update . AddUpdate ( OpenAICmdInfoChatUpdate ( ScreenMemGetCmdInfoChat ( screenId ). Messages ))
return update
2024-01-11 17:34:23 -08:00
}
2024-02-15 16:45:47 -08:00
func UpdateWithUpdateOpenAICmdInfoPacket ( ctx context . Context , screenId string , messageID int , pk * packet . OpenAICmdInfoChatMessage ) ( * scbus . ModelUpdatePacketType , error ) {
2024-01-11 17:34:23 -08:00
err := ScreenMemUpdateCmdInfoChatMessage ( screenId , messageID , pk )
if err != nil {
return nil , err
}
2024-02-15 16:45:47 -08:00
return UpdateWithCurrentOpenAICmdInfoChat ( screenId , nil ), nil
2024-01-11 17:34:23 -08:00
}
2024-01-26 16:25:21 -08:00
func UpdateCmdForRestart ( ctx context . Context , ck base . CommandKey , ts int64 , cmdPid int , remotePid int , termOpts * TermOpts ) error {
return WithTx ( ctx , func ( tx * TxWrap ) error {
query := `UPDATE cmd
SET restartts = ?, status = ?, exitcode = ?, cmdpid = ?, remotepid = ?, durationms = ?, termopts = ?, origtermopts = ?
WHERE screenid = ? AND lineid = ?`
tx . Exec ( query , ts , CmdStatusRunning , 0 , cmdPid , remotePid , 0 , quickJson ( termOpts ), quickJson ( termOpts ), ck . GetGroupId (), lineIdFromCK ( ck ))
query = `UPDATE history
SET ts = ?, status = ?, exitcode = ?, durationms = ?
WHERE screenid = ? AND lineid = ?`
tx . Exec ( query , ts , CmdStatusRunning , 0 , 0 , ck . GetGroupId (), lineIdFromCK ( ck ))
return nil
})
}
2024-05-02 14:16:00 -07:00
func UpdateCmdStartInfo ( ctx context . Context , ck base . CommandKey , cmdPid int , waveshellPid int ) error {
2024-04-04 15:08:45 -07:00
return WithTx ( ctx , func ( tx * TxWrap ) error {
query := `UPDATE cmd SET cmdpid = ?, remotepid = ? WHERE screenid = ? AND lineid = ?`
2024-05-02 14:16:00 -07:00
tx . Exec ( query , cmdPid , waveshellPid , ck . GetGroupId (), lineIdFromCK ( ck ))
2024-04-04 15:08:45 -07:00
return nil
})
}
type CmdDoneDataValues struct {
Ts int64
ExitCode int
DurationMs int64
}
func UpdateCmdDoneInfo ( ctx context . Context , update * scbus . ModelUpdatePacketType , ck base . CommandKey , donePk CmdDoneDataValues , status string ) error {
2022-11-28 18:03:02 -08:00
if ck . IsEmpty () {
2024-03-13 18:52:41 -07:00
return fmt . Errorf ( "cannot update cmddoneinfo, empty ck" )
2022-07-07 16:29:14 -07:00
}
2023-03-25 12:54:56 -07:00
screenId := ck . GetGroupId ()
2022-08-19 17:14:53 -07:00
var rtnCmd * CmdType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-12-27 13:11:53 -08:00
lineId := lineIdFromCK ( ck )
2023-07-30 17:16:43 -07:00
query := `UPDATE cmd SET status = ?, donets = ?, exitcode = ?, durationms = ? WHERE screenid = ? AND lineid = ?`
2023-12-27 13:11:53 -08:00
tx . Exec ( query , status , donePk . Ts , donePk . ExitCode , donePk . DurationMs , screenId , lineId )
query = `UPDATE history SET status = ?, exitcode = ?, durationms = ? WHERE screenid = ? AND lineid = ?`
tx . Exec ( query , status , donePk . ExitCode , donePk . DurationMs , screenId , lineId )
2022-08-19 17:14:53 -07:00
var err error
2023-12-27 13:11:53 -08:00
rtnCmd , err = GetCmdByScreenId ( tx . Context (), screenId , lineId )
2022-08-19 17:14:53 -07:00
if err != nil {
return err
}
2023-03-25 12:54:56 -07:00
if isWebShare ( tx , screenId ) {
2023-12-27 13:11:53 -08:00
insertScreenLineUpdate ( tx , screenId , lineId , UpdateType_CmdExitCode )
insertScreenLineUpdate ( tx , screenId , lineId , UpdateType_CmdDurationMs )
insertScreenLineUpdate ( tx , screenId , lineId , UpdateType_CmdStatus )
2023-03-25 12:54:56 -07:00
}
2022-07-07 16:29:14 -07:00
return nil
})
2022-08-19 17:14:53 -07:00
if txErr != nil {
2024-03-13 18:52:41 -07:00
return txErr
2022-08-19 17:14:53 -07:00
}
if rtnCmd == nil {
2024-03-13 18:52:41 -07:00
return fmt . Errorf ( "cmd data not found for ck[%s]" , ck )
2022-08-19 17:14:53 -07:00
}
2024-02-15 16:45:47 -08:00
update . AddUpdate ( * rtnCmd )
2024-01-17 13:07:01 -05:00
// Update in-memory screen indicator status
var indicator StatusIndicatorLevel
if rtnCmd . ExitCode == 0 {
indicator = StatusIndicatorLevel_Success
} else {
indicator = StatusIndicatorLevel_Error
}
2024-01-28 13:47:36 -08:00
err := SetStatusIndicatorLevel_Update ( ctx , update , screenId , indicator , false )
if err != nil {
// This is not a fatal error, so just log it
log . Printf ( "error setting status indicator level after done packet: %v\n" , err )
}
2024-04-02 18:47:54 -07:00
go IncrementNumRunningCmds ( screenId , - 1 )
2024-03-13 18:52:41 -07:00
return nil
2022-07-07 16:29:14 -07:00
}
2024-03-28 16:56:39 -07:00
func UpdateCmdRtnState ( ctx context . Context , ck base . CommandKey , statePtr packet . ShellStatePtr ) error {
2022-11-28 18:03:02 -08:00
if ck . IsEmpty () {
return fmt . Errorf ( "cannot update cmdrtnstate, empty ck" )
}
2023-03-25 12:54:56 -07:00
screenId := ck . GetGroupId ()
2023-07-30 17:16:43 -07:00
lineId := lineIdFromCK ( ck )
2022-11-28 18:03:02 -08:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-07-30 17:16:43 -07:00
query := `UPDATE cmd SET rtnbasehash = ?, rtndiffhasharr = ? WHERE screenid = ? AND lineid = ?`
tx . Exec ( query , statePtr . BaseHash , quickJsonArr ( statePtr . DiffHashArr ), screenId , lineId )
2023-03-25 12:54:56 -07:00
if isWebShare ( tx , screenId ) {
2023-07-30 17:16:43 -07:00
insertScreenLineUpdate ( tx , screenId , lineId , UpdateType_CmdRtnState )
2023-03-25 12:54:56 -07:00
}
2022-11-28 18:03:02 -08:00
return nil
})
if txErr != nil {
return txErr
}
return nil
}
2023-01-11 20:53:46 -08:00
func ReInitFocus ( ctx context . Context ) error {
return WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-13 02:09:29 -07:00
query := `UPDATE screen SET focustype = 'input'`
2023-02-14 16:17:54 -08:00
tx . Exec ( query )
2023-01-11 20:53:46 -08:00
return nil
})
}
2022-07-07 16:29:14 -07:00
func HangupAllRunningCmds ( ctx context . Context ) error {
return WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-26 13:21:58 -07:00
var cmdPtrs [] CmdPtr
2023-07-30 17:16:43 -07:00
query := `SELECT screenid, lineid FROM cmd WHERE status = ?`
2023-03-26 13:21:58 -07:00
tx . Select ( & cmdPtrs , query , CmdStatusRunning )
query = `UPDATE cmd SET status = ? WHERE status = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , CmdStatusHangup , CmdStatusRunning )
2023-03-26 13:21:58 -07:00
for _ , cmdPtr := range cmdPtrs {
2023-04-13 12:53:15 -07:00
if isWebShare ( tx , cmdPtr . ScreenId ) {
2023-07-30 17:16:43 -07:00
insertScreenLineUpdate ( tx , cmdPtr . ScreenId , cmdPtr . LineId , UpdateType_CmdStatus )
2023-04-13 12:53:15 -07:00
}
2023-12-27 13:11:53 -08:00
query = `UPDATE history SET status = ? WHERE screenid = ? AND lineid = ?`
tx . Exec ( query , CmdStatusHangup , cmdPtr . ScreenId , cmdPtr . LineId )
2023-03-26 13:21:58 -07:00
}
2022-07-07 16:29:14 -07:00
return nil
})
}
2023-04-13 12:53:15 -07:00
// TODO send update
func HangupRunningCmdsByRemoteId ( ctx context . Context , remoteId string ) ([] * ScreenType , error ) {
return WithTxRtn ( ctx , func ( tx * TxWrap ) ([] * ScreenType , error ) {
2023-03-26 13:21:58 -07:00
var cmdPtrs [] CmdPtr
2023-07-30 17:16:43 -07:00
query := `SELECT screenid, lineid FROM cmd WHERE status = ? AND remoteid = ?`
2023-03-26 13:21:58 -07:00
tx . Select ( & cmdPtrs , query , CmdStatusRunning , remoteId )
query = `UPDATE cmd SET status = ? WHERE status = ? AND remoteid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , CmdStatusHangup , CmdStatusRunning , remoteId )
2023-04-13 12:53:15 -07:00
var rtn [] * ScreenType
2023-03-26 13:21:58 -07:00
for _ , cmdPtr := range cmdPtrs {
2023-04-13 12:53:15 -07:00
if isWebShare ( tx , cmdPtr . ScreenId ) {
2023-07-30 17:16:43 -07:00
insertScreenLineUpdate ( tx , cmdPtr . ScreenId , cmdPtr . LineId , UpdateType_CmdStatus )
2023-04-13 12:53:15 -07:00
}
2023-12-27 13:11:53 -08:00
query = `UPDATE history SET status = ? WHERE screenid = ? AND lineid = ?`
tx . Exec ( query , CmdStatusHangup , cmdPtr . ScreenId , cmdPtr . LineId )
2023-07-30 17:16:43 -07:00
screen , err := UpdateScreenFocusForDoneCmd ( tx . Context (), cmdPtr . ScreenId , cmdPtr . LineId )
2023-04-13 12:53:15 -07:00
if err != nil {
return nil , err
}
2023-12-27 13:11:53 -08:00
// this doesn't add dups because UpdateScreenFocusForDoneCmd will only return a screen once
2023-04-13 12:53:15 -07:00
if screen != nil {
rtn = append ( rtn , screen )
}
2023-03-26 13:21:58 -07:00
}
2023-04-13 12:53:15 -07:00
return rtn , nil
2022-07-07 16:29:14 -07:00
})
}
2022-07-14 18:39:40 -07:00
2023-04-13 12:53:15 -07:00
// TODO send update
func HangupCmd ( ctx context . Context , ck base . CommandKey ) ( * ScreenType , error ) {
return WithTxRtn ( ctx , func ( tx * TxWrap ) ( * ScreenType , error ) {
2023-07-30 17:16:43 -07:00
query := `UPDATE cmd SET status = ? WHERE screenid = ? AND lineid = ?`
tx . Exec ( query , CmdStatusHangup , ck . GetGroupId (), lineIdFromCK ( ck ))
2023-12-27 13:11:53 -08:00
query = `UPDATE history SET status = ? WHERE screenid = ? AND lineid = ?`
tx . Exec ( query , CmdStatusHangup , ck . GetGroupId (), lineIdFromCK ( ck ))
2023-04-13 12:53:15 -07:00
if isWebShare ( tx , ck . GetGroupId ()) {
2023-07-30 17:16:43 -07:00
insertScreenLineUpdate ( tx , ck . GetGroupId (), lineIdFromCK ( ck ), UpdateType_CmdStatus )
2023-04-13 12:53:15 -07:00
}
2023-07-30 17:16:43 -07:00
screen , err := UpdateScreenFocusForDoneCmd ( tx . Context (), ck . GetGroupId (), lineIdFromCK ( ck ))
2023-04-13 12:53:15 -07:00
if err != nil {
return nil , err
}
return screen , nil
2022-11-27 14:12:15 -08:00
})
}
2022-07-15 01:57:45 -07:00
func getNextId ( ids [] string , delId string ) string {
if len ( ids ) == 0 {
return ""
}
if len ( ids ) == 1 {
if ids [ 0 ] == delId {
return ""
}
return ids [ 0 ]
}
for idx := 0 ; idx < len ( ids ); idx ++ {
if ids [ idx ] == delId {
var rtnIdx int
if idx == len ( ids ) - 1 {
rtnIdx = idx - 1
} else {
rtnIdx = idx + 1
}
return ids [ rtnIdx ]
}
}
return ids [ 0 ]
}
2024-02-15 16:45:47 -08:00
func SwitchScreenById ( ctx context . Context , sessionId string , screenId string ) ( * scbus . ModelUpdatePacketType , error ) {
2024-01-24 11:32:48 -08:00
SetActiveSessionId ( ctx , sessionId )
2022-07-14 18:39:40 -07:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-12-26 12:18:13 -08:00
query := `SELECT screenid FROM screen WHERE sessionid = ? AND screenid = ?`
2022-07-14 18:39:40 -07:00
if ! tx . Exists ( query , sessionId , screenId ) {
return fmt . Errorf ( "cannot switch to screen, screen=%s does not exist in session=%s" , screenId , sessionId )
}
query = `UPDATE session SET activescreenid = ? WHERE sessionid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , screenId , sessionId )
2022-07-14 18:39:40 -07:00
return nil
})
2022-12-26 16:09:21 -08:00
if txErr != nil {
return nil , txErr
}
bareSession , err := GetBareSessionById ( ctx , sessionId )
if err != nil {
return nil , err
}
2024-02-15 16:45:47 -08:00
update := scbus . MakeUpdatePacket ()
update . AddUpdate ( ActiveSessionIdUpdate ( sessionId ))
update . AddUpdate ( * bareSession )
2023-12-26 12:59:25 -08:00
memState := GetScreenMemState ( screenId )
if memState != nil {
2024-02-15 16:45:47 -08:00
update . AddUpdate ( CmdLineUpdate ( memState . CmdInputText ))
2024-02-09 17:19:44 -08:00
UpdateWithCurrentOpenAICmdInfoChat ( screenId , update )
2024-01-17 13:07:01 -05:00
// Clear any previous status indicator for this screen
2024-01-28 13:47:36 -08:00
err := ResetStatusIndicator_Update ( update , screenId )
if err != nil {
// This is not a fatal error, so just log it
log . Printf ( "error resetting status indicator when switching screens: %v\n" , err )
}
2023-12-26 12:59:25 -08:00
}
return update , nil
2022-07-14 18:39:40 -07:00
}
2022-07-15 01:57:45 -07:00
2023-03-20 19:20:57 -07:00
// screen may not exist at this point (so don't query screen table)
func cleanScreenCmds ( ctx context . Context , screenId string ) error {
2023-01-25 14:29:12 -08:00
var removedCmds [] string
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-07-30 17:16:43 -07:00
query := `SELECT lineid FROM cmd WHERE screenid = ? AND lineid NOT IN (SELECT lineid FROM line WHERE screenid = ?)`
2023-03-20 19:20:57 -07:00
removedCmds = tx . SelectStrings ( query , screenId , screenId )
2023-07-30 17:16:43 -07:00
query = `DELETE FROM cmd WHERE screenid = ? AND lineid NOT IN (SELECT lineid FROM line WHERE screenid = ?)`
2023-03-20 19:20:57 -07:00
tx . Exec ( query , screenId , screenId )
2023-01-02 12:09:01 -08:00
return nil
})
if txErr != nil {
return txErr
}
2023-07-30 17:16:43 -07:00
for _ , lineId := range removedCmds {
DeletePtyOutFile ( ctx , screenId , lineId )
2023-01-25 14:29:12 -08:00
}
2023-01-02 12:09:01 -08:00
return nil
}
2024-02-15 16:45:47 -08:00
func ArchiveScreen ( ctx context . Context , sessionId string , screenId string ) ( scbus . UpdatePacket , error ) {
2023-03-13 10:50:29 -07:00
var isActive bool
2022-12-23 15:56:29 -08:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT screenid FROM screen WHERE sessionid = ? AND screenid = ?`
if ! tx . Exists ( query , sessionId , screenId ) {
return fmt . Errorf ( "cannot close screen (not found)" )
}
2023-04-02 00:20:05 -07:00
if isWebShare ( tx , screenId ) {
return fmt . Errorf ( "cannot archive screen while web-sharing. stop web-sharing before trying to archive." )
}
2022-12-25 13:03:11 -08:00
query = `SELECT archived FROM screen WHERE sessionid = ? AND screenid = ?`
2022-12-23 15:56:29 -08:00
closeVal := tx . GetBool ( query , sessionId , screenId )
if closeVal {
return nil
}
2022-12-25 13:03:11 -08:00
query = `SELECT count(*) FROM screen WHERE sessionid = ? AND NOT archived`
2022-12-23 15:56:29 -08:00
numScreens := tx . GetInt ( query , sessionId )
if numScreens <= 1 {
2022-12-26 12:18:13 -08:00
return fmt . Errorf ( "cannot archive the last screen in a session" )
2022-12-23 15:56:29 -08:00
}
2022-12-25 13:21:48 -08:00
query = `UPDATE screen SET archived = 1, archivedts = ?, screenidx = 0 WHERE sessionid = ? AND screenid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , time . Now (). UnixMilli (), sessionId , screenId )
2023-03-13 10:50:29 -07:00
isActive = tx . Exists ( `SELECT sessionid FROM session WHERE sessionid = ? AND activescreenid = ?` , sessionId , screenId )
2022-12-23 15:56:29 -08:00
if isActive {
2022-12-25 13:03:11 -08:00
screenIds := tx . SelectStrings ( `SELECT screenid FROM screen WHERE sessionid = ? AND NOT archived ORDER BY screenidx` , sessionId )
2022-12-23 15:56:29 -08:00
nextId := getNextId ( screenIds , screenId )
2023-02-14 16:17:54 -08:00
tx . Exec ( `UPDATE session SET activescreenid = ? WHERE sessionid = ?` , nextId , sessionId )
2022-12-23 15:56:29 -08:00
}
return nil
})
if txErr != nil {
return nil , txErr
}
2023-03-13 10:50:29 -07:00
newScreen , err := GetScreenById ( ctx , screenId )
2022-12-26 16:09:21 -08:00
if err != nil {
2023-03-13 10:50:29 -07:00
return nil , fmt . Errorf ( "cannot retrive archived screen: %w" , err )
2022-12-26 16:09:21 -08:00
}
2024-02-15 16:45:47 -08:00
update := scbus . MakeUpdatePacket ()
update . AddUpdate ( * newScreen )
2023-03-13 10:50:29 -07:00
if isActive {
bareSession , err := GetBareSessionById ( ctx , sessionId )
if err != nil {
return nil , err
}
2024-02-15 16:45:47 -08:00
update . AddUpdate ( * bareSession )
2022-12-26 12:38:47 -08:00
}
2022-12-23 15:56:29 -08:00
return update , nil
}
2022-12-25 13:21:48 -08:00
func UnArchiveScreen ( ctx context . Context , sessionId string , screenId string ) error {
2022-12-23 15:56:29 -08:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-12-25 13:03:11 -08:00
query := `SELECT screenid FROM screen WHERE sessionid = ? AND screenid = ? AND archived`
2022-12-23 15:56:29 -08:00
if ! tx . Exists ( query , sessionId , screenId ) {
2022-12-25 13:03:11 -08:00
return fmt . Errorf ( "cannot re-open screen (not found or not archived)" )
2022-12-23 15:56:29 -08:00
}
2022-12-25 13:03:11 -08:00
maxScreenIdx := tx . GetInt ( `SELECT COALESCE(max(screenidx), 0) FROM screen WHERE sessionid = ? AND NOT archived` , sessionId )
2022-12-26 12:18:13 -08:00
query = `UPDATE screen SET archived = 0, screenidx = ? WHERE sessionid = ? AND screenid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , maxScreenIdx + 1 , sessionId , screenId )
2022-12-23 15:56:29 -08:00
return nil
})
return txErr
}
2023-12-27 13:11:53 -08:00
// if sessionDel is passed, we do *not* delete the screen directory (session delete will handle that)
2024-02-15 16:45:47 -08:00
func DeleteScreen ( ctx context . Context , screenId string , sessionDel bool , update * scbus . ModelUpdatePacketType ) ( * scbus . ModelUpdatePacketType , error ) {
2023-03-14 16:37:22 -07:00
var sessionId string
2023-03-13 10:50:29 -07:00
var isActive bool
2023-12-27 13:11:53 -08:00
var screenTombstone * ScreenTombstoneType
2022-07-15 01:57:45 -07:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-12-27 13:11:53 -08:00
screen , err := GetScreenById ( tx . Context (), screenId )
if err != nil {
return fmt . Errorf ( "cannot get screen to delete: %w" , err )
}
if screen == nil {
return fmt . Errorf ( "cannot delete screen (not found)" )
2022-12-25 13:03:11 -08:00
}
2023-04-17 15:22:30 -07:00
webSharing := isWebShare ( tx , screenId )
2023-03-20 19:20:57 -07:00
if ! sessionDel {
2023-12-27 13:11:53 -08:00
query := `SELECT sessionid FROM screen WHERE screenid = ?`
2023-03-20 19:20:57 -07:00
sessionId = tx . GetString ( query , screenId )
if sessionId == "" {
2023-12-27 13:11:53 -08:00
return fmt . Errorf ( "cannot delete screen (no sessionid)" )
2023-03-20 19:20:57 -07:00
}
isActive = tx . Exists ( `SELECT sessionid FROM session WHERE sessionid = ? AND activescreenid = ?` , sessionId , screenId )
if isActive {
screenIds := tx . SelectStrings ( `SELECT screenid FROM screen WHERE sessionid = ? AND NOT archived ORDER BY screenidx` , sessionId )
nextId := getNextId ( screenIds , screenId )
tx . Exec ( `UPDATE session SET activescreenid = ? WHERE sessionid = ?` , nextId , sessionId )
}
2022-07-15 01:57:45 -07:00
}
2023-12-27 13:11:53 -08:00
screenTombstone = & ScreenTombstoneType {
ScreenId : screen . ScreenId ,
SessionId : screen . SessionId ,
Name : screen . Name ,
DeletedTs : time . Now (). UnixMilli (),
ScreenOpts : screen . ScreenOpts ,
}
query := `INSERT INTO screen_tombstone ( screenid, sessionid, name, deletedts, screenopts)
VALUES (:screenid,:sessionid,:name,:deletedts,:screenopts)`
tx . NamedExec ( query , dbutil . ToDBMap ( screenTombstone , false ))
2023-03-13 02:09:29 -07:00
query = `DELETE FROM screen WHERE screenid = ?`
tx . Exec ( query , screenId )
2023-03-20 19:20:57 -07:00
query = `DELETE FROM line WHERE screenid = ?`
tx . Exec ( query , screenId )
2023-04-07 15:48:44 -07:00
query = `DELETE FROM cmd WHERE screenid = ?`
tx . Exec ( query , screenId )
2023-12-27 13:11:53 -08:00
query = `UPDATE history SET lineid = '', linenum = 0 WHERE screenid = ?`
tx . Exec ( query , screenId )
2023-04-17 15:22:30 -07:00
if webSharing {
insertScreenDelUpdate ( tx , screenId )
}
2022-07-15 01:57:45 -07:00
return nil
})
if txErr != nil {
2022-07-15 17:53:23 -07:00
return nil , txErr
2022-07-15 01:57:45 -07:00
}
2023-12-27 13:11:53 -08:00
if ! sessionDel {
GoDeleteScreenDirs ( screenId )
2023-04-07 15:48:44 -07:00
}
2024-02-09 17:19:44 -08:00
if update == nil {
2024-02-15 16:45:47 -08:00
update = scbus . MakeUpdatePacket ()
2024-02-09 17:19:44 -08:00
}
2024-02-15 16:45:47 -08:00
update . AddUpdate ( * screenTombstone )
update . AddUpdate ( ScreenType { SessionId : sessionId , ScreenId : screenId , Remove : true })
2023-03-13 10:50:29 -07:00
if isActive {
2023-03-14 16:37:22 -07:00
bareSession , err := GetBareSessionById ( ctx , sessionId )
2023-03-13 10:50:29 -07:00
if err != nil {
return nil , err
}
2024-02-15 16:45:47 -08:00
update . AddUpdate ( * bareSession )
2022-12-26 16:09:21 -08:00
}
2022-12-28 13:56:19 -08:00
return update , nil
2022-07-15 01:57:45 -07:00
}
2022-07-15 17:37:32 -07:00
2024-03-28 16:56:39 -07:00
func GetRemoteState ( ctx context . Context , sessionId string , screenId string , remotePtr RemotePtrType ) ( * packet . ShellState , * packet . ShellStatePtr , error ) {
2023-03-14 16:37:22 -07:00
ssptr , err := GetRemoteStatePtr ( ctx , sessionId , screenId , remotePtr )
2022-11-28 18:03:02 -08:00
if err != nil {
return nil , nil , err
}
if ssptr == nil {
return nil , nil , nil
}
state , err := GetFullState ( ctx , * ssptr )
if err != nil {
return nil , nil , err
}
return state , ssptr , err
}
2024-03-28 16:56:39 -07:00
func GetRemoteStatePtr ( ctx context . Context , sessionId string , screenId string , remotePtr RemotePtrType ) ( * packet . ShellStatePtr , error ) {
var ssptr * packet . ShellStatePtr
2022-07-15 17:37:32 -07:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-14 16:37:22 -07:00
ri , err := GetRemoteInstance ( tx . Context (), sessionId , screenId , remotePtr )
2022-11-28 00:13:00 -08:00
if err != nil {
return err
}
if ri == nil {
2022-07-15 17:37:32 -07:00
return nil
}
2024-03-28 16:56:39 -07:00
ssptr = & packet . ShellStatePtr { ri . StateBaseHash , ri . StateDiffHashArr }
2022-07-15 17:37:32 -07:00
return nil
})
2022-11-28 00:13:00 -08:00
if txErr != nil {
return nil , txErr
}
2022-11-28 18:03:02 -08:00
return ssptr , nil
2022-07-15 17:37:32 -07:00
}
2022-08-09 14:24:57 -07:00
2023-03-14 16:37:22 -07:00
func validateSessionScreen ( tx * TxWrap , sessionId string , screenId string ) error {
if screenId == "" {
2022-08-24 02:14:16 -07:00
query := `SELECT sessionid FROM session WHERE sessionid = ?`
if ! tx . Exists ( query , sessionId ) {
return fmt . Errorf ( "no session found" )
}
return nil
} else {
2023-03-14 16:37:22 -07:00
query := `SELECT screenid FROM screen WHERE sessionid = ? AND screenid = ?`
if ! tx . Exists ( query , sessionId , screenId ) {
2023-03-13 02:09:29 -07:00
return fmt . Errorf ( "no screen found" )
2022-08-09 14:24:57 -07:00
}
2022-08-24 02:14:16 -07:00
return nil
}
}
2023-03-14 16:37:22 -07:00
func GetRemoteInstance ( ctx context . Context , sessionId string , screenId string , remotePtr RemotePtrType ) ( * RemoteInstance , error ) {
2022-11-28 00:13:00 -08:00
if remotePtr . IsSessionScope () {
2023-03-14 16:37:22 -07:00
screenId = ""
2022-11-28 00:13:00 -08:00
}
var ri * RemoteInstance
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-14 16:37:22 -07:00
query := `SELECT * FROM remote_instance WHERE sessionid = ? AND screenid = ? AND remoteownerid = ? AND remoteid = ? AND name = ?`
2023-03-27 14:11:02 -07:00
ri = dbutil . GetMapGen [ * RemoteInstance ]( tx , query , sessionId , screenId , remotePtr . OwnerId , remotePtr . RemoteId , remotePtr . Name )
2022-11-28 00:13:00 -08:00
return nil
})
if txErr != nil {
return nil , txErr
}
return ri , nil
}
2024-01-16 16:11:04 -08:00
// internal function for UpdateRemoteState (sets StateBaseHash, StateDiffHashArr, and ShellType)
2022-11-28 00:13:00 -08:00
func updateRIWithState ( ctx context . Context , ri * RemoteInstance , stateBase * packet . ShellState , stateDiff * packet . ShellStateDiff ) error {
if stateBase != nil {
ri . StateBaseHash = stateBase . GetHashVal ( false )
2022-11-28 18:03:02 -08:00
ri . StateDiffHashArr = nil
2024-01-16 16:11:04 -08:00
ri . ShellType = stateBase . GetShellType ()
2022-11-28 00:13:00 -08:00
err := StoreStateBase ( ctx , stateBase )
if err != nil {
return err
}
} else if stateDiff != nil {
ri . StateBaseHash = stateDiff . BaseHash
ri . StateDiffHashArr = append ( stateDiff . DiffHashArr , stateDiff . GetHashVal ( false ))
2024-01-16 16:11:04 -08:00
ri . ShellType = stateDiff . GetShellType ()
2022-11-28 00:13:00 -08:00
err := StoreStateDiff ( ctx , stateDiff )
if err != nil {
return err
}
}
return nil
}
2023-03-14 16:37:22 -07:00
func UpdateRemoteState ( ctx context . Context , sessionId string , screenId string , remotePtr RemotePtrType , feState FeStateType , stateBase * packet . ShellState , stateDiff * packet . ShellStateDiff ) ( * RemoteInstance , error ) {
2022-11-28 00:13:00 -08:00
if stateBase == nil && stateDiff == nil {
return nil , fmt . Errorf ( "UpdateRemoteState, must set state or diff" )
}
if stateBase != nil && stateDiff != nil {
return nil , fmt . Errorf ( "UpdateRemoteState, cannot set state and diff" )
}
2022-08-24 02:14:16 -07:00
if remotePtr . IsSessionScope () {
2023-03-14 16:37:22 -07:00
screenId = ""
2022-08-24 02:14:16 -07:00
}
2022-10-16 23:51:04 -07:00
var ri * RemoteInstance
2022-08-24 02:14:16 -07:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-14 16:37:22 -07:00
err := validateSessionScreen ( tx , sessionId , screenId )
2022-08-24 02:14:16 -07:00
if err != nil {
2022-11-28 00:13:00 -08:00
return fmt . Errorf ( "cannot update remote instance state: %w" , err )
2022-08-24 02:14:16 -07:00
}
2023-03-14 16:37:22 -07:00
query := `SELECT * FROM remote_instance WHERE sessionid = ? AND screenid = ? AND remoteownerid = ? AND remoteid = ? AND name = ?`
2023-03-27 14:11:02 -07:00
ri = dbutil . GetMapGen [ * RemoteInstance ]( tx , query , sessionId , screenId , remotePtr . OwnerId , remotePtr . RemoteId , remotePtr . Name )
2022-10-16 23:51:04 -07:00
if ri == nil {
ri = & RemoteInstance {
2023-11-01 01:26:19 -07:00
RIId : scbase . GenWaveUUID (),
2022-08-24 13:21:54 -07:00
Name : remotePtr . Name ,
SessionId : sessionId ,
2023-03-14 16:37:22 -07:00
ScreenId : screenId ,
2022-08-24 13:21:54 -07:00
RemoteOwnerId : remotePtr . OwnerId ,
RemoteId : remotePtr . RemoteId ,
2022-11-28 00:13:00 -08:00
FeState : feState ,
2022-08-09 14:24:57 -07:00
}
2022-11-28 00:13:00 -08:00
err = updateRIWithState ( tx . Context (), ri , stateBase , stateDiff )
if err != nil {
return err
}
2024-01-16 16:11:04 -08:00
query = `INSERT INTO remote_instance ( riid, name, sessionid, screenid, remoteownerid, remoteid, festate, statebasehash, statediffhasharr, shelltype)
VALUES (:riid,:name,:sessionid,:screenid,:remoteownerid,:remoteid,:festate,:statebasehash,:statediffhasharr,:shelltype)`
2023-02-14 16:17:54 -08:00
tx . NamedExec ( query , ri . ToMap ())
2022-08-09 14:24:57 -07:00
return nil
2022-11-28 00:13:00 -08:00
} else {
2024-01-16 16:11:04 -08:00
query = `UPDATE remote_instance SET festate = ?, statebasehash = ?, statediffhasharr = ?, shelltype = ? WHERE riid = ?`
2022-11-28 00:13:00 -08:00
ri . FeState = feState
err = updateRIWithState ( tx . Context (), ri , stateBase , stateDiff )
if err != nil {
return err
}
2024-01-16 16:11:04 -08:00
tx . Exec ( query , quickJson ( ri . FeState ), ri . StateBaseHash , quickJsonArr ( ri . StateDiffHashArr ), ri . ShellType , ri . RIId )
2022-11-28 00:13:00 -08:00
return nil
2022-08-09 14:24:57 -07:00
}
})
2022-10-16 23:51:04 -07:00
return ri , txErr
2022-08-09 14:24:57 -07:00
}
2022-08-17 12:24:09 -07:00
2023-03-13 01:52:30 -07:00
func UpdateCurRemote ( ctx context . Context , screenId string , remotePtr RemotePtrType ) error {
return WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT screenid FROM screen WHERE screenid = ?`
if ! tx . Exists ( query , screenId ) {
return fmt . Errorf ( "cannot update curremote: no screen found" )
2022-08-17 12:24:09 -07:00
}
2023-03-13 01:52:30 -07:00
query = `UPDATE screen SET curremoteownerid = ?, curremoteid = ?, curremotename = ? WHERE screenid = ?`
tx . Exec ( query , remotePtr . OwnerId , remotePtr . RemoteId , remotePtr . Name , screenId )
2022-08-17 12:24:09 -07:00
return nil
})
}
2022-08-26 13:12:17 -07:00
func reorderStrings ( strs [] string , toMove string , newIndex int ) [] string {
if toMove == "" {
return strs
}
var newStrs [] string
if newIndex < 0 {
newStrs = append ( newStrs , toMove )
}
for _ , sval := range strs {
if len ( newStrs ) == newIndex {
newStrs = append ( newStrs , toMove )
}
if sval != toMove {
newStrs = append ( newStrs , sval )
}
}
if newIndex >= len ( newStrs ) {
newStrs = append ( newStrs , toMove )
}
return newStrs
}
func ReIndexSessions ( ctx context . Context , sessionId string , newIndex int ) error {
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-12-26 16:09:21 -08:00
query := `SELECT sessionid FROM session WHERE NOT archived ORDER BY sessionidx, name, sessionid`
2022-08-26 13:12:17 -07:00
ids := tx . SelectStrings ( query )
if sessionId != "" {
ids = reorderStrings ( ids , sessionId , newIndex )
}
2022-08-26 16:21:19 -07:00
query = `UPDATE session SET sessionid = ? WHERE sessionid = ?`
2022-08-26 13:12:17 -07:00
for idx , id := range ids {
2023-02-14 16:17:54 -08:00
tx . Exec ( query , id , idx + 1 )
2022-08-26 13:12:17 -07:00
}
return nil
})
return txErr
}
func SetSessionName ( ctx context . Context , sessionId string , name string ) error {
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-08-26 16:21:19 -07:00
query := `SELECT sessionid FROM session WHERE sessionid = ?`
2022-08-26 13:12:17 -07:00
if ! tx . Exists ( query , sessionId ) {
return fmt . Errorf ( "session does not exist" )
}
2022-12-26 16:09:21 -08:00
query = `SELECT archived FROM session WHERE sessionid = ?`
isArchived := tx . GetBool ( query , sessionId )
if ! isArchived {
query = `SELECT sessionid FROM session WHERE name = ? AND NOT archived`
dupSessionId := tx . GetString ( query , name )
if dupSessionId == sessionId {
return nil
}
if dupSessionId != "" {
return fmt . Errorf ( "invalid duplicate session name '%s'" , name )
}
2022-08-26 17:17:33 -07:00
}
2022-08-26 16:21:19 -07:00
query = `UPDATE session SET name = ? WHERE sessionid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , name , sessionId )
2022-08-26 13:12:17 -07:00
return nil
})
return txErr
}
2022-08-26 17:51:28 -07:00
func SetScreenName ( ctx context . Context , sessionId string , screenId string , name string ) error {
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT screenid FROM screen WHERE sessionid = ? AND screenid = ?`
if ! tx . Exists ( query , sessionId , screenId ) {
return fmt . Errorf ( "screen does not exist" )
}
query = `UPDATE screen SET name = ? WHERE sessionid = ? AND screenid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , name , sessionId , screenId )
2022-08-26 17:51:28 -07:00
return nil
})
return txErr
}
2022-08-26 21:44:18 -07:00
2024-02-15 16:45:47 -08:00
func ArchiveScreenLines ( ctx context . Context , screenId string ) ( * scbus . ModelUpdatePacketType , error ) {
2022-08-26 21:44:18 -07:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-20 19:20:57 -07:00
query := `SELECT screenid FROM screen WHERE screenid = ?`
if ! tx . Exists ( query , screenId ) {
return fmt . Errorf ( "screen does not exist" )
2023-01-02 12:09:01 -08:00
}
2023-11-29 18:29:44 -08:00
query = `UPDATE line SET archived = 1
WHERE line.archived = 0 AND line.screenid = ? AND NOT EXISTS (SELECT * FROM cmd c
WHERE line.screenid = c.screenid AND line.lineid = c.lineid AND c.status IN ('running', 'detached'))`
2023-03-20 19:20:57 -07:00
tx . Exec ( query , screenId )
2023-01-02 12:09:01 -08:00
return nil
})
if txErr != nil {
return nil , txErr
}
2023-03-13 10:50:29 -07:00
screenLines , err := GetScreenLinesById ( ctx , screenId )
2023-01-02 12:09:01 -08:00
if err != nil {
return nil , err
}
2024-02-15 16:45:47 -08:00
ret := scbus . MakeUpdatePacket ()
ret . AddUpdate ( * screenLines )
2024-02-09 17:19:44 -08:00
return ret , nil
2023-01-02 12:09:01 -08:00
}
2024-02-15 16:45:47 -08:00
func DeleteScreenLines ( ctx context . Context , screenId string ) ( * scbus . ModelUpdatePacketType , error ) {
2022-08-26 22:01:29 -07:00
var lineIds [] string
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2024-01-26 16:25:21 -08:00
query := `SELECT lineid FROM line
WHERE screenid = ?
AND NOT EXISTS (SELECT lineid FROM cmd c WHERE c.screenid = ? AND c.lineid = line.lineid AND c.status IN ('running', 'detached'))`
lineIds = tx . SelectStrings ( query , screenId , screenId )
query = `DELETE FROM line
WHERE screenid = ? AND lineid IN (SELECT value FROM json_each(?))`
tx . Exec ( query , screenId , quickJsonArr ( lineIds ))
query = `UPDATE history SET lineid = '', linenum = 0
WHERE screenid = ? AND lineid IN (SELECT value FROM json_each(?))`
tx . Exec ( query , screenId , quickJsonArr ( lineIds ))
2022-08-26 22:01:29 -07:00
return nil
})
if txErr != nil {
return nil , txErr
}
2023-12-27 13:11:53 -08:00
go func () {
cleanCtx , cancelFn := context . WithTimeout ( context . Background (), time . Minute )
defer cancelFn ()
cleanScreenCmds ( cleanCtx , screenId )
}()
2023-03-13 10:50:29 -07:00
screen , err := GetScreenById ( ctx , screenId )
if err != nil {
return nil , err
}
screenLines , err := GetScreenLinesById ( ctx , screenId )
2022-08-26 22:01:29 -07:00
if err != nil {
return nil , err
}
for _ , lineId := range lineIds {
line := & LineType {
2023-03-20 19:20:57 -07:00
ScreenId : screenId ,
LineId : lineId ,
Remove : true ,
2022-08-26 22:01:29 -07:00
}
2023-03-13 10:50:29 -07:00
screenLines . Lines = append ( screenLines . Lines , line )
2022-08-26 22:01:29 -07:00
}
2024-02-15 16:45:47 -08:00
ret := scbus . MakeUpdatePacket ()
ret . AddUpdate ( * screen )
ret . AddUpdate ( * screenLines )
2024-02-09 17:19:44 -08:00
return ret , nil
2022-08-26 22:01:29 -07:00
}
2022-09-05 20:08:59 -07:00
2023-03-20 19:20:57 -07:00
func GetRunningScreenCmds ( ctx context . Context , screenId string ) ([] * CmdType , error ) {
2022-09-05 20:08:59 -07:00
var rtn [] * CmdType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-07-30 17:16:43 -07:00
query := `SELECT * FROM cmd WHERE screenid = ? AND status = ?`
2023-03-27 14:11:02 -07:00
rtn = dbutil . SelectMapsGen [ * CmdType ]( tx , query , screenId , CmdStatusRunning )
2022-09-05 20:08:59 -07:00
return nil
})
if txErr != nil {
return nil , txErr
}
return rtn , nil
}
2023-07-30 17:16:43 -07:00
func UpdateCmdTermOpts ( ctx context . Context , screenId string , lineId string , termOpts TermOpts ) error {
2022-09-05 20:08:59 -07:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-07-30 17:16:43 -07:00
query := `UPDATE cmd SET termopts = ? WHERE screenid = ? AND lineid = ?`
tx . Exec ( query , termOpts , screenId , lineId )
insertScreenLineUpdate ( tx , screenId , lineId , UpdateType_CmdTermOpts )
2022-09-05 20:08:59 -07:00
return nil
})
return txErr
}
2022-09-13 12:06:12 -07:00
2023-01-31 17:56:56 -08:00
// returns riids of deleted RIs
2023-03-13 01:52:30 -07:00
func ScreenReset ( ctx context . Context , screenId string ) ([] * RemoteInstance , error ) {
return WithTxRtn ( ctx , func ( tx * TxWrap ) ([] * RemoteInstance , error ) {
2023-03-14 16:37:22 -07:00
query := `SELECT sessionid FROM screen WHERE screenid = ?`
sessionId := tx . GetString ( query , screenId )
if sessionId == "" {
2023-03-13 01:52:30 -07:00
return nil , fmt . Errorf ( "screen does not exist" )
2023-01-31 17:56:56 -08:00
}
2023-03-14 16:37:22 -07:00
query = `SELECT riid FROM remote_instance WHERE sessionid = ? AND screenid = ?`
riids := tx . SelectStrings ( query , sessionId , screenId )
2023-03-13 01:52:30 -07:00
var delRis [] * RemoteInstance
2023-01-31 17:56:56 -08:00
for _ , riid := range riids {
2023-03-14 16:37:22 -07:00
ri := & RemoteInstance { SessionId : sessionId , ScreenId : screenId , RIId : riid , Remove : true }
2023-01-31 17:56:56 -08:00
delRis = append ( delRis , ri )
}
2023-03-14 16:37:22 -07:00
query = `DELETE FROM remote_instance WHERE sessionid = ? AND screenid = ?`
tx . Exec ( query , sessionId , screenId )
2023-03-13 01:52:30 -07:00
return delRis , nil
2023-01-31 17:56:56 -08:00
})
}
2024-02-15 16:45:47 -08:00
func DeleteSession ( ctx context . Context , sessionId string ) ( scbus . UpdatePacket , error ) {
2022-12-26 16:09:21 -08:00
var newActiveSessionId string
2023-03-20 19:20:57 -07:00
var screenIds [] string
2023-12-27 13:11:53 -08:00
var sessionTombstone * SessionTombstoneType
2024-02-15 16:45:47 -08:00
update := scbus . MakeUpdatePacket ()
2022-12-26 16:09:21 -08:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-12-27 13:11:53 -08:00
bareSession , err := GetBareSessionById ( tx . Context (), sessionId )
if err != nil {
return fmt . Errorf ( "cannot get session to delete: %w" , err )
2022-12-26 16:09:21 -08:00
}
2023-12-27 13:11:53 -08:00
if bareSession == nil {
return fmt . Errorf ( "cannot delete session (not found)" )
}
query := `SELECT screenid FROM screen WHERE sessionid = ?`
2023-03-20 19:20:57 -07:00
screenIds = tx . SelectStrings ( query , sessionId )
for _ , screenId := range screenIds {
2024-02-09 17:19:44 -08:00
_ , err := DeleteScreen ( tx . Context (), screenId , true , update )
2023-03-20 19:20:57 -07:00
if err != nil {
2023-12-27 13:11:53 -08:00
return fmt . Errorf ( "error deleting screen[%s]: %v" , screenId , err )
}
2023-03-20 19:20:57 -07:00
}
2022-12-26 16:09:21 -08:00
query = `DELETE FROM session WHERE sessionid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , sessionId )
2022-12-26 16:09:21 -08:00
newActiveSessionId , _ = fixActiveSessionId ( tx . Context ())
2023-12-27 13:11:53 -08:00
sessionTombstone = & SessionTombstoneType {
SessionId : sessionId ,
Name : bareSession . Name ,
DeletedTs : time . Now (). UnixMilli (),
}
query = `INSERT INTO session_tombstone ( sessionid, name, deletedts)
VALUES (:sessionid,:name,:deletedts)`
tx . NamedExec ( query , dbutil . ToDBMap ( sessionTombstone , false ))
2022-12-26 16:09:21 -08:00
return nil
})
if txErr != nil {
return nil , txErr
}
2023-12-27 13:11:53 -08:00
GoDeleteScreenDirs ( screenIds ... )
2022-12-26 16:09:21 -08:00
if newActiveSessionId != "" {
2024-02-15 16:45:47 -08:00
update . AddUpdate ( ActiveSessionIdUpdate ( newActiveSessionId ))
2024-02-09 17:19:44 -08:00
}
2024-02-15 16:45:47 -08:00
update . AddUpdate ( SessionType { SessionId : sessionId , Remove : true })
2024-02-09 17:19:44 -08:00
if sessionTombstone != nil {
2024-02-15 16:45:47 -08:00
update . AddUpdate ( * sessionTombstone )
2022-12-26 16:09:21 -08:00
}
return update , nil
}
func fixActiveSessionId ( ctx context . Context ) ( string , error ) {
var newActiveSessionId string
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
curActiveSessionId := tx . GetString ( "SELECT activesessionid FROM client" )
query := `SELECT sessionid FROM session WHERE sessionid = ? AND NOT archived`
if tx . Exists ( query , curActiveSessionId ) {
return nil
}
var err error
newActiveSessionId , err = GetFirstSessionId ( tx . Context ())
if err != nil {
return err
}
2023-02-14 16:17:54 -08:00
tx . Exec ( "UPDATE client SET activesessionid = ?" , newActiveSessionId )
2022-12-26 16:09:21 -08:00
return nil
})
if txErr != nil {
return "" , txErr
}
return newActiveSessionId , nil
}
2024-02-15 16:45:47 -08:00
func ArchiveSession ( ctx context . Context , sessionId string ) ( * scbus . ModelUpdatePacketType , error ) {
2022-12-26 16:09:21 -08:00
if sessionId == "" {
return nil , fmt . Errorf ( "invalid blank sessionid" )
}
var newActiveSessionId string
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT sessionid FROM session WHERE sessionid = ?`
if ! tx . Exists ( query , sessionId ) {
return fmt . Errorf ( "session does not exist" )
}
query = `SELECT archived FROM session WHERE sessionid = ?`
isArchived := tx . GetBool ( query , sessionId )
if isArchived {
return nil
}
query = `UPDATE session SET archived = 1, archivedts = ? WHERE sessionid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , time . Now (). UnixMilli (), sessionId )
2022-12-26 16:09:21 -08:00
newActiveSessionId , _ = fixActiveSessionId ( tx . Context ())
return nil
})
if txErr != nil {
return nil , txErr
}
bareSession , _ := GetBareSessionById ( ctx , sessionId )
2024-02-15 16:45:47 -08:00
update := scbus . MakeUpdatePacket ()
2022-12-26 16:09:21 -08:00
if bareSession != nil {
2024-02-15 16:45:47 -08:00
update . AddUpdate ( * bareSession )
2022-12-26 16:09:21 -08:00
}
if newActiveSessionId != "" {
2024-02-15 16:45:47 -08:00
update . AddUpdate ( ActiveSessionIdUpdate ( newActiveSessionId ))
2022-12-26 16:09:21 -08:00
}
return update , nil
}
2024-02-15 16:45:47 -08:00
func UnArchiveSession ( ctx context . Context , sessionId string , activate bool ) ( * scbus . ModelUpdatePacketType , error ) {
2022-12-26 18:42:55 -08:00
if sessionId == "" {
return nil , fmt . Errorf ( "invalid blank sessionid" )
}
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT sessionid FROM session WHERE sessionid = ?`
if ! tx . Exists ( query , sessionId ) {
return fmt . Errorf ( "session does not exist" )
}
query = `SELECT archived FROM session WHERE sessionid = ?`
isArchived := tx . GetBool ( query , sessionId )
if ! isArchived {
return nil
}
query = `UPDATE session SET archived = 0, archivedts = 0 WHERE sessionid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , sessionId )
2022-12-26 18:42:55 -08:00
if activate {
query = `UPDATE client SET activesessionid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , sessionId )
2022-12-26 18:42:55 -08:00
}
return nil
})
if txErr != nil {
return nil , txErr
}
bareSession , _ := GetBareSessionById ( ctx , sessionId )
2024-02-15 16:45:47 -08:00
update := scbus . MakeUpdatePacket ()
2024-02-09 17:19:44 -08:00
2022-12-26 18:42:55 -08:00
if bareSession != nil {
2024-02-15 16:45:47 -08:00
update . AddUpdate ( * bareSession )
2022-12-26 18:42:55 -08:00
}
if activate {
2024-02-15 16:45:47 -08:00
update . AddUpdate ( ActiveSessionIdUpdate ( sessionId ))
2022-12-26 18:42:55 -08:00
}
return update , nil
2022-09-13 12:06:12 -07:00
}
2022-09-20 14:15:20 -07:00
func GetSessionStats ( ctx context . Context , sessionId string ) ( * SessionStatsType , error ) {
rtn := & SessionStatsType { SessionId : sessionId }
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT sessionid FROM session WHERE sessionid = ?`
if ! tx . Exists ( query , sessionId ) {
return fmt . Errorf ( "not found" )
}
2022-12-25 13:03:11 -08:00
query = `SELECT count(*) FROM screen WHERE sessionid = ? AND NOT archived`
2022-09-20 14:15:20 -07:00
rtn . NumScreens = tx . GetInt ( query , sessionId )
2022-12-25 13:03:11 -08:00
query = `SELECT count(*) FROM screen WHERE sessionid = ? AND archived`
rtn . NumArchivedScreens = tx . GetInt ( query , sessionId )
2023-04-05 00:46:47 -07:00
query = `SELECT count(*) FROM line WHERE screenid IN (SELECT screenid FROM screen WHERE sessionid = ?)`
2022-09-20 14:15:20 -07:00
rtn . NumLines = tx . GetInt ( query , sessionId )
2023-04-05 00:46:47 -07:00
query = `SELECT count(*) FROM cmd WHERE screenid IN (SELECT screenid FROM screen WHERE sessionid = ?)`
2022-09-20 14:15:20 -07:00
rtn . NumCmds = tx . GetInt ( query , sessionId )
return nil
})
if txErr != nil {
return nil , txErr
}
diskSize , err := SessionDiskSize ( sessionId )
if err != nil {
return nil , err
}
rtn . DiskStats = diskSize
return rtn , nil
}
2022-10-02 18:52:55 -07:00
const (
RemoteField_Alias = "alias" // string
RemoteField_ConnectMode = "connectmode" // string
RemoteField_SSHKey = "sshkey" // string
RemoteField_SSHPassword = "sshpassword" // string
RemoteField_Color = "color" // string
2024-01-16 16:11:04 -08:00
RemoteField_ShellPref = "shellpref" // string
2022-10-02 18:52:55 -07:00
)
// editMap: alias, connectmode, autoinstall, sshkey, color, sshpassword (from constants)
2024-01-16 16:11:04 -08:00
// note that all validation should have already happened outside of this function
2022-10-02 18:52:55 -07:00
func UpdateRemote ( ctx context . Context , remoteId string , editMap map [ string ] interface {}) ( * RemoteType , error ) {
var rtn * RemoteType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT remoteid FROM remote WHERE remoteid = ?`
if ! tx . Exists ( query , remoteId ) {
return fmt . Errorf ( "remote not found" )
}
if alias , found := editMap [ RemoteField_Alias ]; found {
query = `SELECT remoteid FROM remote WHERE remotealias = ? AND remoteid <> ?`
2022-10-03 19:04:48 -07:00
if alias != "" && tx . Exists ( query , alias , remoteId ) {
2022-10-02 18:52:55 -07:00
return fmt . Errorf ( "remote has duplicate alias, cannot update" )
}
query = `UPDATE remote SET remotealias = ? WHERE remoteid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , alias , remoteId )
2022-10-02 18:52:55 -07:00
}
if mode , found := editMap [ RemoteField_ConnectMode ]; found {
query = `UPDATE remote SET connectmode = ? WHERE remoteid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , mode , remoteId )
2022-10-02 18:52:55 -07:00
}
if sshKey , found := editMap [ RemoteField_SSHKey ]; found {
query = `UPDATE remote SET sshopts = json_set(sshopts, '$.sshidentity', ?) WHERE remoteid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , sshKey , remoteId )
2022-10-02 18:52:55 -07:00
}
if sshPassword , found := editMap [ RemoteField_SSHPassword ]; found {
query = `UPDATE remote SET sshopts = json_set(sshopts, '$.sshpassword', ?) WHERE remoteid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , sshPassword , remoteId )
2022-10-02 18:52:55 -07:00
}
2024-01-16 16:11:04 -08:00
if shellPref , found := editMap [ RemoteField_ShellPref ]; found {
query = `UPDATE remote SET shellpref = ? WHERE remoteid = ?`
tx . Exec ( query , shellPref , remoteId )
}
2022-10-02 18:52:55 -07:00
if color , found := editMap [ RemoteField_Color ]; found {
query = `UPDATE remote SET remoteopts = json_set(remoteopts, '$.color', ?) WHERE remoteid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , color , remoteId )
2022-10-02 18:52:55 -07:00
}
var err error
rtn , err = GetRemoteById ( tx . Context (), remoteId )
if err != nil {
return err
}
return nil
})
if txErr != nil {
return nil , txErr
}
return rtn , nil
}
2022-10-06 18:33:54 -07:00
const (
2023-03-13 01:52:30 -07:00
ScreenField_AnchorLine = "anchorline" // int
ScreenField_AnchorOffset = "anchoroffset" // int
ScreenField_SelectedLine = "selectedline" // int
ScreenField_Focus = "focustype" // string
ScreenField_TabColor = "tabcolor" // string
2023-11-29 18:29:44 -08:00
ScreenField_TabIcon = "tabicon" // string
2023-03-13 01:52:30 -07:00
ScreenField_PTerm = "pterm" // string
ScreenField_Name = "name" // string
2023-04-04 22:28:52 -07:00
ScreenField_ShareName = "sharename" // string
2022-10-06 18:33:54 -07:00
)
2023-03-13 01:52:30 -07:00
func UpdateScreen ( ctx context . Context , screenId string , editMap map [ string ] interface {}) ( * ScreenType , error ) {
2022-10-06 18:33:54 -07:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-13 01:52:30 -07:00
query := `SELECT screenid FROM screen WHERE screenid = ?`
if ! tx . Exists ( query , screenId ) {
return fmt . Errorf ( "screen not found" )
2022-10-06 18:33:54 -07:00
}
2023-03-13 01:52:30 -07:00
if anchorLine , found := editMap [ ScreenField_AnchorLine ]; found {
query = `UPDATE screen SET anchor = json_set(anchor, '$.anchorline', ?) WHERE screenid = ?`
tx . Exec ( query , anchorLine , screenId )
2022-10-11 01:11:04 -07:00
}
2023-03-13 01:52:30 -07:00
if anchorOffset , found := editMap [ ScreenField_AnchorOffset ]; found {
query = `UPDATE screen SET anchor = json_set(anchor, '$.anchoroffset', ?) WHERE screenid = ?`
tx . Exec ( query , anchorOffset , screenId )
2022-10-06 18:33:54 -07:00
}
2023-03-13 01:52:30 -07:00
if sline , found := editMap [ ScreenField_SelectedLine ]; found {
query = `UPDATE screen SET selectedline = ? WHERE screenid = ?`
tx . Exec ( query , sline , screenId )
2023-03-30 18:08:35 -07:00
if isWebShare ( tx , screenId ) {
insertScreenUpdate ( tx , screenId , UpdateType_ScreenSelectedLine )
}
2022-10-07 01:08:03 -07:00
}
2023-03-13 01:52:30 -07:00
if focusType , found := editMap [ ScreenField_Focus ]; found {
query = `UPDATE screen SET focustype = ? WHERE screenid = ?`
tx . Exec ( query , focusType , screenId )
2022-10-11 01:11:04 -07:00
}
2023-03-13 01:52:30 -07:00
if tabColor , found := editMap [ ScreenField_TabColor ]; found {
query = `UPDATE screen SET screenopts = json_set(screenopts, '$.tabcolor', ?) WHERE screenid = ?`
tx . Exec ( query , tabColor , screenId )
}
2023-11-07 16:04:25 +08:00
if tabIcon , found := editMap [ ScreenField_TabIcon ]; found {
query = `UPDATE screen SET screenopts = json_set(screenopts, '$.tabicon', ?) WHERE screenid = ?`
tx . Exec ( query , tabIcon , screenId )
}
2023-03-13 01:52:30 -07:00
if pterm , found := editMap [ ScreenField_PTerm ]; found {
query = `UPDATE screen SET screenopts = json_set(screenopts, '$.pterm', ?) WHERE screenid = ?`
tx . Exec ( query , pterm , screenId )
}
if name , found := editMap [ ScreenField_Name ]; found {
query = `UPDATE screen SET name = ? WHERE screenid = ?`
tx . Exec ( query , name , screenId )
2022-10-06 18:33:54 -07:00
}
2023-04-04 22:28:52 -07:00
if shareName , found := editMap [ ScreenField_ShareName ]; found {
if ! isWebShare ( tx , screenId ) {
return fmt . Errorf ( "cannot set sharename, screen is not web-shared" )
}
query = `UPDATE screen SET webshareopts = json_set(webshareopts, '$.sharename', ?) WHERE screenid = ?`
tx . Exec ( query , shareName , screenId )
insertScreenUpdate ( tx , screenId , UpdateType_ScreenName )
}
2022-10-06 18:33:54 -07:00
return nil
})
if txErr != nil {
return nil , txErr
}
2023-03-13 01:52:30 -07:00
return GetScreenById ( ctx , screenId )
2022-10-07 01:08:03 -07:00
}
2023-12-17 23:46:53 -08:00
func ScreenUpdateViewOpts ( ctx context . Context , screenId string , viewOpts ScreenViewOptsType ) error {
return WithTx ( ctx , func ( tx * TxWrap ) error {
query := `UPDATE screen SET screenviewopts = ? WHERE screenid = ?`
tx . Exec ( query , quickJson ( viewOpts ), screenId )
return nil
})
}
2023-03-20 19:20:57 -07:00
func GetLineResolveItems ( ctx context . Context , screenId string ) ([] ResolveItem , error ) {
2022-10-06 23:58:38 -07:00
var rtn [] ResolveItem
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-30 18:08:35 -07:00
query := `SELECT lineid as id, linenum as num, archived as hidden FROM line WHERE screenid = ? ORDER BY linenum`
2023-03-20 19:20:57 -07:00
tx . Select ( & rtn , query , screenId )
2022-10-06 23:58:38 -07:00
return nil
})
if txErr != nil {
return nil , txErr
}
return rtn , nil
}
2022-10-11 23:11:43 -07:00
2023-07-30 17:16:43 -07:00
func UpdateScreenFocusForDoneCmd ( ctx context . Context , screenId string , lineId string ) ( * ScreenType , error ) {
2023-03-20 19:20:57 -07:00
return WithTxRtn ( ctx , func ( tx * TxWrap ) ( * ScreenType , error ) {
query := `SELECT screenid
FROM screen s
2023-04-13 12:53:15 -07:00
WHERE s.screenid = ? AND s.focustype = ?
2023-07-30 17:16:43 -07:00
AND s.selectedline IN (SELECT linenum FROM line l WHERE l.screenid = s.screenid AND l.lineid = ?)
2023-03-20 19:20:57 -07:00
`
2023-07-30 17:16:43 -07:00
if ! tx . Exists ( query , screenId , ScreenFocusCmd , lineId ) {
2023-03-20 19:20:57 -07:00
return nil , nil
2022-10-11 23:11:43 -07:00
}
2023-03-20 19:20:57 -07:00
editMap := make ( map [ string ] interface {})
editMap [ ScreenField_Focus ] = ScreenFocusInput
screen , err := UpdateScreen ( tx . Context (), screenId , editMap )
if err != nil {
return nil , err
2022-10-11 23:11:43 -07:00
}
2023-03-20 19:20:57 -07:00
return screen , nil
2022-10-11 23:11:43 -07:00
})
}
2022-11-28 00:13:00 -08:00
func StoreStateBase ( ctx context . Context , state * packet . ShellState ) error {
stateBase := & StateBase {
Version : state . Version ,
Ts : time . Now (). UnixMilli (),
}
stateBase . BaseHash , stateBase . Data = state . EncodeAndHash ()
2023-04-13 12:53:15 -07:00
// envMap := shexec.DeclMapFromState(state)
2022-11-28 00:13:00 -08:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT basehash FROM state_base WHERE basehash = ?`
if tx . Exists ( query , stateBase . BaseHash ) {
return nil
}
query = `INSERT INTO state_base (basehash, ts, version, data) VALUES (:basehash,:ts,:version,:data)`
2023-02-14 16:17:54 -08:00
tx . NamedExec ( query , stateBase )
2022-11-28 00:13:00 -08:00
return nil
})
if txErr != nil {
return txErr
}
return nil
}
func StoreStateDiff ( ctx context . Context , diff * packet . ShellStateDiff ) error {
stateDiff := & StateDiff {
BaseHash : diff . BaseHash ,
Ts : time . Now (). UnixMilli (),
DiffHashArr : diff . DiffHashArr ,
}
stateDiff . DiffHash , stateDiff . Data = diff . EncodeAndHash ()
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT basehash FROM state_base WHERE basehash = ?`
if stateDiff . BaseHash == "" || ! tx . Exists ( query , stateDiff . BaseHash ) {
return fmt . Errorf ( "cannot store statediff, basehash:%s does not exist" , stateDiff . BaseHash )
}
query = `SELECT diffhash FROM state_diff WHERE diffhash = ?`
for idx , diffHash := range stateDiff . DiffHashArr {
if ! tx . Exists ( query , diffHash ) {
return fmt . Errorf ( "cannot store statediff, diffhash[%d]:%s does not exist" , idx , diffHash )
}
}
if tx . Exists ( query , stateDiff . DiffHash ) {
return nil
}
query = `INSERT INTO state_diff (diffhash, ts, basehash, diffhasharr, data) VALUES (:diffhash,:ts,:basehash,:diffhasharr,:data)`
2023-02-14 16:17:54 -08:00
tx . NamedExec ( query , stateDiff . ToMap ())
2022-11-28 00:13:00 -08:00
return nil
})
if txErr != nil {
return txErr
}
return nil
}
2024-03-06 16:37:54 -08:00
func GetStateBaseVersion ( ctx context . Context , baseHash string ) ( string , error ) {
return WithTxRtn ( ctx , func ( tx * TxWrap ) ( string , error ) {
query := `SELECT version FROM state_base WHERE basehash = ?`
rtn := tx . GetString ( query , baseHash )
return rtn , nil
})
}
2024-03-28 16:56:39 -07:00
func GetCurStateDiffFromPtr ( ctx context . Context , ssPtr * packet . ShellStatePtr ) ( * packet . ShellStateDiff , error ) {
2024-03-06 16:37:54 -08:00
if ssPtr == nil {
return nil , fmt . Errorf ( "cannot resolve state, empty stateptr" )
}
if len ( ssPtr . DiffHashArr ) == 0 {
baseVersion , err := GetStateBaseVersion ( ctx , ssPtr . BaseHash )
if err != nil {
return nil , fmt . Errorf ( "cannot get base version: %v" , err )
}
// return an empty diff
return & packet . ShellStateDiff { Version : baseVersion , BaseHash : ssPtr . BaseHash }, nil
}
lastDiffHash := ssPtr . DiffHashArr [ len ( ssPtr . DiffHashArr ) - 1 ]
return GetStateDiff ( ctx , lastDiffHash )
}
func GetStateBase ( ctx context . Context , baseHash string ) ( * packet . ShellState , error ) {
stateBase , txErr := WithTxRtn ( ctx , func ( tx * TxWrap ) ( * StateBase , error ) {
var stateBase StateBase
query := `SELECT * FROM state_base WHERE basehash = ?`
found := tx . Get ( & stateBase , query , baseHash )
if ! found {
return nil , fmt . Errorf ( "StateBase %s not found" , baseHash )
}
return & stateBase , nil
})
if txErr != nil {
return nil , txErr
}
state := & packet . ShellState {}
err := state . DecodeShellState ( stateBase . Data )
if err != nil {
return nil , err
}
return state , nil
}
func GetStateDiff ( ctx context . Context , diffHash string ) ( * packet . ShellStateDiff , error ) {
stateDiff , txErr := WithTxRtn ( ctx , func ( tx * TxWrap ) ( * StateDiff , error ) {
query := `SELECT * FROM state_diff WHERE diffhash = ?`
stateDiff := dbutil . GetMapGen [ * StateDiff ]( tx , query , diffHash )
if stateDiff == nil {
return nil , fmt . Errorf ( "StateDiff %s not found" , diffHash )
}
return stateDiff , nil
})
if txErr != nil {
return nil , txErr
}
state := & packet . ShellStateDiff {}
err := state . DecodeShellStateDiff ( stateDiff . Data )
if err != nil {
return nil , err
}
return state , nil
}
2022-11-28 00:13:00 -08:00
// returns error when not found
2024-03-28 16:56:39 -07:00
func GetFullState ( ctx context . Context , ssPtr packet . ShellStatePtr ) ( * packet . ShellState , error ) {
2022-11-28 00:13:00 -08:00
var state * packet . ShellState
2022-11-28 18:03:02 -08:00
if ssPtr . BaseHash == "" {
2022-11-28 00:13:00 -08:00
return nil , fmt . Errorf ( "invalid empty basehash" )
}
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
var stateBase StateBase
query := `SELECT * FROM state_base WHERE basehash = ?`
2023-02-14 16:17:54 -08:00
found := tx . Get ( & stateBase , query , ssPtr . BaseHash )
2022-11-28 00:13:00 -08:00
if ! found {
2022-11-28 18:03:02 -08:00
return fmt . Errorf ( "ShellState %s not found" , ssPtr . BaseHash )
2022-11-28 00:13:00 -08:00
}
state = & packet . ShellState {}
err := state . DecodeShellState ( stateBase . Data )
if err != nil {
return err
}
2024-01-16 16:11:04 -08:00
sapi , err := shellapi . MakeShellApi ( state . GetShellType ())
if err != nil {
return err
}
2022-11-28 18:03:02 -08:00
for idx , diffHash := range ssPtr . DiffHashArr {
2022-11-28 00:13:00 -08:00
query = `SELECT * FROM state_diff WHERE diffhash = ?`
2023-03-27 14:11:02 -07:00
stateDiff := dbutil . GetMapGen [ * StateDiff ]( tx , query , diffHash )
2022-11-28 00:13:00 -08:00
if stateDiff == nil {
return fmt . Errorf ( "ShellStateDiff %s not found" , diffHash )
}
2024-01-16 16:11:04 -08:00
ssDiff := & packet . ShellStateDiff {}
2022-11-28 00:13:00 -08:00
err = ssDiff . DecodeShellStateDiff ( stateDiff . Data )
if err != nil {
return err
}
2024-01-16 16:11:04 -08:00
newState , err := sapi . ApplyShellStateDiff ( state , ssDiff )
2022-11-28 00:13:00 -08:00
if err != nil {
return fmt . Errorf ( "GetFullState, diff[%d]:%s: %v" , idx , diffHash , err )
}
2024-01-16 16:11:04 -08:00
state = newState
2022-11-28 00:13:00 -08:00
}
return nil
})
if txErr != nil {
return nil , txErr
}
if state == nil {
return nil , fmt . Errorf ( "ShellState not found" )
}
return state , nil
}
2022-12-05 22:59:00 -08:00
2023-09-01 15:21:35 -07:00
func UpdateLineStar ( ctx context . Context , screenId string , lineId string , starVal int ) error {
2022-12-05 22:59:00 -08:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-09-01 15:21:35 -07:00
query := `UPDATE line SET star = ? WHERE screenid = ? AND lineid = ?`
tx . Exec ( query , starVal , screenId , lineId )
2022-12-05 22:59:00 -08:00
return nil
})
if txErr != nil {
return txErr
}
return nil
}
2023-03-31 00:32:38 -07:00
func UpdateLineHeight ( ctx context . Context , screenId string , lineId string , heightVal int ) error {
2023-01-31 22:21:19 -08:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-09-01 15:21:35 -07:00
query := `UPDATE line SET contentheight = ? WHERE screenid = ? AND lineid = ?`
tx . Exec ( query , heightVal , screenId , lineId )
2023-03-31 00:32:38 -07:00
if isWebShare ( tx , screenId ) {
insertScreenLineUpdate ( tx , screenId , lineId , UpdateType_LineContentHeight )
}
2023-01-31 22:21:19 -08:00
return nil
})
if txErr != nil {
return txErr
}
return nil
}
2023-03-25 12:54:56 -07:00
func UpdateLineRenderer ( ctx context . Context , screenId string , lineId string , renderer string ) error {
2023-03-17 14:47:30 -07:00
return WithTx ( ctx , func ( tx * TxWrap ) error {
2023-09-01 15:21:35 -07:00
query := `UPDATE line SET renderer = ? WHERE screenid = ? AND lineid = ?`
tx . Exec ( query , renderer , screenId , lineId )
2023-03-25 12:54:56 -07:00
if isWebShare ( tx , screenId ) {
2023-03-26 13:21:58 -07:00
insertScreenLineUpdate ( tx , screenId , lineId , UpdateType_LineRenderer )
2023-03-25 12:54:56 -07:00
}
2023-03-17 14:47:30 -07:00
return nil
})
}
2023-09-01 15:21:35 -07:00
func UpdateLineState ( ctx context . Context , screenId string , lineId string , lineState map [ string ] any ) error {
qjs := dbutil . QuickJson ( lineState )
if len ( qjs ) > MaxLineStateSize {
return fmt . Errorf ( "linestate for line[%s:%s] exceeds maxsize, size[%d] max[%d]" , screenId , lineId , len ( qjs ), MaxLineStateSize )
}
return WithTx ( ctx , func ( tx * TxWrap ) error {
query := `UPDATE line SET linestate = ? WHERE screenid = ? AND lineid = ?`
tx . Exec ( query , qjs , screenId , lineId )
if isWebShare ( tx , screenId ) {
insertScreenLineUpdate ( tx , screenId , lineId , UpdateType_LineState )
2022-12-05 22:59:00 -08:00
}
return nil
})
2023-09-01 15:21:35 -07:00
}
// can return nil, nil if line is not found
func GetLineById ( ctx context . Context , screenId string , lineId string ) ( * LineType , error ) {
return WithTxRtn ( ctx , func ( tx * TxWrap ) ( * LineType , error ) {
query := `SELECT * FROM line WHERE screenid = ? AND lineid = ?`
line := dbutil . GetMappable [ * LineType ]( tx , query , screenId , lineId )
return line , nil
})
2022-12-05 22:59:00 -08:00
}
2022-12-21 17:45:40 -08:00
2023-03-25 12:54:56 -07:00
func SetLineArchivedById ( ctx context . Context , screenId string , lineId string , archived bool ) error {
2022-12-21 17:45:40 -08:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-09-01 15:21:35 -07:00
query := `UPDATE line SET archived = ? WHERE screenid = ? AND lineid = ?`
tx . Exec ( query , archived , screenId , lineId )
2023-03-25 12:54:56 -07:00
if isWebShare ( tx , screenId ) {
2023-03-30 12:59:58 -07:00
if archived {
insertScreenLineUpdate ( tx , screenId , lineId , UpdateType_LineDel )
} else {
insertScreenLineUpdate ( tx , screenId , lineId , UpdateType_LineNew )
}
2023-03-25 12:54:56 -07:00
}
2022-12-21 17:45:40 -08:00
return nil
})
return txErr
}
2024-01-26 16:25:21 -08:00
func GetScreenSelectedLineId ( ctx context . Context , screenId string ) ( string , error ) {
return WithTxRtn ( ctx , func ( tx * TxWrap ) ( string , error ) {
query := `SELECT selectedline FROM screen WHERE screenid = ?`
sline := tx . GetInt ( query , screenId )
if sline <= 0 {
return "" , nil
}
query = `SELECT lineid FROM line WHERE screenid = ? AND linenum = ?`
lineId := tx . GetString ( query , screenId , sline )
return lineId , nil
})
}
2024-01-08 22:58:32 -08:00
// returns updated screen (only if updated)
func FixupScreenSelectedLine ( ctx context . Context , screenId string ) ( * ScreenType , error ) {
return WithTxRtn ( ctx , func ( tx * TxWrap ) ( * ScreenType , error ) {
query := `SELECT selectedline FROM screen WHERE screenid = ?`
sline := tx . GetInt ( query , screenId )
query = `SELECT linenum FROM line WHERE screenid = ? AND linenum = ?`
if tx . Exists ( query , screenId , sline ) {
// selected line is valid
return nil , nil
}
query = `SELECT min(linenum) FROM line WHERE screenid = ? AND linenum > ?`
newSLine := tx . GetInt ( query , screenId , sline )
if newSLine == 0 {
query = `SELECT max(linenum) FROM line WHERE screenid = ? AND linenum < ?`
newSLine = tx . GetInt ( query , screenId , sline )
}
// newSLine might be 0, but that's ok (because that means there are no lines)
query = `UPDATE screen SET selectedline = ? WHERE screenid = ?`
tx . Exec ( query , newSLine , screenId )
return GetScreenById ( tx . Context (), screenId )
})
}
2023-12-27 13:11:53 -08:00
func DeleteLinesByIds ( ctx context . Context , screenId string , lineIds [] string ) error {
2022-12-21 17:45:40 -08:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-25 12:54:56 -07:00
isWS := isWebShare ( tx , screenId )
2023-03-03 13:31:16 -08:00
for _ , lineId := range lineIds {
2023-12-27 13:11:53 -08:00
query := `SELECT status FROM cmd WHERE screenid = ? AND lineid = ?`
cmdStatus := tx . GetString ( query , screenId , lineId )
if cmdStatus == CmdStatusRunning {
2024-01-08 22:58:32 -08:00
return fmt . Errorf ( "cannot delete line[%s], cmd is running" , lineId )
2022-12-21 17:45:40 -08:00
}
2023-12-27 13:11:53 -08:00
query = `DELETE FROM line WHERE screenid = ? AND lineid = ?`
tx . Exec ( query , screenId , lineId )
query = `DELETE FROM cmd WHERE screenid = ? AND lineid = ?`
tx . Exec ( query , screenId , lineId )
// don't delete history anymore, just remove lineid reference
query = `UPDATE history SET lineid = '', linenum = 0 WHERE screenid = ? AND lineid = ?`
tx . Exec ( query , screenId , lineId )
2023-03-25 12:54:56 -07:00
if isWS {
2023-03-26 13:21:58 -07:00
insertScreenLineUpdate ( tx , screenId , lineId , UpdateType_LineDel )
2023-03-25 12:54:56 -07:00
}
2022-12-21 17:45:40 -08:00
}
return nil
})
return txErr
}
2022-12-30 17:01:17 -08:00
2023-03-14 16:37:22 -07:00
func GetRIsForScreen ( ctx context . Context , sessionId string , screenId string ) ([] * RemoteInstance , error ) {
2022-12-30 17:01:17 -08:00
var rtn [] * RemoteInstance
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-14 16:37:22 -07:00
query := `SELECT * FROM remote_instance WHERE sessionid = ? AND (screenid = '' OR screenid = ?)`
2023-03-27 14:11:02 -07:00
rtn = dbutil . SelectMapsGen [ * RemoteInstance ]( tx , query , sessionId , screenId )
2022-12-30 17:01:17 -08:00
return nil
})
if txErr != nil {
return nil , txErr
}
return rtn , nil
}
2023-01-16 23:36:52 -08:00
2023-02-20 15:41:39 -08:00
func foundInStrArr ( strs [] string , s string ) bool {
for _ , sval := range strs {
if s == sval {
return true
}
}
return false
}
// newPos is 0-indexed
func reorderStrs ( strs [] string , toMove string , newPos int ) [] string {
if ! foundInStrArr ( strs , toMove ) {
return strs
}
var added bool
rtn := make ([] string , 0 , len ( strs ))
for _ , s := range strs {
if s == toMove {
continue
}
if len ( rtn ) == newPos {
added = true
rtn = append ( rtn , toMove )
}
rtn = append ( rtn , s )
}
if ! added {
rtn = append ( rtn , toMove )
}
return rtn
}
// newScreenIdx is 1-indexed
func SetScreenIdx ( ctx context . Context , sessionId string , screenId string , newScreenIdx int ) error {
if newScreenIdx <= 0 {
return fmt . Errorf ( "invalid screenidx/pos, must be greater than 0" )
}
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT screenid FROM screen WHERE sessionid = ? AND screenid = ? AND NOT archived`
if ! tx . Exists ( query , sessionId , screenId ) {
return fmt . Errorf ( "invalid screen, not found (or archived)" )
}
query = `SELECT screenid FROM screen WHERE sessionid = ? AND NOT archived ORDER BY screenidx`
screens := tx . SelectStrings ( query , sessionId )
newScreens := reorderStrs ( screens , screenId , newScreenIdx - 1 )
query = `UPDATE screen SET screenidx = ? WHERE sessionid = ? AND screenid = ?`
for idx , sid := range newScreens {
tx . Exec ( query , idx + 1 , sessionId , sid )
}
return nil
})
return txErr
}
func GetDBVersion ( ctx context . Context ) ( int , error ) {
var version int
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT version FROM schema_migrations`
version = tx . GetInt ( query )
return nil
})
return version , txErr
}
2023-02-20 21:39:29 -08:00
2023-04-04 23:38:34 -07:00
func CountScreenWebShares ( ctx context . Context ) ( int , error ) {
return WithTxRtn ( ctx , func ( tx * TxWrap ) ( int , error ) {
query := `SELECT count(*) FROM screen WHERE sharemode = ?`
count := tx . GetInt ( query , ShareModeWeb )
return count , nil
})
}
func CountScreenLines ( ctx context . Context , screenId string ) ( int , error ) {
return WithTxRtn ( ctx , func ( tx * TxWrap ) ( int , error ) {
query := `SELECT count(*) FROM line WHERE screenid = ? AND NOT archived`
lineCount := tx . GetInt ( query , screenId )
return lineCount , nil
})
}
2024-03-25 20:20:52 -07:00
// Below is currently not used and is causing circular dependency due to moving telemetry code to a new package. It will likely be rewritten whenever we add back webshare and should be moved to a different package then.
// func CanScreenWebShare(ctx context.Context, 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)
// }
// if screen.Archived {
// return fmt.Errorf("screen cannot be shared, must un-archive before sharing")
// }
// webShareCount, err := CountScreenWebShares(ctx)
// if err != nil {
// return fmt.Errorf("screen cannot be share: error getting webshare count: %v", err)
// }
// if webShareCount >= MaxWebShareScreenCount {
// go UpdateCurrentActivity(context.Background(), ActivityUpdate{WebShareLimit: 1})
// return fmt.Errorf("screen cannot be shared, limited to a maximum of %d shared screen(s)", MaxWebShareScreenCount)
// }
// lineCount, err := CountScreenLines(ctx, screen.ScreenId)
// if err != nil {
// return fmt.Errorf("screen cannot be share: error getting screen line count: %v", err)
// }
// if lineCount > MaxWebShareLineCount {
// go UpdateCurrentActivity(context.Background(), ActivityUpdate{WebShareLimit: 1})
// return fmt.Errorf("screen cannot be shared, limited to a maximum of %d lines", MaxWebShareLineCount)
// }
// return nil
// }
2023-04-04 23:38:34 -07:00
2023-03-24 10:34:07 -07:00
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 )
2023-03-30 18:08:35 -07:00
insertScreenNewUpdate ( tx , screenId )
2023-03-24 10:34:07 -07:00
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 )
2023-03-31 13:25:57 -07:00
handleScreenDelUpdate ( tx , screenId )
2023-03-24 10:34:07 -07:00
return nil
})
}
func isWebShare ( tx * TxWrap , screenId string ) bool {
return tx . Exists ( `SELECT screenid FROM screen WHERE screenid = ? AND sharemode = ?` , screenId , ShareModeWeb )
}
2023-03-25 12:54:56 -07:00
2023-03-26 13:21:58 -07:00
func insertScreenUpdate ( tx * TxWrap , screenId string , updateType string ) {
if screenId == "" {
tx . SetErr ( errors . New ( "invalid screen-update, screenid is empty" ))
return
}
2023-03-30 18:08:35 -07:00
nowTs := time . Now (). UnixMilli ()
2023-03-26 13:21:58 -07:00
query := `INSERT INTO screenupdate (screenid, lineid, updatetype, updatets) VALUES (?, ?, ?, ?)`
2023-03-30 18:08:35 -07:00
tx . Exec ( query , screenId , "" , updateType , nowTs )
2023-03-26 23:07:30 -07:00
NotifyUpdateWriter ()
2023-03-26 13:21:58 -07:00
}
2023-03-30 18:08:35 -07:00
func insertScreenNewUpdate ( tx * TxWrap , screenId string ) {
nowTs := time . Now (). UnixMilli ()
query := `INSERT INTO screenupdate (screenid, lineid, updatetype, updatets)
2023-04-04 21:52:20 -07:00
SELECT screenid, lineid, ?, ? FROM line WHERE screenid = ? AND NOT archived ORDER BY linenum DESC`
2023-03-30 18:08:35 -07:00
tx . Exec ( query , UpdateType_LineNew , nowTs , screenId )
query = `INSERT INTO screenupdate (screenid, lineid, updatetype, updatets)
2023-07-30 17:16:43 -07:00
SELECT c.screenid, c.lineid, ?, ? FROM cmd c, line l WHERE c.screenid = ? AND l.lineid = c.lineid AND NOT l.archived ORDER BY l.linenum DESC`
2023-03-30 18:08:35 -07:00
tx . Exec ( query , UpdateType_PtyPos , nowTs , screenId )
NotifyUpdateWriter ()
}
2023-03-31 13:25:57 -07:00
func handleScreenDelUpdate ( tx * TxWrap , screenId string ) {
2023-03-30 18:08:35 -07:00
query := `DELETE FROM screenupdate WHERE screenid = ?`
tx . Exec ( query , screenId )
query = `DELETE FROM webptypos WHERE screenid = ?`
tx . Exec ( query , screenId )
2023-03-31 13:25:57 -07:00
// don't insert UpdateType_ScreenDel (we already processed it in cmdrunner)
}
func insertScreenDelUpdate ( tx * TxWrap , screenId string ) {
handleScreenDelUpdate ( tx , screenId )
2023-03-30 18:08:35 -07:00
insertScreenUpdate ( tx , screenId , UpdateType_ScreenDel )
2023-03-31 13:25:57 -07:00
// don't insert UpdateType_ScreenDel (we already processed it in cmdrunner)
2023-03-30 18:08:35 -07:00
}
2023-03-26 13:21:58 -07:00
func insertScreenLineUpdate ( tx * TxWrap , screenId string , lineId string , updateType string ) {
2023-03-25 12:54:56 -07:00
if screenId == "" {
tx . SetErr ( errors . New ( "invalid screen-update, screenid is empty" ))
return
}
if lineId == "" {
tx . SetErr ( errors . New ( "invalid screen-update, lineid is empty" ))
return
}
2023-03-30 12:59:58 -07:00
if updateType == UpdateType_LineNew || updateType == UpdateType_LineDel {
query := `DELETE FROM screenupdate WHERE screenid = ? AND lineid = ?`
tx . Exec ( query , screenId , lineId )
2023-03-26 23:07:30 -07:00
}
2023-03-30 12:59:58 -07:00
query := `INSERT INTO screenupdate (screenid, lineid, updatetype, updatets) VALUES (?, ?, ?, ?)`
tx . Exec ( query , screenId , lineId , updateType , time . Now (). UnixMilli ())
if updateType == UpdateType_LineNew {
tx . Exec ( query , screenId , lineId , UpdateType_PtyPos , time . Now (). UnixMilli ())
}
NotifyUpdateWriter ()
2023-03-25 12:54:56 -07:00
}
2023-03-26 18:48:43 -07:00
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 {
2023-03-30 18:08:35 -07:00
if updateId < 0 {
return nil // in-memory updates (not from DB)
}
2023-03-26 13:21:58 -07:00
return WithTx ( ctx , func ( tx * TxWrap ) error {
query := `DELETE FROM screenupdate WHERE updateid = ?`
tx . Exec ( query , updateId )
return nil
})
}
2023-03-31 18:15:51 -07:00
func CountScreenUpdates ( ctx context . Context ) ( int , error ) {
return WithTxRtn ( ctx , func ( tx * TxWrap ) ( int , error ) {
query := `SELECT count(*) FROM screenupdate`
return tx . GetInt ( query ), nil
})
}
func RemoveScreenUpdates ( ctx context . Context , updateIds [] int64 ) error {
return WithTx ( ctx , func ( tx * TxWrap ) error {
query := `DELETE FROM screenupdate WHERE updateid IN (SELECT value FROM json_each(?))`
tx . Exec ( query , quickJsonArr ( updateIds ))
return nil
})
}
2023-07-30 17:16:43 -07:00
func MaybeInsertPtyPosUpdate ( ctx context . Context , screenId string , lineId string ) error {
2023-03-26 13:21:58 -07:00
return WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-28 00:24:37 -07:00
if ! isWebShare ( tx , screenId ) {
return nil
}
2023-07-30 17:16:43 -07:00
insertScreenLineUpdate ( tx , screenId , lineId , UpdateType_PtyPos )
2023-03-28 00:24:37 -07:00
return nil
})
}
2023-03-26 13:21:58 -07:00
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
})
}
2023-03-30 12:59:58 -07:00
func DeleteWebPtyPos ( ctx context . Context , screenId string , lineId string ) error {
fmt . Printf ( "del webptypos %s:%s\n" , screenId , lineId )
return WithTx ( ctx , func ( tx * TxWrap ) error {
query := `DELETE FROM webptypos WHERE screenid = ? AND lineid = ?`
tx . Exec ( query , screenId , lineId )
return nil
})
}
2023-03-26 13:21:58 -07:00
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
})
}
2024-01-16 16:11:04 -08:00
func GetRemoteActiveShells ( ctx context . Context , remoteId string ) ([] string , error ) {
return WithTxRtn ( ctx , func ( tx * TxWrap ) ([] string , error ) {
query := `SELECT * FROM remote_instance WHERE remoteid = ?`
riArr := dbutil . SelectMapsGen [ * RemoteInstance ]( tx , query , remoteId )
shellTypeMap := make ( map [ string ] bool )
for _ , ri := range riArr {
2024-02-15 17:42:43 -08:00
if ri . ShellType == "" {
continue
}
2024-01-16 16:11:04 -08:00
shellTypeMap [ ri . ShellType ] = true
}
return utilfn . GetMapKeys ( shellTypeMap ), nil
})
}