2022-07-01 14:07:13 -07:00
package sstore
import (
"context"
"fmt"
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
"github.com/google/uuid"
2023-02-14 16:17:54 -08:00
"github.com/jmoiron/sqlx"
"github.com/sawka/txwrap"
2022-11-27 14:12:15 -08:00
"github.com/scripthaus-dev/mshell/pkg/base"
2022-07-07 16:29:14 -07:00
"github.com/scripthaus-dev/mshell/pkg/packet"
2022-11-28 00:13:00 -08:00
"github.com/scripthaus-dev/mshell/pkg/shexec"
2022-09-20 17:37:49 -07:00
"github.com/scripthaus-dev/sh2-server/pkg/scbase"
2022-07-01 14:07:13 -07:00
)
2022-12-19 18:52:08 -08:00
const HistoryCols = "historyid, ts, userid, sessionid, screenid, windowid, lineid, cmdid, haderror, cmdstr, remoteownerid, remoteid, remotename, ismetacmd, incognito"
2022-08-29 19:18:02 -07:00
const DefaultMaxHistoryItems = 1000
2023-02-14 16:17:54 -08:00
type SingleConnDBGetter struct {
SingleConnLock * sync . Mutex
}
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 )
}
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
}
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 {
rtn = append ( rtn , RemoteFromMap ( m ))
}
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
}
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 )
remote = RemoteFromMap ( m )
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 )
2022-07-07 13:26:46 -07:00
remote = RemoteFromMap ( m )
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 )
remote = RemoteFromMap ( m )
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 = ?`
m := tx . GetMap ( query , cname )
remote = RemoteFromMap ( m )
return nil
})
if err != nil {
return nil , err
}
return remote , nil
}
2022-08-16 15:08:28 -07:00
func GetRemoteByPhysicalId ( ctx context . Context , physicalId 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 physicalid = ?`
m := tx . GetMap ( query , physicalId )
2022-07-07 13:26:46 -07:00
remote = RemoteFromMap ( m )
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-09-13 17:11:36 -07:00
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
2022-11-28 00:13:00 -08:00
( remoteid, physicalid, remotetype, remotealias, remotecanonicalname, remotesudo, remoteuser, remotehost, connectmode, autoinstall, sshopts, remoteopts, lastconnectts, archived, remoteidx, local) VALUES
(:remoteid,:physicalid,:remotetype,:remotealias,:remotecanonicalname,:remotesudo,:remoteuser,:remotehost,:connectmode,:autoinstall,:sshopts,:remoteopts,:lastconnectts,:archived,:remoteidx,:local)`
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
}
2022-08-11 12:07:41 -07:00
func InsertHistoryItem ( ctx context . Context , hitem * HistoryItemType ) error {
if hitem == nil {
return fmt . Errorf ( "cannot insert nil history item" )
}
2022-10-10 17:30:48 -07:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `INSERT INTO history
2022-12-19 18:52:08 -08:00
( historyid, ts, userid, sessionid, screenid, windowid, lineid, cmdid, haderror, cmdstr, remoteownerid, remoteid, remotename, ismetacmd, incognito) VALUES
(:historyid,:ts,:userid,:sessionid,:screenid,:windowid,:lineid,:cmdid,:haderror,:cmdstr,:remoteownerid,:remoteid,:remotename,:ismetacmd,:incognito)`
2023-02-14 16:17:54 -08:00
tx . NamedExec ( query , hitem . ToMap ())
2022-10-10 17:30:48 -07:00
return nil
})
return txErr
2022-08-11 12:07:41 -07:00
}
2022-12-19 18:52:08 -08:00
func IsIncognitoScreen ( ctx context . Context , sessionId string , screenId string ) ( bool , error ) {
var rtn bool
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT incognito FROM screen WHERE sessionid = ? AND screenid = ?`
2023-02-14 16:17:54 -08:00
tx . Get ( & rtn , query , sessionId , screenId )
2022-12-19 18:52:08 -08:00
return nil
})
return rtn , txErr
}
2022-08-29 19:18:02 -07:00
func runHistoryQuery ( tx * TxWrap , sessionId string , windowId string , opts HistoryQueryOpts ) ([] * HistoryItemType , error ) {
// check sessionid/windowid format because we are directly inserting them into the SQL
if sessionId != "" {
_ , err := uuid . Parse ( sessionId )
if err != nil {
return nil , fmt . Errorf ( "malformed sessionid" )
}
}
if windowId != "" {
_ , err := uuid . Parse ( windowId )
if err != nil {
return nil , fmt . Errorf ( "malformed windowid" )
}
}
hnumStr := ""
whereClause := ""
if sessionId != "" && windowId != "" {
whereClause = fmt . Sprintf ( "WHERE sessionid = '%s' AND windowid = '%s'" , sessionId , windowId )
hnumStr = "w"
} else if sessionId != "" {
whereClause = fmt . Sprintf ( "WHERE sessionid = '%s'" , sessionId )
hnumStr = "s"
} else {
hnumStr = "g"
}
maxItems := opts . MaxItems
if maxItems == 0 {
maxItems = DefaultMaxHistoryItems
}
query := fmt . Sprintf ( "SELECT %s, '%s' || row_number() OVER win AS historynum FROM history %s WINDOW win AS (ORDER BY ts, historyid) ORDER BY ts DESC, historyid DESC LIMIT %d" , HistoryCols , hnumStr , whereClause , maxItems )
if opts . FromTs > 0 {
query = fmt . Sprintf ( "SELECT * FROM (%s) WHERE ts >= %d" , query , opts . FromTs )
}
marr := tx . SelectMaps ( query )
rtn := make ([] * HistoryItemType , len ( marr ))
for idx , m := range marr {
hitem := HistoryItemFromMap ( m )
rtn [ idx ] = hitem
}
return rtn , nil
}
func GetHistoryItems ( ctx context . Context , sessionId string , windowId string , opts HistoryQueryOpts ) ([] * HistoryItemType , error ) {
2022-08-11 23:45:15 -07:00
var rtn [] * HistoryItemType
2022-08-29 19:18:02 -07:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
var err error
rtn , err = runHistoryQuery ( tx , sessionId , windowId , opts )
if err != nil {
return err
2022-08-28 14:24:05 -07:00
}
2022-08-11 23:45:15 -07:00
return nil
})
2022-08-29 19:18:02 -07:00
if txErr != nil {
return nil , txErr
2022-08-11 23:45:15 -07:00
}
return rtn , 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
}
2022-08-26 16:21:19 -07:00
func GetAllSessions ( ctx context . Context ) ( * ModelUpdate , error ) {
2022-07-08 13:23:45 -07:00
var rtn [] * SessionType
2022-08-26 16:21:19 -07:00
var activeSessionId string
txErr := 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-10 18:33:32 -07:00
sessionMap := make ( map [ string ] * SessionType )
2022-08-08 16:21:46 -07:00
for _ , session := range rtn {
2022-08-10 18:33:32 -07:00
sessionMap [ session . SessionId ] = session
2022-08-08 16:21:46 -07:00
session . Full = true
}
2022-07-12 21:51:17 -07:00
var screens [] * ScreenType
2022-12-25 13:21:48 -08:00
query = `SELECT * FROM screen ORDER BY archived, screenidx, archivedts`
2023-02-14 16:17:54 -08:00
tx . Select ( & screens , query )
2022-07-12 21:51:17 -07:00
screenMap := make ( map [ string ][] * ScreenType )
for _ , screen := range screens {
screenArr := screenMap [ screen . SessionId ]
screenArr = append ( screenArr , screen )
screenMap [ screen . SessionId ] = screenArr
}
for _ , session := range rtn {
session . Screens = screenMap [ session . SessionId ]
}
var sws [] * ScreenWindowType
query = `SELECT * FROM screen_window`
2023-02-14 16:17:54 -08:00
tx . Select ( & sws , query )
2022-07-12 21:51:17 -07:00
screenIdMap := make ( map [ string ] * ScreenType )
for _ , screen := range screens {
screenIdMap [ screen . SessionId + screen . ScreenId ] = screen
}
for _ , sw := range sws {
screen := screenIdMap [ sw . SessionId + sw . ScreenId ]
if screen == nil {
continue
}
screen . Windows = append ( screen . Windows , sw )
}
2022-08-24 02:14:16 -07:00
query = `SELECT * FROM remote_instance`
2022-10-16 23:51:04 -07:00
riMaps := tx . SelectMaps ( query )
for _ , m := range riMaps {
ri := RIFromMap ( m )
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`
activeSessionId = tx . GetString ( query )
2022-07-12 13:50:44 -07:00
return nil
})
2022-08-26 16:21:19 -07:00
if txErr != nil {
return nil , txErr
}
return & ModelUpdate { Sessions : rtn , ActiveSessionId : activeSessionId }, nil
2022-07-12 13:50:44 -07:00
}
func GetWindowById ( ctx context . Context , sessionId string , windowId string ) ( * WindowType , error ) {
var rtnWindow * WindowType
err := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT * FROM window WHERE sessionid = ? AND windowid = ?`
2022-08-24 02:14:16 -07:00
m := tx . GetMap ( query , sessionId , windowId )
if m == nil {
2022-07-12 13:50:44 -07:00
return nil
}
2022-08-24 02:14:16 -07:00
rtnWindow = WindowFromMap ( m )
2022-10-06 23:58:38 -07:00
query = `SELECT * FROM line WHERE sessionid = ? AND windowid = ? ORDER BY linenum`
2023-02-14 16:17:54 -08:00
tx . Select ( & rtnWindow . Lines , query , sessionId , windowId )
2022-07-12 13:50:44 -07:00
query = `SELECT * FROM cmd WHERE cmdid IN (SELECT cmdid FROM line WHERE sessionid = ? AND windowid = ?)`
cmdMaps := tx . SelectMaps ( query , sessionId , windowId )
for _ , m := range cmdMaps {
2022-08-24 02:14:16 -07:00
rtnWindow . Cmds = append ( rtnWindow . Cmds , CmdFromMap ( m ))
2022-07-12 13:50:44 -07:00
}
return nil
})
return rtnWindow , err
2022-07-08 13:23:45 -07:00
}
2022-12-26 12:38:47 -08:00
// includes archived screens (does not include screen windows)
func GetBareSessionScreens ( ctx context . Context , sessionId string ) ([] * ScreenType , error ) {
2022-07-14 18:39:40 -07:00
var rtn [] * ScreenType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-12-25 13:21:48 -08:00
query := `SELECT * FROM screen WHERE sessionid = ? ORDER BY archived, screenidx, archivedts`
2023-02-14 16:17:54 -08:00
tx . Select ( & rtn , query , sessionId )
2022-12-23 15:56:29 -08:00
return nil
})
return rtn , txErr
}
2022-07-01 14:07:13 -07:00
func GetSessionById ( ctx context . Context , id string ) ( * SessionType , error ) {
2022-08-26 16:21:19 -07:00
allSessionsUpdate , err := GetAllSessions ( ctx )
2022-07-01 14:07:13 -07:00
if err != nil {
return nil , err
}
2022-08-26 16:21:19 -07:00
allSessions := allSessionsUpdate . Sessions
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
}
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
}
2022-07-08 13:23:45 -07:00
// also creates default window, returns sessionId
// if sessionName == "", it will be generated
2022-08-24 02:14:16 -07:00
func InsertSessionWithName ( ctx context . Context , sessionName string , activate bool ) ( UpdatePacket , error ) {
2022-12-19 18:52:08 -08:00
newSessionId := scbase . GenPromptUUID ()
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` )
sessionName = fmtUniqueName ( sessionName , "session-%d" , len ( names ) + 1 , names )
maxSessionIdx := tx . GetInt ( `SELECT COALESCE(max(sessionidx), 0) FROM session` )
2022-12-25 13:21:48 -08:00
query := `INSERT INTO session (sessionid, name, activescreenid, sessionidx, notifynum, archived, archivedts, ownerid, sharemode, accesskey)
VALUES (?, ?, '', ?, ?, 0, 0, '', 'local', '')`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , newSessionId , sessionName , maxSessionIdx + 1 , 0 )
2022-07-14 18:39:40 -07:00
_ , err := InsertScreen ( tx . Context (), newSessionId , "" , true )
2022-07-12 21:51:17 -07:00
if err != nil {
return err
2022-07-08 13:23:45 -07:00
}
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 {
2022-08-08 16:21:46 -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 {
return nil , err
}
2022-08-24 02:14:16 -07:00
update := ModelUpdate {
2022-08-08 16:21:46 -07:00
Sessions : [] * SessionType { session },
}
if activate {
update . ActiveSessionId = newSessionId
}
return update , 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
}
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
}
}
2022-08-26 17:17:33 -07:00
func InsertScreen ( ctx context . Context , sessionId string , origScreenName string , activate bool ) ( UpdatePacket , 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
}
2022-08-24 02:14:16 -07:00
remoteId := tx . GetString ( `SELECT remoteid FROM remote WHERE remotealias = ?` , LocalRemoteAlias )
if remoteId == "" {
return fmt . Errorf ( "cannot create screen, no local remote found" )
}
newWindowId := txCreateWindow ( tx , sessionId , RemotePtrType { RemoteId : remoteId })
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
}
2022-12-19 18:52:08 -08:00
newScreenId = scbase . GenPromptUUID ()
2022-12-25 13:21:48 -08:00
query = `INSERT INTO screen (sessionid, screenid, name, activewindowid, screenidx, screenopts, ownerid, sharemode, incognito, archived, archivedts) VALUES (?, ?, ?, ?, ?, ?, '', 'local', 0, 0, 0)`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , sessionId , newScreenId , screenName , newWindowId , maxScreenIdx + 1 , ScreenOptsType {})
2022-07-12 21:51:17 -07:00
layout := LayoutType { Type : LayoutFull }
2022-10-11 01:11:04 -07:00
query = `INSERT INTO screen_window (sessionid, screenid, windowid, name, layout, selectedline, anchor, focustype) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , sessionId , newScreenId , newWindowId , DefaultScreenWindowName , layout , 0 , SWAnchorType {}, "input" )
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
}
2022-07-12 16:10:46 -07:00
return nil
})
2022-12-26 16:09:21 -08:00
if txErr != nil {
return nil , txErr
}
2022-07-15 01:57:45 -07:00
newScreen , err := GetScreenById ( ctx , sessionId , newScreenId )
if err != nil {
2022-07-15 17:53:23 -07:00
return nil , err
2022-07-15 01:57:45 -07:00
}
2022-12-26 16:09:21 -08:00
bareSession , err := GetBareSessionById ( ctx , sessionId )
if err != nil {
return nil , err
2022-07-15 01:57:45 -07:00
}
2022-12-26 16:09:21 -08:00
bareSession . Screens = append ( bareSession . Screens , newScreen )
return ModelUpdate { Sessions : [] * SessionType { bareSession }}, nil
2022-07-12 16:10:46 -07:00
}
2022-07-14 18:39:40 -07:00
func GetScreenById ( ctx context . Context , sessionId string , screenId string ) ( * ScreenType , error ) {
var rtnScreen * ScreenType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT * FROM screen WHERE sessionid = ? AND screenid = ?`
var screen ScreenType
2023-02-14 16:17:54 -08:00
found := tx . Get ( & screen , query , sessionId , screenId )
2022-07-14 18:39:40 -07:00
if ! found {
return nil
}
rtnScreen = & screen
query = `SELECT * FROM screen_window WHERE sessionid = ? AND screenid = ?`
2023-02-14 16:17:54 -08:00
tx . Select ( & screen . Windows , query , sessionId , screenId )
2022-07-15 01:57:45 -07:00
screen . Full = true
2022-07-14 18:39:40 -07:00
return nil
})
if txErr != nil {
return nil , txErr
}
return rtnScreen , nil
}
2022-08-24 02:14:16 -07:00
func txCreateWindow ( tx * TxWrap , sessionId string , curRemote RemotePtrType ) string {
w := & WindowType {
2022-09-20 17:01:25 -07:00
SessionId : sessionId ,
2022-12-19 18:52:08 -08:00
WindowId : scbase . GenPromptUUID (),
2022-09-20 17:01:25 -07:00
CurRemote : curRemote ,
NextLineNum : 1 ,
WinOpts : WindowOptsType {},
ShareMode : ShareModeLocal ,
ShareOpts : WindowShareOptsType {},
2022-08-24 02:14:16 -07:00
}
wmap := w . ToMap ()
2022-09-20 17:01:25 -07:00
query := `INSERT INTO window ( sessionid, windowid, curremoteownerid, curremoteid, curremotename, nextlinenum, winopts, ownerid, sharemode, shareopts)
VALUES (:sessionid,:windowid,:curremoteownerid,:curremoteid,:curremotename,:nextlinenum,:winopts,:ownerid,:sharemode,:shareopts)`
2023-02-14 16:17:54 -08:00
tx . NamedExec ( query , wmap )
2022-08-24 02:14:16 -07:00
return w . WindowId
2022-07-12 13:50:44 -07:00
}
2022-09-20 17:37:49 -07:00
func FindLineIdByArg ( ctx context . Context , sessionId string , windowId string , lineArg string ) ( string , error ) {
var lineId string
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
lineNum , err := strconv . Atoi ( lineArg )
if err == nil {
// valid linenum
query := `SELECT lineid FROM line WHERE sessionid = ? AND windowid = ? AND linenum = ?`
lineId = tx . GetString ( query , sessionId , windowId , lineNum )
} else if len ( lineArg ) == 8 {
// prefix id string match
query := `SELECT lineid FROM line WHERE sessionid = ? AND windowid = ? AND substr(lineid, 1, 8) = ?`
lineId = tx . GetString ( query , sessionId , windowId , lineArg )
} else {
// id match
2022-12-05 22:59:00 -08:00
query := `SELECT lineid FROM line WHERE sessionid = ? AND windowid = ? AND lineid = ?`
2022-09-20 17:37:49 -07:00
lineId = tx . GetString ( query , sessionId , windowId , lineArg )
}
return nil
})
if txErr != nil {
return "" , txErr
}
return lineId , nil
}
2022-10-27 22:00:10 -07:00
func GetLineCmdByLineId ( ctx context . Context , sessionId string , windowId string , lineId string ) ( * LineType , * CmdType , error ) {
2022-09-20 17:37:49 -07:00
var lineRtn * LineType
var cmdRtn * CmdType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT windowid FROM window WHERE sessionid = ? AND windowid = ?`
if ! tx . Exists ( query , sessionId , windowId ) {
return fmt . Errorf ( "window not found" )
}
var lineVal LineType
query = `SELECT * FROM line WHERE sessionid = ? AND windowid = ? AND lineid = ?`
2023-02-14 16:17:54 -08:00
found := tx . Get ( & lineVal , query , sessionId , windowId , lineId )
2022-09-20 17:37:49 -07:00
if ! found {
return nil
}
lineRtn = & lineVal
if lineVal . CmdId != "" {
query = `SELECT * FROM cmd WHERE sessionid = ? AND cmdid = ?`
m := tx . GetMap ( query , sessionId , lineVal . CmdId )
cmdRtn = CmdFromMap ( m )
}
return nil
})
if txErr != nil {
return nil , nil , txErr
}
return lineRtn , cmdRtn , nil
}
2022-10-27 22:00:10 -07:00
func GetLineCmdByCmdId ( ctx context . Context , sessionId string , windowId string , cmdId string ) ( * LineType , * CmdType , error ) {
var lineRtn * LineType
var cmdRtn * CmdType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT windowid FROM window WHERE sessionid = ? AND windowid = ?`
if ! tx . Exists ( query , sessionId , windowId ) {
return fmt . Errorf ( "window not found" )
}
var lineVal LineType
query = `SELECT * FROM line WHERE sessionid = ? AND windowid = ? AND cmdid = ?`
2023-02-14 16:17:54 -08:00
found := tx . Get ( & lineVal , query , sessionId , windowId , cmdId )
2022-10-27 22:00:10 -07:00
if ! found {
return nil
}
lineRtn = & lineVal
query = `SELECT * FROM cmd WHERE sessionid = ? AND cmdid = ?`
m := tx . GetMap ( query , sessionId , cmdId )
cmdRtn = CmdFromMap ( m )
return nil
})
if txErr != nil {
return nil , nil , txErr
}
return lineRtn , cmdRtn , nil
}
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" )
}
2022-07-02 13:31:56 -07:00
return WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT windowid FROM window WHERE sessionid = ? AND windowid = ?`
2022-09-20 17:37:49 -07:00
if ! tx . Exists ( query , line . SessionId , line . WindowId ) {
2022-07-02 13:31:56 -07:00
return fmt . Errorf ( "window not found, cannot insert line[%s/%s]" , line . SessionId , line . WindowId )
}
2022-09-20 17:01:25 -07:00
query = `SELECT nextlinenum FROM window WHERE sessionid = ? AND windowid = ?`
nextLineNum := tx . GetInt ( query , line . SessionId , line . WindowId )
line . LineNum = int64 ( nextLineNum )
2023-02-20 21:39:29 -08:00
query = `INSERT INTO line ( sessionid, windowid, userid, lineid, ts, linenum, linenumtemp, linelocal, linetype, text, cmdid, renderer, ephemeral, contentheight, star, archived, bookmarked, pinned)
VALUES (:sessionid,:windowid,:userid,:lineid,:ts,:linenum,:linenumtemp,:linelocal,:linetype,:text,:cmdid,:renderer,:ephemeral,:contentheight,:star,:archived,:bookmarked,:pinned)`
2023-02-14 16:17:54 -08:00
tx . NamedExec ( query , line )
2022-09-20 17:01:25 -07:00
query = `UPDATE window SET nextlinenum = ? WHERE sessionid = ? AND windowid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , nextLineNum + 1 , line . SessionId , line . WindowId )
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 = `
2022-11-28 18:03:02 -08:00
INSERT INTO cmd ( sessionid, cmdid, remoteownerid, remoteid, remotename, cmdstr, festate, statebasehash, statediffhasharr, termopts, origtermopts, status, startpk, doneinfo, rtnstate, runout, rtnbasehash, rtndiffhasharr)
VALUES (:sessionid,:cmdid,:remoteownerid,:remoteid,:remotename,:cmdstr,:festate,:statebasehash,:statediffhasharr,:termopts,:origtermopts,:status,:startpk,:doneinfo,: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
})
}
2022-07-07 13:26:46 -07:00
func GetCmdById ( ctx context . Context , sessionId string , cmdId string ) ( * CmdType , error ) {
var cmd * CmdType
err := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT * FROM cmd WHERE sessionid = ? AND cmdid = ?`
m := tx . GetMap ( query , sessionId , cmdId )
cmd = CmdFromMap ( m )
return nil
})
2022-07-07 00:10:37 -07:00
if err != nil {
return nil , err
}
2022-07-07 13:26:46 -07:00
return cmd , nil
2022-07-07 00:10:37 -07:00
}
2022-07-07 16:29:14 -07:00
2022-11-28 18:03:02 -08:00
func HasDoneInfo ( ctx context . Context , ck base . CommandKey ) ( bool , error ) {
2022-11-27 14:12:15 -08:00
var found bool
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-11-28 18:03:02 -08:00
found = tx . Exists ( `SELECT sessionid FROM cmd WHERE sessionid = ? AND cmdid = ? AND doneinfo is NOT NULL` , ck . GetSessionId (), ck . GetCmdId ())
2022-11-27 14:12:15 -08:00
return nil
})
if txErr != nil {
return false , txErr
}
return found , nil
}
2022-11-28 18:03:02 -08:00
func UpdateCmdDoneInfo ( ctx context . Context , ck base . CommandKey , doneInfo * CmdDoneInfo ) ( * ModelUpdate , error ) {
if doneInfo == nil {
return nil , fmt . Errorf ( "invalid cmddone packet" )
}
if ck . IsEmpty () {
return nil , fmt . Errorf ( "cannot update cmddoneinfo, empty ck" )
2022-07-07 16:29:14 -07:00
}
2022-08-19 17:14:53 -07:00
var rtnCmd * CmdType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-11-28 18:03:02 -08:00
query := `UPDATE cmd SET status = ?, doneinfo = ? WHERE sessionid = ? AND cmdid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , CmdStatusDone , quickJson ( doneInfo ), ck . GetSessionId (), ck . GetCmdId ())
2022-08-19 17:14:53 -07:00
var err error
2022-11-28 18:03:02 -08:00
rtnCmd , err = GetCmdById ( tx . Context (), ck . GetSessionId (), ck . GetCmdId ())
2022-08-19 17:14:53 -07:00
if err != nil {
return err
}
2022-07-07 16:29:14 -07:00
return nil
})
2022-08-19 17:14:53 -07:00
if txErr != nil {
return nil , txErr
}
if rtnCmd == nil {
2022-11-28 18:03:02 -08:00
return nil , fmt . Errorf ( "cmd data not found for ck[%s]" , ck )
2022-08-19 17:14:53 -07:00
}
2022-10-11 23:11:43 -07:00
return & ModelUpdate { Cmd : rtnCmd }, nil
2022-07-07 16:29:14 -07:00
}
2022-11-28 18:03:02 -08:00
func UpdateCmdRtnState ( ctx context . Context , ck base . CommandKey , statePtr ShellStatePtr ) error {
if ck . IsEmpty () {
return fmt . Errorf ( "cannot update cmdrtnstate, empty ck" )
}
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `UPDATE cmd SET rtnbasehash = ?, rtndiffhasharr = ? WHERE sessionid = ? AND cmdid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , statePtr . BaseHash , quickJsonArr ( statePtr . DiffHashArr ), ck . GetSessionId (), ck . GetCmdId ())
2022-11-28 18:03:02 -08:00
return nil
})
if txErr != nil {
return txErr
}
return nil
}
2022-07-07 16:29:14 -07:00
func AppendCmdErrorPk ( ctx context . Context , errPk * packet . CmdErrorPacketType ) error {
if errPk == nil || errPk . CK . IsEmpty () {
return fmt . Errorf ( "invalid cmderror packet (no ck)" )
}
return WithTx ( ctx , func ( tx * TxWrap ) error {
query := `UPDATE cmd SET runout = json_insert(runout, '$[#]', ?) WHERE sessionid = ? AND cmdid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , quickJson ( errPk ), errPk . CK . GetSessionId (), errPk . CK . GetCmdId ())
2022-07-07 16:29:14 -07:00
return nil
})
}
2023-01-11 20:53:46 -08:00
func ReInitFocus ( ctx context . Context ) error {
return WithTx ( ctx , func ( tx * TxWrap ) error {
query := `UPDATE screen_window 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 {
query := `UPDATE cmd SET status = ? WHERE status = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , CmdStatusHangup , CmdStatusRunning )
2022-07-07 16:29:14 -07:00
return nil
})
}
func HangupRunningCmdsByRemoteId ( ctx context . Context , remoteId string ) error {
return WithTx ( ctx , func ( tx * TxWrap ) error {
query := `UPDATE cmd SET status = ? WHERE status = ? AND remoteid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , CmdStatusHangup , CmdStatusRunning , remoteId )
2022-07-07 16:29:14 -07:00
return nil
})
}
2022-07-14 18:39:40 -07:00
2022-11-27 14:12:15 -08:00
func HangupCmd ( ctx context . Context , ck base . CommandKey ) error {
return WithTx ( ctx , func ( tx * TxWrap ) error {
query := `UPDATE cmd SET status = ? WHERE sessionid = ? AND cmdid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , CmdStatusHangup , ck . GetSessionId (), ck . GetCmdId ())
2022-11-27 14:12:15 -08:00
return nil
})
}
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 ]
}
2022-07-15 17:53:23 -07:00
func SwitchScreenById ( ctx context . Context , sessionId string , screenId string ) ( UpdatePacket , error ) {
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
}
return ModelUpdate { Sessions : [] * SessionType { bareSession }}, nil
2022-07-14 18:39:40 -07:00
}
2022-07-15 01:57:45 -07:00
2023-01-02 12:09:01 -08:00
func cleanSessionCmds ( ctx context . Context , sessionId string ) error {
2023-01-25 14:29:12 -08:00
var removedCmds [] string
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-01-02 12:09:01 -08:00
query := `SELECT cmdid FROM cmd WHERE sessionid = ? AND cmdid NOT IN (SELECT cmdid FROM line WHERE sessionid = ?)`
2023-01-25 14:29:12 -08:00
removedCmds = tx . SelectStrings ( query , sessionId , sessionId )
2023-01-02 12:09:01 -08:00
query = `DELETE FROM cmd WHERE sessionid = ? AND cmdid NOT IN (SELECT cmdid FROM line WHERE sessionid = ?)`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , sessionId , sessionId )
2023-02-20 21:39:29 -08:00
query = `DELETE FROM bookmark_cmd WHERE sessionid = ? AND cmdid NOT IN (SELECT cmdid FROM cmd WHERE sessionid = ?)`
tx . Exec ( query , sessionId , sessionId )
2023-01-02 12:09:01 -08:00
return nil
})
if txErr != nil {
return txErr
}
2023-01-25 14:29:12 -08:00
for _ , cmdId := range removedCmds {
DeletePtyOutFile ( ctx , sessionId , cmdId )
}
2023-01-02 12:09:01 -08:00
return nil
}
2022-12-25 13:03:11 -08:00
func CleanWindows ( sessionId string ) {
2023-01-25 14:29:12 -08:00
// NOTE: context.Background() here! (this could take a long time, and is async)
2022-12-25 13:03:11 -08:00
txErr := WithTx ( context . Background (), func ( tx * TxWrap ) error {
query := `SELECT windowid FROM window WHERE sessionid = ? AND windowid NOT IN (SELECT windowid FROM screen_window WHERE sessionid = ?)`
removedWindowIds := tx . SelectStrings ( query , sessionId , sessionId )
if len ( removedWindowIds ) == 0 {
return nil
}
for _ , windowId := range removedWindowIds {
query = `DELETE FROM window WHERE sessionid = ? AND windowid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , sessionId , windowId )
2022-12-25 13:03:11 -08:00
query = `DELETE FROM history WHERE sessionid = ? AND windowid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , sessionId , windowId )
2022-12-25 13:21:48 -08:00
query = `DELETE FROM line WHERE sessionid = ? AND windowid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , sessionId , windowId )
2022-12-25 13:03:11 -08:00
}
2023-01-02 12:09:01 -08:00
return cleanSessionCmds ( tx . Context (), sessionId )
2022-12-25 13:03:11 -08:00
})
if txErr != nil {
fmt . Printf ( "ERROR cleaning windows sessionid:%s: %v\n" , sessionId , txErr )
}
2022-07-15 01:57:45 -07:00
}
2022-12-25 13:21:48 -08:00
func ArchiveScreen ( ctx context . Context , sessionId string , screenId string ) ( UpdatePacket , error ) {
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)" )
}
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 )
2022-12-23 15:56:29 -08:00
isActive := tx . Exists ( `SELECT sessionid FROM session WHERE sessionid = ? AND activescreenid = ?` , sessionId , screenId )
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
}
2022-12-26 16:09:21 -08:00
bareSession , err := GetBareSessionById ( ctx , sessionId )
if err != nil {
return nil , txErr
}
update := ModelUpdate { Sessions : [] * SessionType { bareSession }}
2022-12-26 12:38:47 -08:00
newScreen , _ := GetScreenById ( ctx , sessionId , screenId )
if newScreen != nil {
2022-12-26 16:09:21 -08:00
bareSession . Screens = append ( bareSession . Screens , newScreen )
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
}
2022-07-15 17:53:23 -07:00
func DeleteScreen ( ctx context . Context , sessionId string , screenId string ) ( UpdatePacket , error ) {
2022-07-15 01:57:45 -07: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 = ?`
if ! tx . Exists ( query , sessionId , screenId ) {
return fmt . Errorf ( "cannot purge screen (not found)" )
}
query = `SELECT count(*) FROM screen WHERE sessionid = ? AND NOT archived`
numScreens := tx . GetInt ( query , sessionId )
if numScreens <= 1 {
return fmt . Errorf ( "cannot purge the last screen in a session" )
}
2022-07-15 01:57:45 -07:00
isActive := tx . Exists ( `SELECT sessionid FROM session WHERE sessionid = ? AND activescreenid = ?` , sessionId , screenId )
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-07-15 01:57:45 -07:00
nextId := getNextId ( screenIds , screenId )
2023-02-14 16:17:54 -08:00
tx . Exec ( `UPDATE session SET activescreenid = ? WHERE sessionid = ?` , nextId , sessionId )
2022-07-15 01:57:45 -07:00
}
2022-12-25 13:03:11 -08:00
query = `DELETE FROM screen_window WHERE sessionid = ? AND screenid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , sessionId , screenId )
2022-07-15 01:57:45 -07:00
query = `DELETE FROM screen WHERE sessionid = ? AND screenid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , sessionId , 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
}
2022-12-25 13:03:11 -08:00
go CleanWindows ( sessionId )
2022-12-26 16:09:21 -08:00
bareSession , err := GetBareSessionById ( ctx , sessionId )
if err != nil {
return nil , err
}
bareSession . Screens = append ( bareSession . Screens , & ScreenType { SessionId : sessionId , ScreenId : screenId , Remove : true })
2022-12-28 13:56:19 -08:00
update := ModelUpdate { Sessions : [] * SessionType { bareSession }}
return update , nil
2022-07-15 01:57:45 -07:00
}
2022-07-15 17:37:32 -07:00
2022-11-28 18:03:02 -08:00
func GetRemoteState ( ctx context . Context , sessionId string , windowId string , remotePtr RemotePtrType ) ( * packet . ShellState , * ShellStatePtr , error ) {
ssptr , err := GetRemoteStatePtr ( ctx , sessionId , windowId , remotePtr )
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
}
func GetRemoteStatePtr ( ctx context . Context , sessionId string , windowId string , remotePtr RemotePtrType ) ( * ShellStatePtr , error ) {
var ssptr * ShellStatePtr
2022-07-15 17:37:32 -07:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-11-28 00:13:00 -08:00
ri , err := GetRemoteInstance ( tx . Context (), sessionId , windowId , remotePtr )
if err != nil {
return err
}
if ri == nil {
2022-07-15 17:37:32 -07:00
return nil
}
2022-11-28 18:03:02 -08:00
ssptr = & 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
2022-08-24 02:14:16 -07:00
func validateSessionWindow ( tx * TxWrap , sessionId string , windowId string ) error {
if windowId == "" {
query := `SELECT sessionid FROM session WHERE sessionid = ?`
if ! tx . Exists ( query , sessionId ) {
return fmt . Errorf ( "no session found" )
}
return nil
} else {
2022-08-09 14:24:57 -07:00
query := `SELECT windowid FROM window WHERE sessionid = ? AND windowid = ?`
if ! tx . Exists ( query , sessionId , windowId ) {
2022-08-24 02:14:16 -07:00
return fmt . Errorf ( "no window found" )
2022-08-09 14:24:57 -07:00
}
2022-08-24 02:14:16 -07:00
return nil
}
}
2022-11-28 00:13:00 -08:00
func GetRemoteInstance ( ctx context . Context , sessionId string , windowId string , remotePtr RemotePtrType ) ( * RemoteInstance , error ) {
if remotePtr . IsSessionScope () {
windowId = ""
}
var ri * RemoteInstance
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT * FROM remote_instance WHERE sessionid = ? AND windowid = ? AND remoteownerid = ? AND remoteid = ? AND name = ?`
m := tx . GetMap ( query , sessionId , windowId , remotePtr . OwnerId , remotePtr . RemoteId , remotePtr . Name )
ri = RIFromMap ( m )
return nil
})
if txErr != nil {
return nil , txErr
}
return ri , nil
}
// internal function for UpdateRemoteState
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
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 ))
err := StoreStateDiff ( ctx , stateDiff )
if err != nil {
return err
}
}
return nil
}
func UpdateRemoteState ( ctx context . Context , sessionId string , windowId string , remotePtr RemotePtrType , feState FeStateType , stateBase * packet . ShellState , stateDiff * packet . ShellStateDiff ) ( * RemoteInstance , error ) {
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 () {
windowId = ""
}
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 {
err := validateSessionWindow ( tx , sessionId , windowId )
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
}
2022-08-24 13:21:54 -07:00
query := `SELECT * FROM remote_instance WHERE sessionid = ? AND windowid = ? AND remoteownerid = ? AND remoteid = ? AND name = ?`
2022-10-16 23:51:04 -07:00
m := tx . GetMap ( query , sessionId , windowId , remotePtr . OwnerId , remotePtr . RemoteId , remotePtr . Name )
ri = RIFromMap ( m )
if ri == nil {
ri = & RemoteInstance {
2022-12-19 18:52:08 -08:00
RIId : scbase . GenPromptUUID (),
2022-08-24 13:21:54 -07:00
Name : remotePtr . Name ,
SessionId : sessionId ,
WindowId : windowId ,
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
}
query = `INSERT INTO remote_instance ( riid, name, sessionid, windowid, remoteownerid, remoteid, festate, statebasehash, statediffhasharr)
VALUES (:riid,:name,:sessionid,:windowid,:remoteownerid,:remoteid,:festate,:statebasehash,:statediffhasharr)`
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 {
2022-11-28 18:03:02 -08:00
query = `UPDATE remote_instance SET festate = ?, statebasehash = ?, statediffhasharr = ? 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
}
2023-02-14 16:17:54 -08:00
tx . Exec ( query , quickJson ( ri . FeState ), ri . StateBaseHash , quickJsonArr ( ri . StateDiffHashArr ), 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
2022-08-24 02:14:16 -07:00
func UpdateCurRemote ( ctx context . Context , sessionId string , windowId string , remotePtr RemotePtrType ) error {
2022-08-17 12:24:09 -07:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT windowid FROM window WHERE sessionid = ? AND windowid = ?`
if ! tx . Exists ( query , sessionId , windowId ) {
2022-08-24 02:14:16 -07:00
return fmt . Errorf ( "cannot update curremote: no window found" )
2022-08-17 12:24:09 -07:00
}
2022-08-24 13:21:54 -07:00
query = `UPDATE window SET curremoteownerid = ?, curremoteid = ?, curremotename = ? WHERE sessionid = ? AND windowid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , remotePtr . OwnerId , remotePtr . RemoteId , remotePtr . Name , sessionId , windowId )
2022-08-17 12:24:09 -07:00
return nil
})
return txErr
}
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
func SetScreenOpts ( ctx context . Context , sessionId string , screenId string , opts * ScreenOptsType ) error {
if opts == nil {
return fmt . Errorf ( "invalid screen opts cannot be nil" )
}
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 screenopts = ? WHERE sessionid = ? AND screenid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , opts , sessionId , screenId )
2022-08-26 21:44:18 -07:00
return nil
})
return txErr
}
2022-08-26 22:01:29 -07:00
2023-01-02 12:09:01 -08:00
func ArchiveWindowLines ( ctx context . Context , sessionId string , windowId string ) ( * ModelUpdate , error ) {
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT windowid FROM window WHERE sessionid = ? AND windowid = ?`
if ! tx . Exists ( query , sessionId , windowId ) {
return fmt . Errorf ( "window does not exist" )
}
query = `UPDATE line SET archived = 1 WHERE sessionid = ? AND windowid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , sessionId , windowId )
2023-01-02 12:09:01 -08:00
return nil
})
if txErr != nil {
return nil , txErr
}
win , err := GetWindowById ( ctx , sessionId , windowId )
if err != nil {
return nil , err
}
2023-01-31 17:56:56 -08:00
return & ModelUpdate { Windows : [] * WindowType { win }}, nil
2023-01-02 12:09:01 -08:00
}
func PurgeWindowLines ( ctx context . Context , sessionId string , windowId string ) ( * ModelUpdate , error ) {
2022-08-26 22:01:29 -07:00
var lineIds [] string
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT windowid FROM window WHERE sessionid = ? AND windowid = ?`
if ! tx . Exists ( query , sessionId , windowId ) {
return fmt . Errorf ( "window does not exist" )
}
query = `SELECT lineid FROM line WHERE sessionid = ? AND windowid = ?`
lineIds = tx . SelectStrings ( query , sessionId , windowId )
query = `DELETE FROM line WHERE sessionid = ? AND windowid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , sessionId , windowId )
2023-01-02 12:09:01 -08:00
query = `DELETE FROM history WHERE sessionid = ? AND windowid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , sessionId , windowId )
2022-09-20 17:01:25 -07:00
query = `UPDATE window SET nextlinenum = 1 WHERE sessionid = ? AND windowid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , sessionId , windowId )
2022-08-26 22:01:29 -07:00
return nil
})
if txErr != nil {
return nil , txErr
}
2023-01-02 12:09:01 -08:00
go cleanSessionCmds ( context . Background (), sessionId )
2022-08-26 22:01:29 -07:00
win , err := GetWindowById ( ctx , sessionId , windowId )
if err != nil {
return nil , err
}
for _ , lineId := range lineIds {
line := & LineType {
SessionId : sessionId ,
WindowId : windowId ,
LineId : lineId ,
Remove : true ,
}
win . Lines = append ( win . Lines , line )
}
2023-01-31 17:56:56 -08:00
return & ModelUpdate { Windows : [] * WindowType { win }}, nil
2022-08-26 22:01:29 -07:00
}
2022-09-05 20:08:59 -07:00
func GetRunningWindowCmds ( ctx context . Context , sessionId string , windowId string ) ([] * CmdType , error ) {
var rtn [] * CmdType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT * from cmd WHERE cmdid IN (SELECT cmdid FROM line WHERE sessionid = ? AND windowid = ?) AND status = ?`
cmdMaps := tx . SelectMaps ( query , sessionId , windowId , CmdStatusRunning )
for _ , m := range cmdMaps {
rtn = append ( rtn , CmdFromMap ( m ))
}
return nil
})
if txErr != nil {
return nil , txErr
}
return rtn , nil
}
func UpdateCmdTermOpts ( ctx context . Context , sessionId string , cmdId string , termOpts TermOpts ) error {
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `UPDATE cmd SET termopts = ? WHERE sessionid = ? AND cmdid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , termOpts , sessionId , cmdId )
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
func WindowReset ( ctx context . Context , sessionId string , windowId string ) ([] * RemoteInstance , error ) {
var delRis [] * RemoteInstance
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT windowid FROM window WHERE sessionid = ? AND windowid = ?`
if ! tx . Exists ( query , sessionId , windowId ) {
return fmt . Errorf ( "window does not exist" )
}
query = `SELECT riid FROM remote_instance WHERE sessionid = ? AND windowid = ?`
riids := tx . SelectStrings ( query , sessionId , windowId )
for _ , riid := range riids {
ri := & RemoteInstance { SessionId : sessionId , WindowId : windowId , RIId : riid , Remove : true }
delRis = append ( delRis , ri )
}
query = `DELETE FROM remote_instance WHERE sessionid = ? AND windowid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , sessionId , windowId )
2023-01-31 17:56:56 -08:00
return nil
})
return delRis , txErr
}
2022-12-26 16:09:21 -08:00
func DeleteSession ( ctx context . Context , sessionId string ) ( UpdatePacket , error ) {
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 = `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
query = `DELETE FROM screen WHERE sessionid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , sessionId )
2022-12-26 16:09:21 -08:00
query = `DELETE FROM screen_window WHERE sessionid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , sessionId )
2022-12-26 16:09:21 -08:00
query = `DELETE FROM window WHERE sessionid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , sessionId )
2022-12-26 16:09:21 -08:00
query = `DELETE FROM history WHERE sessionid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , sessionId )
2022-12-26 16:09:21 -08:00
query = `DELETE FROM line WHERE sessionid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , sessionId )
2022-12-26 16:09:21 -08:00
query = `DELETE FROM cmd WHERE sessionid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , sessionId )
2023-02-20 21:39:29 -08:00
query = `DELETE FROM bookmark_cmd WHERE sessionid = ?`
tx . Exec ( query , sessionId )
2022-12-26 16:09:21 -08:00
newActiveSessionId , _ = fixActiveSessionId ( tx . Context ())
return nil
})
if txErr != nil {
return nil , txErr
}
delErr := DeleteSessionDir ( ctx , sessionId )
update := ModelUpdate {}
if newActiveSessionId != "" {
update . ActiveSessionId = newActiveSessionId
}
if delErr != nil {
update . Info = & InfoMsgType {
InfoMsg : fmt . Sprintf ( "error removing session files: %v" , delErr ),
}
}
update . Sessions = append ( update . Sessions , & SessionType { SessionId : sessionId , Remove : true })
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
}
2022-12-26 18:42:55 -08:00
func ArchiveSession ( ctx context . Context , sessionId string ) ( * ModelUpdate , 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 )
2022-12-26 18:42:55 -08:00
update := & ModelUpdate {}
2022-12-26 16:09:21 -08:00
if bareSession != nil {
update . Sessions = append ( update . Sessions , bareSession )
}
if newActiveSessionId != "" {
update . ActiveSessionId = newActiveSessionId
}
return update , nil
}
2022-12-26 18:42:55 -08:00
func UnArchiveSession ( ctx context . Context , sessionId string , activate bool ) ( * ModelUpdate , error ) {
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 )
update := & ModelUpdate {}
if bareSession != nil {
update . Sessions = append ( update . Sessions , bareSession )
}
if activate {
update . ActiveSessionId = sessionId
}
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 )
2022-09-20 14:15:20 -07:00
query = `SELECT count(*) FROM window WHERE sessionid = ?`
rtn . NumWindows = tx . GetInt ( query , sessionId )
query = `SELECT count(*) FROM line WHERE sessionid = ?`
rtn . NumLines = tx . GetInt ( query , sessionId )
query = `SELECT count(*) FROM cmd WHERE sessionid = ?`
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_AutoInstall = "autoinstall" // bool
RemoteField_SSHKey = "sshkey" // string
RemoteField_SSHPassword = "sshpassword" // string
RemoteField_Color = "color" // string
)
// editMap: alias, connectmode, autoinstall, sshkey, color, sshpassword (from constants)
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 autoInstall , found := editMap [ RemoteField_AutoInstall ]; found {
query = `UPDATE remote SET autoinstall = ? WHERE remoteid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , autoInstall , 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
}
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 (
2022-10-11 01:11:04 -07:00
SWField_AnchorLine = "anchorline" // int
SWField_AnchorOffset = "anchoroffset" // int
2022-10-07 01:08:03 -07:00
SWField_SelectedLine = "selectedline" // int
2022-10-11 01:11:04 -07:00
SWField_Focus = "focustype" // string
2022-10-06 18:33:54 -07:00
)
func UpdateScreenWindow ( ctx context . Context , sessionId string , screenId string , windowId string , editMap map [ string ] interface {}) ( * ScreenWindowType , error ) {
var rtn * ScreenWindowType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT sessionid FROM screen_window WHERE sessionid = ? AND screenid = ? AND windowid = ?`
if ! tx . Exists ( query , sessionId , screenId , windowId ) {
return fmt . Errorf ( "screen-window not found" )
}
2022-10-11 01:11:04 -07:00
if anchorLine , found := editMap [ SWField_AnchorLine ]; found {
query = `UPDATE screen_window SET anchor = json_set(anchor, '$.anchorline', ?) WHERE sessionid = ? AND screenid = ? AND windowid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , anchorLine , sessionId , screenId , windowId )
2022-10-11 01:11:04 -07:00
}
if anchorOffset , found := editMap [ SWField_AnchorOffset ]; found {
query = `UPDATE screen_window SET anchor = json_set(anchor, '$.anchoroffset', ?) WHERE sessionid = ? AND screenid = ? AND windowid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , anchorOffset , sessionId , screenId , windowId )
2022-10-06 18:33:54 -07:00
}
2022-10-07 01:08:03 -07:00
if sline , found := editMap [ SWField_SelectedLine ]; found {
query = `UPDATE screen_window SET selectedline = ? WHERE sessionid = ? AND screenid = ? AND windowid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , sline , sessionId , screenId , windowId )
2022-10-07 01:08:03 -07:00
}
2022-10-11 01:11:04 -07:00
if focusType , found := editMap [ SWField_Focus ]; found {
query = `UPDATE screen_window SET focustype = ? WHERE sessionid = ? AND screenid = ? AND windowid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , focusType , sessionId , screenId , windowId )
2022-10-11 01:11:04 -07:00
}
2022-10-06 18:33:54 -07:00
var sw ScreenWindowType
query = `SELECT * FROM screen_window WHERE sessionid = ? AND screenid = ? AND windowid = ?`
2023-02-14 16:17:54 -08:00
found := tx . Get ( & sw , query , sessionId , screenId , windowId )
2022-10-06 18:33:54 -07:00
if found {
rtn = & sw
}
return nil
})
if txErr != nil {
return nil , txErr
}
return rtn , nil
}
2022-10-06 23:58:38 -07:00
2022-10-07 01:08:03 -07:00
func GetScreenWindowByIds ( ctx context . Context , sessionId string , screenId string , windowId string ) ( * ScreenWindowType , error ) {
var rtn * ScreenWindowType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
var sw ScreenWindowType
query := `SELECT * FROM screen_window WHERE sessionid = ? AND screenid = ? AND windowid = ?`
2023-02-14 16:17:54 -08:00
found := tx . Get ( & sw , query , sessionId , screenId , windowId )
2022-10-07 01:08:03 -07:00
if found {
rtn = & sw
}
return nil
})
if txErr != nil {
return nil , txErr
}
return rtn , nil
}
2022-10-06 23:58:38 -07:00
func GetLineResolveItems ( ctx context . Context , sessionId string , windowId string ) ([] ResolveItem , error ) {
var rtn [] ResolveItem
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT lineid as id, linenum as num FROM line WHERE sessionid = ? AND windowid = ? ORDER BY linenum`
2023-02-14 16:17:54 -08:00
tx . Select ( & rtn , query , sessionId , windowId )
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
func UpdateSWsWithCmdFg ( ctx context . Context , sessionId string , cmdId string ) ([] * ScreenWindowType , error ) {
var rtn [] * ScreenWindowType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT sessionid, screenid, windowid
FROM screen_window sw
WHERE
sessionid = ?
AND focustype = 'cmd-fg'
AND selectedline IN (SELECT linenum
FROM line l
WHERE l.sessionid = sw.sessionid
AND l.windowid = sw.windowid
AND l.cmdid = ?
)`
var swKeys [] SWKey
2023-02-14 16:17:54 -08:00
tx . Select ( & swKeys , query , sessionId , cmdId )
2022-10-11 23:11:43 -07:00
if len ( swKeys ) == 0 {
return nil
}
for _ , key := range swKeys {
editMap := make ( map [ string ] interface {})
editMap [ SWField_Focus ] = SWFocusInput
sw , err := UpdateScreenWindow ( tx . Context (), key . SessionId , key . ScreenId , key . WindowId , editMap )
if err != nil {
return err
}
rtn = append ( rtn , sw )
}
return nil
})
if txErr != nil {
return nil , txErr
}
return rtn , nil
}
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 ()
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
}
// returns error when not found
2022-11-28 18:03:02 -08:00
func GetFullState ( ctx context . Context , ssPtr 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
}
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 = ?`
m := tx . GetMap ( query , diffHash )
stateDiff := StateDiffFromMap ( m )
if stateDiff == nil {
return fmt . Errorf ( "ShellStateDiff %s not found" , diffHash )
}
var ssDiff packet . ShellStateDiff
err = ssDiff . DecodeShellStateDiff ( stateDiff . Data )
if err != nil {
return err
}
newState , err := shexec . ApplyShellStateDiff ( * state , ssDiff )
if err != nil {
return fmt . Errorf ( "GetFullState, diff[%d]:%s: %v" , idx , diffHash , err )
}
state = & newState
}
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
func UpdateLineStar ( ctx context . Context , lineId string , starVal int ) error {
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `UPDATE line SET star = ? WHERE lineid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , starVal , lineId )
2022-12-05 22:59:00 -08:00
return nil
})
if txErr != nil {
return txErr
}
return nil
}
2023-01-31 22:21:19 -08:00
func UpdateLineHeight ( ctx context . Context , lineId string , heightVal int ) error {
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `UPDATE line SET contentheight = ? WHERE lineid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , heightVal , lineId )
2023-01-31 22:21:19 -08:00
return nil
})
if txErr != nil {
return txErr
}
return nil
}
2022-12-05 22:59:00 -08:00
// can return nil, nil if line is not found
2023-02-20 21:39:29 -08:00
func GetLineById ( ctx context . Context , sessionId string , windowId string , lineId string ) ( * LineType , error ) {
2022-12-05 22:59:00 -08:00
var rtn * LineType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
var line LineType
2023-02-20 21:39:29 -08:00
query := `SELECT * FROM line WHERE sessionid = ? AND windowid = ? AND lineid = ?`
found := tx . Get ( & line , query , sessionId , windowId , lineId )
2022-12-05 22:59:00 -08:00
if found {
rtn = & line
}
return nil
})
if txErr != nil {
return nil , txErr
}
return rtn , nil
}
2022-12-21 17:45:40 -08:00
2022-12-27 23:12:27 -08:00
func SetLineArchivedById ( ctx context . Context , lineId string , archived bool ) error {
2022-12-21 17:45:40 -08:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-12-27 23:12:27 -08:00
query := `UPDATE line SET archived = ? WHERE lineid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , archived , lineId )
2022-12-21 17:45:40 -08:00
return nil
})
return txErr
}
func purgeCmdById ( ctx context . Context , sessionId string , cmdId string ) error {
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `DELETE FROM cmd WHERE sessionid = ? AND cmdid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , sessionId , cmdId )
2022-12-21 17:45:40 -08:00
return DeletePtyOutFile ( tx . Context (), sessionId , cmdId )
})
return txErr
}
func PurgeLineById ( ctx context . Context , sessionId string , lineId string ) error {
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT cmdid FROM line WHERE sessionid = ? AND lineid = ?`
cmdId := tx . GetString ( query , sessionId , lineId )
query = `DELETE FROM line WHERE sessionid = ? AND lineid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , sessionId , lineId )
2023-01-02 12:09:01 -08:00
query = `DELETE FROM history WHERE sessionid = ? AND lineid = ?`
2023-02-14 16:17:54 -08:00
tx . Exec ( query , sessionId , lineId )
2022-12-21 17:45:40 -08:00
if cmdId != "" {
query = `SELECT count(*) FROM line WHERE sessionid = ? AND cmdid = ?`
cmdRefCount := tx . GetInt ( query , sessionId , cmdId )
if cmdRefCount == 0 {
err := purgeCmdById ( tx . Context (), sessionId , cmdId )
if err != nil {
return err
}
}
}
return nil
})
return txErr
}
2022-12-30 17:01:17 -08:00
func GetRIsForWindow ( ctx context . Context , sessionId string , windowId string ) ([] * RemoteInstance , error ) {
var rtn [] * RemoteInstance
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT * FROM remote_instance WHERE sessionid = ? AND (windowid = '' OR windowid = ?)`
riMaps := tx . SelectMaps ( query , sessionId , windowId )
for _ , m := range riMaps {
ri := RIFromMap ( m )
if ri != nil {
rtn = append ( rtn , ri )
}
}
return nil
})
if txErr != nil {
return nil , txErr
}
return rtn , nil
}
2023-01-16 23:36:52 -08:00
2023-01-23 12:54:32 -08:00
func GetCurDayStr () string {
2023-01-16 23:36:52 -08:00
now := time . Now ()
dayStr := now . Format ( "2006-01-02" )
2023-01-23 12:54:32 -08:00
return dayStr
}
func UpdateCurrentActivity ( ctx context . Context , update ActivityUpdate ) error {
now := time . Now ()
dayStr := GetCurDayStr ()
2023-01-16 23:36:52 -08:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-01-23 12:54:32 -08:00
var tdata TelemetryData
query := `SELECT tdata FROM activity WHERE day = ?`
2023-02-14 16:17:54 -08:00
found := tx . Get ( & tdata , query , dayStr )
2023-01-23 12:54:32 -08:00
if ! found {
2023-02-23 15:17:47 -08:00
query = `INSERT INTO activity (day, uploaded, tdata, tzname, tzoffset, clientversion, clientarch, buildtime, osrelease)
VALUES (?, 0, ?, ?, ?, ?, ? , ? , ?)`
2023-01-16 23:36:52 -08:00
tzName , tzOffset := now . Zone ()
if len ( tzName ) > MaxTzNameLen {
tzName = tzName [ 0 : MaxTzNameLen ]
}
2023-02-23 15:17:47 -08:00
tx . Exec ( query , dayStr , tdata , tzName , tzOffset , scbase . PromptVersion , scbase . ClientArch (), scbase . BuildTime , scbase . MacOSRelease ())
2023-01-16 23:36:52 -08:00
}
2023-01-23 12:54:32 -08:00
tdata . NumCommands += update . NumCommands
tdata . FgMinutes += update . FgMinutes
tdata . ActiveMinutes += update . ActiveMinutes
tdata . OpenMinutes += update . OpenMinutes
2023-02-21 22:41:56 -08:00
tdata . ClickShared += update . ClickShared
tdata . HistoryView += update . HistoryView
tdata . BookmarksView += update . BookmarksView
if update . NumConns > 0 {
tdata . NumConns = update . NumConns
}
2023-01-17 16:02:44 -08:00
query = `UPDATE activity
2023-01-23 12:54:32 -08:00
SET tdata = ?,
2023-02-23 15:17:47 -08:00
clientversion = ?,
buildtime = ?
2023-01-17 16:02:44 -08:00
WHERE day = ?`
2023-02-23 15:17:47 -08:00
tx . Exec ( query , tdata , scbase . PromptVersion , scbase . BuildTime , dayStr )
2023-01-16 23:36:52 -08:00
return nil
})
if txErr != nil {
return txErr
}
return nil
}
func GetNonUploadedActivity ( ctx context . Context ) ([] * ActivityType , error ) {
var rtn [] * ActivityType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT * FROM activity WHERE uploaded = 0 ORDER BY day DESC LIMIT 30`
2023-02-14 16:17:54 -08:00
tx . Select ( & rtn , query )
2023-01-16 23:36:52 -08:00
return nil
})
if txErr != nil {
return nil , txErr
}
return rtn , nil
}
2023-01-23 12:54:32 -08:00
// note, will not mark the current day as uploaded
2023-01-16 23:36:52 -08:00
func MarkActivityAsUploaded ( ctx context . Context , activityArr [] * ActivityType ) error {
2023-01-23 12:54:32 -08:00
dayStr := GetCurDayStr ()
2023-01-16 23:36:52 -08:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `UPDATE activity SET uploaded = 1 WHERE day = ?`
for _ , activity := range activityArr {
2023-01-23 12:54:32 -08:00
if activity . Day == dayStr {
continue
}
2023-02-14 16:17:54 -08:00
tx . Exec ( query , activity . Day )
2023-01-16 23:36:52 -08:00
}
return nil
})
return txErr
}
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-02-20 22:00:07 -08:00
type bookmarkOrderType struct {
BookmarkId string
OrderIdx int64
}
type bookmarkCmdType struct {
BookmarkId string
SessionId string
CmdId string
}
func GetBookmarks ( ctx context . Context , tag string ) ([] * BookmarkType , error ) {
var bms [] * BookmarkType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
var query string
var marr [] map [ string ] interface {}
if tag == "" {
query = `SELECT * FROM bookmark`
marr = tx . SelectMaps ( query )
} else {
query = `SELECT * FROM bookmark WHERE EXISTS (SELECT 1 FROM json_each(tags) WHERE value = ?)`
marr = tx . SelectMaps ( query , tag )
}
bmMap := make ( map [ string ] * BookmarkType )
for _ , m := range marr {
bm := BookmarkFromMap ( m )
bms = append ( bms , bm )
bmMap [ bm . BookmarkId ] = bm
}
var orders [] bookmarkOrderType
query = `SELECT bookmarkid, orderidx FROM bookmark_order WHERE tag = ?`
tx . Select ( & orders , query , tag )
for _ , bmOrder := range orders {
bm := bmMap [ bmOrder . BookmarkId ]
if bm != nil {
bm . OrderIdx = bmOrder . OrderIdx
}
}
var cmds [] bookmarkCmdType
query = `SELECT bookmarkid, sessionid, cmdid FROM bookmark_cmd`
tx . Select ( & cmds , query )
for _ , cmd := range cmds {
bm := bmMap [ cmd . BookmarkId ]
if bm != nil {
bm . Cmds = append ( bm . Cmds , base . MakeCommandKey ( cmd . SessionId , cmd . CmdId ))
}
}
return nil
})
if txErr != nil {
return nil , txErr
}
return bms , nil
}
2023-02-21 18:03:13 -08:00
func GetBookmarkById ( ctx context . Context , bookmarkId string , tag string ) ( * BookmarkType , error ) {
var rtn * BookmarkType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT * FROM bookmark WHERE bookmarkid = ?`
m := tx . GetMap ( query , bookmarkId )
rtn = BookmarkFromMap ( m )
if rtn == nil {
return nil
}
query = `SELECT orderidx FROM bookmark_order WHERE bookmarkid = ? AND tag = ?`
orderIdx := tx . GetInt ( query , bookmarkId , tag )
rtn . OrderIdx = int64 ( orderIdx )
query = `SELECT bookmarkid, sessionid, cmdid FROM bookmark_cmd WHERE bookmarkid = ?`
var cmds [] bookmarkCmdType
tx . Select ( & cmds , query , bookmarkId )
for _ , cmd := range cmds {
rtn . Cmds = append ( rtn . Cmds , base . MakeCommandKey ( cmd . SessionId , cmd . CmdId ))
}
return nil
})
if txErr != nil {
return nil , txErr
}
return rtn , nil
}
func GetBookmarkIdByArg ( ctx context . Context , bookmarkArg string ) ( string , error ) {
var rtnId string
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
if len ( bookmarkArg ) == 8 {
query := `SELECT bookmarkid FROM bookmark WHERE bookmarkid LIKE (? || '%')`
rtnId = tx . GetString ( query , bookmarkArg )
return nil
}
query := `SELECT bookmarkid FROM bookmark WHERE bookmarkid = ?`
rtnId = tx . GetString ( query , bookmarkArg )
return nil
})
if txErr != nil {
return "" , txErr
}
return rtnId , nil
}
2023-02-20 22:00:07 -08:00
// ignores OrderIdx field
2023-02-20 21:39:29 -08:00
func InsertBookmark ( ctx context . Context , bm * BookmarkType ) error {
if bm == nil || bm . BookmarkId == "" {
return fmt . Errorf ( "invalid empty bookmark id" )
}
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT bookmarkid FROM bookmark WHERE bookmarkid = ?`
if tx . Exists ( query , bm . BookmarkId ) {
return fmt . Errorf ( "bookmarkid already exists" )
}
query = `INSERT INTO bookmark ( bookmarkid, createdts, cmdstr, alias, tags, description)
VALUES (:bookmarkid,:createdts,:cmdstr,:alias,:tags,:description)`
tx . NamedExec ( query , bm . ToMap ())
for _ , tag := range append ( bm . Tags , "" ) {
query = `SELECT COALESCE(max(orderidx), 0) FROM bookmark_order WHERE tag = ?`
maxOrder := tx . GetInt ( query , tag )
query = `INSERT INTO bookmark_order (tag, bookmarkid, orderidx) VALUES (?, ?, ?)`
tx . Exec ( query , tag , bm . BookmarkId , maxOrder + 1 )
}
query = `INSERT INTO bookmark_cmd (bookmarkid, sessionid, cmdid) VALUES (?, ?, ?)`
2023-02-20 22:00:07 -08:00
for _ , ck := range bm . Cmds {
2023-02-20 21:39:29 -08:00
tx . Exec ( query , bm . BookmarkId , ck . GetSessionId (), ck . GetCmdId ())
}
query = `UPDATE line SET bookmarked = 1 WHERE sessionid = ? AND cmdid = ?`
2023-02-20 22:00:07 -08:00
for _ , ck := range bm . Cmds {
2023-02-20 21:39:29 -08:00
tx . Exec ( query , ck . GetSessionId (), ck . GetCmdId ())
}
return nil
})
return txErr
}
2023-02-21 18:03:13 -08:00
const (
BookmarkField_Desc = "desc"
BookmarkField_CmdStr = "cmdstr"
)
func EditBookmark ( ctx context . Context , bookmarkId string , editMap map [ string ] interface {}) error {
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT bookmarkid FROM bookmark WHERE bookmarkid = ?`
if ! tx . Exists ( query , bookmarkId ) {
return fmt . Errorf ( "bookmark not found" )
}
if desc , found := editMap [ BookmarkField_Desc ]; found {
query = `UPDATE bookmark SET description = ? WHERE bookmarkid = ?`
tx . Exec ( query , desc , bookmarkId )
}
if cmdStr , found := editMap [ BookmarkField_CmdStr ]; found {
query = `UPDATE bookmark SET cmdstr = ? WHERE bookmarkid = ?`
tx . Exec ( query , cmdStr , bookmarkId )
}
return nil
})
return txErr
}
2023-02-20 21:39:29 -08:00
func fixupBookmarkOrder ( tx * TxWrap ) {
query := `
WITH new_order AS (
SELECT tag, bookmarkid, row_number() OVER (PARTITION BY tag ORDER BY orderidx) AS newidx FROM bookmark_order
)
UPDATE bookmark_order
SET orderidx = new_order.newidx
FROM new_order
WHERE bookmark_order.tag = new_order.tag AND bookmark_order.bookmarkid = new_order.bookmarkid
`
tx . Exec ( query )
}
func DeleteBookmark ( ctx context . Context , bookmarkId string ) error {
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := `SELECT bookmarkid FROM bookmark WHERE bookmarkid = ?`
if ! tx . Exists ( query , bookmarkId ) {
return fmt . Errorf ( "bookmark not found" )
}
query = `DELETE FROM bookmark WHERE bookmarkid = ?`
tx . Exec ( query , bookmarkId )
query = `DELETE FROM bookmark_order WHERE bookmarkid = ?`
tx . Exec ( query , bookmarkId )
query = `UPDATE line SET bookmarked = 0 WHERE bookmarked AND cmdid <> '' AND (sessionid||cmdid) IN (SELECT sessionid||cmdid FROM bookmark_cmd WHERE bookmarkid = ?) `
tx . Exec ( query , bookmarkId )
query = `DELETE FROM bookmark_cmd WHERE bookmarkid = ?`
tx . Exec ( query , bookmarkId )
fixupBookmarkOrder ( tx )
return nil
})
return txErr
}