2022-07-01 23:07:13 +02:00
package sstore
import (
"context"
"database/sql"
"fmt"
2022-07-13 06:51:17 +02:00
"strings"
2022-07-01 23:07:13 +02:00
"github.com/google/uuid"
2022-07-08 01:29:14 +02:00
"github.com/scripthaus-dev/mshell/pkg/packet"
2022-07-01 23:07:13 +02:00
)
2022-08-30 04:18:02 +02:00
const HistoryCols = "historyid, ts, userid, sessionid, screenid, windowid, lineid, cmdid, haderror, cmdstr, remoteownerid, remoteid, remotename, ismetacmd"
const DefaultMaxHistoryItems = 1000
2022-07-01 23:07:13 +02:00
func NumSessions ( ctx context . Context ) ( int , error ) {
2022-07-13 01:10:46 +02:00
db , err := GetDB ( ctx )
2022-07-01 23:07:13 +02:00
if err != nil {
return 0 , err
}
query := "SELECT count(*) FROM session"
var count int
err = db . GetContext ( ctx , & count , query )
if err != nil {
return 0 , err
}
return count , nil
}
2022-07-02 02:38:36 +02:00
func GetAllRemotes ( ctx context . Context ) ( [ ] * RemoteType , error ) {
2022-07-07 22:26:46 +02:00
var rtn [ ] * RemoteType
err := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-09-14 21:06:55 +02:00
query := ` SELECT * FROM remote ORDER BY remoteidx `
2022-07-07 22:26:46 +02:00
marr := tx . SelectMaps ( query )
for _ , m := range marr {
rtn = append ( rtn , RemoteFromMap ( m ) )
}
return nil
} )
2022-07-02 02:38:36 +02:00
if err != nil {
return nil , err
}
2022-07-07 22:26:46 +02:00
return rtn , nil
2022-07-02 02:38:36 +02:00
}
2022-08-17 21:24:09 +02: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-17 00:08:28 +02:00
func GetRemoteById ( ctx context . Context , remoteId string ) ( * RemoteType , error ) {
2022-07-07 22:26:46 +02:00
var remote * RemoteType
err := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-08-17 00:08:28 +02:00
query := ` SELECT * FROM remote WHERE remoteid = ? `
m := tx . GetMap ( query , remoteId )
2022-07-07 22:26:46 +02:00
remote = RemoteFromMap ( m )
return nil
} )
2022-07-01 23:07:13 +02:00
if err != nil {
return nil , err
}
2022-07-07 22:26:46 +02:00
return remote , nil
2022-07-01 23:07:13 +02:00
}
2022-09-14 02:11:36 +02: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-17 00:08:28 +02:00
func GetRemoteByPhysicalId ( ctx context . Context , physicalId string ) ( * RemoteType , error ) {
2022-07-07 22:26:46 +02:00
var remote * RemoteType
err := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-08-17 00:08:28 +02:00
query := ` SELECT * FROM remote WHERE physicalid = ? `
m := tx . GetMap ( query , physicalId )
2022-07-07 22:26:46 +02:00
remote = RemoteFromMap ( m )
return nil
} )
2022-07-01 23:07:13 +02:00
if err != nil {
return nil , err
}
2022-07-07 22:26:46 +02:00
return remote , nil
2022-07-01 23:07:13 +02:00
}
2022-09-14 02:11:36 +02:00
func UpsertRemote ( ctx context . Context , r * RemoteType ) error {
if r == nil {
2022-07-01 23:07:13 +02:00
return fmt . Errorf ( "cannot insert nil remote" )
}
2022-09-14 02:11:36 +02:00
if r . RemoteId == "" {
2022-09-01 21:47:10 +02:00
return fmt . Errorf ( "cannot insert remote without id" )
2022-07-01 23:07:13 +02:00
}
2022-09-14 02:11:36 +02:00
if r . RemoteCanonicalName == "" {
2022-09-01 21:47:10 +02:00
return fmt . Errorf ( "cannot insert remote with canonicalname" )
}
2022-09-14 02:11:36 +02:00
if r . RemoteType == "" {
2022-09-01 21:47:10 +02:00
return fmt . Errorf ( "cannot insert remote without type" )
2022-07-01 23:07:13 +02:00
}
2022-09-01 21:47:10 +02:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT remoteid FROM remote WHERE remoteid = ? `
2022-09-14 02:11:36 +02:00
if tx . Exists ( query , r . RemoteId ) {
tx . ExecWrap ( ` DELETE FROM remote WHERE remoteid = ? ` , r . RemoteId )
2022-09-01 21:47:10 +02:00
}
2022-09-14 02:11:36 +02: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 21:47:10 +02:00
}
2022-09-20 23:23:53 +02:00
query = ` SELECT COALESCE(max(remoteidx), 0) FROM remote `
2022-09-14 21:06:55 +02:00
maxRemoteIdx := tx . GetInt ( query )
r . RemoteIdx = int64 ( maxRemoteIdx + 1 )
2022-09-01 21:47:10 +02:00
query = ` INSERT INTO remote
2022-09-20 23:23:53 +02:00
( remoteid , physicalid , remotetype , remotealias , remotecanonicalname , remotesudo , remoteuser , remotehost , connectmode , initpk , sshopts , remoteopts , lastconnectts , archived , remoteidx ) VALUES
( : remoteid , : physicalid , : remotetype , : remotealias , : remotecanonicalname , : remotesudo , : remoteuser , : remotehost , : connectmode , : initpk , : sshopts , : remoteopts , : lastconnectts , : archived , : remoteidx ) `
2022-09-14 02:11:36 +02:00
tx . NamedExecWrap ( query , r . ToMap ( ) )
2022-09-13 21:06:12 +02:00
return nil
} )
return txErr
}
2022-08-11 21:07:41 +02:00
func InsertHistoryItem ( ctx context . Context , hitem * HistoryItemType ) error {
if hitem == nil {
return fmt . Errorf ( "cannot insert nil history item" )
}
db , err := GetDB ( ctx )
if err != nil {
return err
}
2022-08-28 23:24:05 +02:00
query := ` INSERT INTO history ( historyid , ts , userid , sessionid , screenid , windowid , lineid , cmdid , haderror , cmdstr , remoteownerid , remoteid , remotename , ismetacmd ) VALUES
( : historyid , : ts , : userid , : sessionid , : screenid , : windowid , : lineid , : cmdid , : haderror , : cmdstr , : remoteownerid , : remoteid , : remotename , : ismetacmd ) `
_ , err = db . NamedExec ( query , hitem . ToMap ( ) )
2022-08-11 21:07:41 +02:00
if err != nil {
return err
}
return nil
}
2022-08-30 04:18:02 +02: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-12 08:45:15 +02:00
var rtn [ ] * HistoryItemType
2022-08-30 04:18:02 +02: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 23:24:05 +02:00
}
2022-08-12 08:45:15 +02:00
return nil
} )
2022-08-30 04:18:02 +02:00
if txErr != nil {
return nil , txErr
2022-08-12 08:45:15 +02:00
}
return rtn , nil
}
2022-08-09 01:21:46 +02:00
func GetBareSessions ( ctx context . Context ) ( [ ] * SessionType , error ) {
var rtn [ ] * SessionType
err := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-08-27 01:21:19 +02:00
query := ` SELECT * FROM session ORDER BY sessionidx `
2022-08-09 01:21:46 +02:00
tx . SelectWrap ( & rtn , query )
return nil
} )
if err != nil {
return nil , err
}
return rtn , nil
}
2022-08-27 01:21:19 +02:00
func GetAllSessionIds ( ctx context . Context ) ( [ ] string , error ) {
var rtn [ ] string
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT sessionid from session ORDER by sessionidx `
rtn = tx . SelectStrings ( query )
return nil
} )
if txErr != nil {
return nil , txErr
}
return rtn , nil
}
2022-08-26 22:12:17 +02: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 = ? `
tx . GetWrap ( & rtn , query , sessionId )
return nil
} )
if txErr != nil {
return nil , txErr
}
if rtn . SessionId == "" {
return nil , nil
}
return & rtn , nil
}
2022-08-27 01:21:19 +02:00
func GetAllSessions ( ctx context . Context ) ( * ModelUpdate , error ) {
2022-07-08 22:23:45 +02:00
var rtn [ ] * SessionType
2022-08-27 01:21:19 +02:00
var activeSessionId string
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-07-12 22:50:44 +02:00
query := ` SELECT * FROM session `
tx . SelectWrap ( & rtn , query )
2022-08-11 03:33:32 +02:00
sessionMap := make ( map [ string ] * SessionType )
2022-08-09 01:21:46 +02:00
for _ , session := range rtn {
2022-08-11 03:33:32 +02:00
sessionMap [ session . SessionId ] = session
2022-08-09 01:21:46 +02:00
session . Full = true
}
2022-07-13 06:51:17 +02:00
var screens [ ] * ScreenType
query = ` SELECT * FROM screen ORDER BY screenidx `
tx . SelectWrap ( & screens , query )
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 `
tx . SelectWrap ( & sws , query )
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 11:14:16 +02:00
query = ` SELECT * FROM remote_instance `
2022-08-11 03:33:32 +02:00
var ris [ ] * RemoteInstance
tx . SelectWrap ( & ris , query )
for _ , ri := range ris {
s := sessionMap [ ri . SessionId ]
if s != nil {
s . Remotes = append ( s . Remotes , ri )
}
}
2022-08-27 01:21:19 +02:00
query = ` SELECT activesessionid FROM client `
activeSessionId = tx . GetString ( query )
2022-07-12 22:50:44 +02:00
return nil
} )
2022-08-27 01:21:19 +02:00
if txErr != nil {
return nil , txErr
}
return & ModelUpdate { Sessions : rtn , ActiveSessionId : activeSessionId } , nil
2022-07-12 22:50:44 +02: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 11:14:16 +02:00
m := tx . GetMap ( query , sessionId , windowId )
if m == nil {
2022-07-12 22:50:44 +02:00
return nil
}
2022-08-24 11:14:16 +02:00
rtnWindow = WindowFromMap ( m )
2022-07-12 22:50:44 +02:00
query = ` SELECT * FROM line WHERE sessionid = ? AND windowid = ? `
2022-08-24 11:14:16 +02:00
tx . SelectWrap ( & rtnWindow . Lines , query , sessionId , windowId )
2022-07-12 22:50:44 +02: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 11:14:16 +02:00
rtnWindow . Cmds = append ( rtnWindow . Cmds , CmdFromMap ( m ) )
2022-07-12 22:50:44 +02:00
}
return nil
} )
return rtnWindow , err
2022-07-08 22:23:45 +02:00
}
2022-07-15 03:39:40 +02:00
func GetSessionScreens ( ctx context . Context , sessionId string ) ( [ ] * ScreenType , error ) {
var rtn [ ] * ScreenType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT * FROM screen WHERE sessionid = ? ORDER BY screenidx `
tx . SelectWrap ( & rtn , query , sessionId )
return nil
} )
return rtn , txErr
}
2022-07-01 23:07:13 +02:00
func GetSessionById ( ctx context . Context , id string ) ( * SessionType , error ) {
2022-08-27 01:21:19 +02:00
allSessionsUpdate , err := GetAllSessions ( ctx )
2022-07-01 23:07:13 +02:00
if err != nil {
return nil , err
}
2022-08-27 01:21:19 +02:00
allSessions := allSessionsUpdate . Sessions
2022-08-09 01:21:46 +02:00
for _ , session := range allSessions {
if session . SessionId == id {
return session , nil
}
}
return nil , nil
2022-07-01 23:07:13 +02:00
}
2022-07-05 07:18:01 +02:00
func GetSessionByName ( ctx context . Context , name string ) ( * SessionType , error ) {
2022-07-13 01:10:46 +02:00
db , err := GetDB ( ctx )
2022-07-05 07:18:01 +02:00
if err != nil {
return nil , err
}
var sessionId string
query := ` SELECT sessionid FROM session WHERE name = ? `
err = db . GetContext ( ctx , & sessionId , query , name )
if err != nil {
if err == sql . ErrNoRows {
return nil , nil
}
return nil , err
}
return GetSessionById ( ctx , sessionId )
}
2022-07-08 22:23:45 +02:00
// also creates default window, returns sessionId
// if sessionName == "", it will be generated
2022-08-24 11:14:16 +02:00
func InsertSessionWithName ( ctx context . Context , sessionName string , activate bool ) ( UpdatePacket , error ) {
2022-07-08 22:23:45 +02:00
newSessionId := uuid . New ( ) . String ( )
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-07-13 06:51:17 +02: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-08-24 22:21:54 +02:00
query := ` INSERT INTO session (sessionid, name, activescreenid, sessionidx, notifynum, ownerid, sharemode, accesskey) VALUES (?, ?, '', ?, ?, '', 'local', '') `
2022-07-13 06:51:17 +02:00
tx . ExecWrap ( query , newSessionId , sessionName , maxSessionIdx + 1 , 0 )
2022-07-15 03:39:40 +02:00
_ , err := InsertScreen ( tx . Context ( ) , newSessionId , "" , true )
2022-07-13 06:51:17 +02:00
if err != nil {
return err
2022-07-08 22:23:45 +02:00
}
2022-08-27 01:21:19 +02:00
if activate {
query = ` UPDATE client SET activesessionid = ? `
tx . ExecWrap ( query , newSessionId )
}
2022-07-01 23:07:13 +02:00
return nil
} )
2022-07-15 03:39:40 +02:00
if txErr != nil {
2022-08-09 01:21:46 +02:00
return nil , txErr
}
session , err := GetSessionById ( ctx , newSessionId )
if err != nil {
return nil , err
}
2022-08-24 11:14:16 +02:00
update := ModelUpdate {
2022-08-09 01:21:46 +02:00
Sessions : [ ] * SessionType { session } ,
}
if activate {
update . ActiveSessionId = newSessionId
2022-07-15 03:39:40 +02:00
}
2022-08-09 01:21:46 +02:00
return update , nil
2022-07-08 22:23:45 +02:00
}
2022-08-30 01:31:06 +02:00
func SetActiveSessionId ( ctx context . Context , sessionId string ) error {
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT sessionid FROM session WHERE sessionid = ? `
if ! tx . Exists ( query , sessionId ) {
return fmt . Errorf ( "cannot switch to session, not found" )
}
query = ` UPDATE client SET activesessionid = ? `
tx . ExecWrap ( query , sessionId )
return nil
} )
return txErr
}
2022-07-08 22:23:45 +02:00
func containsStr ( strs [ ] string , testStr string ) bool {
for _ , s := range strs {
if s == testStr {
return true
}
}
return false
}
2022-07-13 06:51:17 +02: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-07-13 01:10:46 +02:00
}
2022-08-27 02:17:33 +02:00
func InsertScreen ( ctx context . Context , sessionId string , origScreenName string , activate bool ) ( UpdatePacket , error ) {
2022-07-13 06:51:17 +02:00
var newScreenId string
2022-07-08 22:23:45 +02:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-07-13 06:51:17 +02:00
query := ` SELECT sessionid FROM session WHERE sessionid = ? `
if ! tx . Exists ( query , sessionId ) {
return fmt . Errorf ( "cannot create screen, no session found" )
2022-07-08 22:23:45 +02:00
}
2022-08-24 11:14:16 +02: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-07-13 06:51:17 +02:00
maxScreenIdx := tx . GetInt ( ` SELECT COALESCE(max(screenidx), 0) FROM screen WHERE sessionid = ? ` , sessionId )
screenNames := tx . SelectStrings ( ` SELECT name FROM screen WHERE sessionid = ? ` , sessionId )
2022-08-27 02:17:33 +02:00
screenName := fmtUniqueName ( origScreenName , "s%d" , maxScreenIdx + 1 , screenNames )
2022-07-13 06:51:17 +02:00
newScreenId = uuid . New ( ) . String ( )
2022-08-24 22:21:54 +02:00
query = ` INSERT INTO screen (sessionid, screenid, name, activewindowid, screenidx, screenopts, ownerid, sharemode) VALUES (?, ?, ?, ?, ?, ?, '', 'local') `
2022-07-13 06:51:17 +02:00
tx . ExecWrap ( query , sessionId , newScreenId , screenName , newWindowId , maxScreenIdx + 1 , ScreenOptsType { } )
layout := LayoutType { Type : LayoutFull }
query = ` INSERT INTO screen_window (sessionid, screenid, windowid, name, layout) VALUES (?, ?, ?, ?, ?) `
tx . ExecWrap ( query , sessionId , newScreenId , newWindowId , DefaultScreenWindowName , layout )
2022-07-15 03:39:40 +02:00
if activate {
query = ` UPDATE session SET activescreenid = ? WHERE sessionid = ? `
tx . ExecWrap ( query , newScreenId , sessionId )
}
2022-07-08 22:23:45 +02:00
return nil
} )
2022-07-15 10:57:45 +02:00
newScreen , err := GetScreenById ( ctx , sessionId , newScreenId )
if err != nil {
2022-07-16 02:53:23 +02:00
return nil , err
2022-07-15 10:57:45 +02:00
}
update , session := MakeSingleSessionUpdate ( sessionId )
if activate {
session . ActiveScreenId = newScreenId
}
session . Screens = append ( session . Screens , newScreen )
2022-07-16 02:53:23 +02:00
return update , txErr
2022-07-01 23:07:13 +02:00
}
2022-07-02 22:31:56 +02:00
2022-07-15 03:39:40 +02: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
found := tx . GetWrap ( & screen , query , sessionId , screenId )
if ! found {
return nil
}
rtnScreen = & screen
query = ` SELECT * FROM screen_window WHERE sessionid = ? AND screenid = ? `
tx . SelectWrap ( & screen . Windows , query , sessionId , screenId )
2022-07-15 10:57:45 +02:00
screen . Full = true
2022-07-15 03:39:40 +02:00
return nil
} )
if txErr != nil {
return nil , txErr
}
return rtnScreen , nil
}
2022-08-24 11:14:16 +02:00
func txCreateWindow ( tx * TxWrap , sessionId string , curRemote RemotePtrType ) string {
w := & WindowType {
2022-09-21 02:01:25 +02:00
SessionId : sessionId ,
WindowId : uuid . New ( ) . String ( ) ,
CurRemote : curRemote ,
NextLineNum : 1 ,
WinOpts : WindowOptsType { } ,
ShareMode : ShareModeLocal ,
ShareOpts : WindowShareOptsType { } ,
2022-08-24 11:14:16 +02:00
}
wmap := w . ToMap ( )
2022-09-21 02:01:25 +02: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 ) `
2022-08-24 11:14:16 +02:00
tx . NamedExecWrap ( query , wmap )
return w . WindowId
2022-07-12 22:50:44 +02:00
}
2022-07-08 06:39:25 +02:00
func InsertLine ( ctx context . Context , line * LineType , cmd * CmdType ) error {
2022-07-02 22:31:56 +02:00
if line == nil {
return fmt . Errorf ( "line cannot be nil" )
}
2022-08-16 21:08:26 +02:00
if line . LineId == "" {
return fmt . Errorf ( "line must have lineid set" )
2022-07-02 22:31:56 +02:00
}
2022-09-21 02:01:25 +02:00
if line . LineNum != 0 {
return fmt . Errorf ( "line should not hage linenum set" )
}
2022-07-02 22:31:56 +02:00
return WithTx ( ctx , func ( tx * TxWrap ) error {
var windowId string
query := ` SELECT windowid FROM window WHERE sessionid = ? AND windowid = ? `
hasWindow := tx . GetWrap ( & windowId , query , line . SessionId , line . WindowId )
if ! hasWindow {
return fmt . Errorf ( "window not found, cannot insert line[%s/%s]" , line . SessionId , line . WindowId )
}
2022-09-21 02:01:25 +02:00
query = ` SELECT nextlinenum FROM window WHERE sessionid = ? AND windowid = ? `
nextLineNum := tx . GetInt ( query , line . SessionId , line . WindowId )
line . LineNum = int64 ( nextLineNum )
query = ` INSERT INTO line ( sessionid , windowid , userid , lineid , ts , linenum , linenumtemp , linelocal , linetype , text , cmdid , ephemeral )
VALUES ( : sessionid , : windowid , : userid , : lineid , : ts , : linenum , : linenumtemp , : linelocal , : linetype , : text , : cmdid , : ephemeral ) `
2022-07-02 22:31:56 +02:00
tx . NamedExecWrap ( query , line )
2022-09-21 02:01:25 +02:00
query = ` UPDATE window SET nextlinenum = ? WHERE sessionid = ? AND windowid = ? `
tx . ExecWrap ( query , nextLineNum + 1 , line . SessionId , line . WindowId )
2022-07-08 06:39:25 +02:00
if cmd != nil {
cmdMap := cmd . ToMap ( )
query = `
2022-08-24 22:21:54 +02:00
INSERT INTO cmd ( sessionid , cmdid , remoteownerid , remoteid , remotename , cmdstr , remotestate , termopts , status , startpk , donepk , runout , usedrows )
VALUES ( : sessionid , : cmdid , : remoteownerid , : remoteid , : remotename , : cmdstr , : remotestate , : termopts , : status , : startpk , : donepk , : runout , : usedrows )
2022-07-07 09:10:37 +02:00
`
2022-07-08 06:39:25 +02:00
tx . NamedExecWrap ( query , cmdMap )
}
2022-07-07 09:10:37 +02:00
return nil
} )
}
2022-07-07 22:26:46 +02: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 09:10:37 +02:00
if err != nil {
return nil , err
}
2022-07-07 22:26:46 +02:00
return cmd , nil
2022-07-07 09:10:37 +02:00
}
2022-07-08 01:29:14 +02:00
2022-08-20 02:14:53 +02:00
func UpdateCmdDonePk ( ctx context . Context , donePk * packet . CmdDonePacketType ) ( UpdatePacket , error ) {
2022-07-08 01:29:14 +02:00
if donePk == nil || donePk . CK . IsEmpty ( ) {
2022-08-20 02:14:53 +02:00
return nil , fmt . Errorf ( "invalid cmddone packet (no ck)" )
2022-07-08 01:29:14 +02:00
}
2022-08-20 02:14:53 +02:00
var rtnCmd * CmdType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-07-08 01:29:14 +02:00
query := ` UPDATE cmd SET status = ?, donepk = ? WHERE sessionid = ? AND cmdid = ? `
tx . ExecWrap ( query , CmdStatusDone , quickJson ( donePk ) , donePk . CK . GetSessionId ( ) , donePk . CK . GetCmdId ( ) )
2022-08-20 02:14:53 +02:00
var err error
rtnCmd , err = GetCmdById ( tx . Context ( ) , donePk . CK . GetSessionId ( ) , donePk . CK . GetCmdId ( ) )
if err != nil {
return err
}
2022-07-08 01:29:14 +02:00
return nil
} )
2022-08-20 02:14:53 +02:00
if txErr != nil {
return nil , txErr
}
if rtnCmd == nil {
return nil , fmt . Errorf ( "cmd data not found for ck[%s]" , donePk . CK )
}
2022-08-24 11:14:16 +02:00
return ModelUpdate { Cmd : rtnCmd } , nil
2022-07-08 01:29:14 +02: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 = ? `
tx . ExecWrap ( query , quickJson ( errPk ) , errPk . CK . GetSessionId ( ) , errPk . CK . GetCmdId ( ) )
return nil
} )
}
func HangupAllRunningCmds ( ctx context . Context ) error {
return WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` UPDATE cmd SET status = ? WHERE status = ? `
tx . ExecWrap ( query , CmdStatusHangup , CmdStatusRunning )
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 = ? `
tx . ExecWrap ( query , CmdStatusHangup , CmdStatusRunning , remoteId )
return nil
} )
}
2022-07-15 03:39:40 +02:00
2022-07-15 10:57:45 +02:00
func getNextId ( ids [ ] string , delId string ) string {
fmt . Printf ( "getnextid %v | %v\n" , ids , delId )
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-16 02:53:23 +02:00
func SwitchScreenById ( ctx context . Context , sessionId string , screenId string ) ( UpdatePacket , error ) {
2022-07-15 03:39:40 +02: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 switch to screen, screen=%s does not exist in session=%s" , screenId , sessionId )
}
query = ` UPDATE session SET activescreenid = ? WHERE sessionid = ? `
tx . ExecWrap ( query , screenId , sessionId )
return nil
} )
2022-07-15 10:57:45 +02:00
update , session := MakeSingleSessionUpdate ( sessionId )
session . ActiveScreenId = screenId
2022-07-16 02:53:23 +02:00
return update , txErr
2022-07-15 03:39:40 +02:00
}
2022-07-15 10:57:45 +02:00
func CleanWindows ( ) {
}
2022-07-16 02:53:23 +02:00
func DeleteScreen ( ctx context . Context , sessionId string , screenId string ) ( UpdatePacket , error ) {
2022-07-15 10:57:45 +02:00
var newActiveScreenId string
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
isActive := tx . Exists ( ` SELECT sessionid FROM session WHERE sessionid = ? AND activescreenid = ? ` , sessionId , screenId )
fmt . Printf ( "delete-screen %s %s | %v\n" , sessionId , screenId , isActive )
if isActive {
screenIds := tx . SelectStrings ( ` SELECT screenid FROM screen WHERE sessionid = ? ORDER BY screenidx ` , sessionId )
nextId := getNextId ( screenIds , screenId )
tx . ExecWrap ( ` UPDATE session SET activescreenid = ? WHERE sessionid = ? ` , nextId , sessionId )
newActiveScreenId = nextId
}
query := ` DELETE FROM screen_window WHERE sessionid = ? AND screenid = ? `
tx . ExecWrap ( query , sessionId , screenId )
query = ` DELETE FROM screen WHERE sessionid = ? AND screenid = ? `
tx . ExecWrap ( query , sessionId , screenId )
return nil
} )
if txErr != nil {
2022-07-16 02:53:23 +02:00
return nil , txErr
2022-07-15 10:57:45 +02:00
}
go CleanWindows ( )
update , session := MakeSingleSessionUpdate ( sessionId )
session . ActiveScreenId = newActiveScreenId
session . Screens = append ( session . Screens , & ScreenType { SessionId : sessionId , ScreenId : screenId , Remove : true } )
2022-07-16 02:53:23 +02:00
return update , nil
2022-07-15 10:57:45 +02:00
}
2022-07-16 02:37:32 +02:00
2022-08-24 11:14:16 +02:00
func GetRemoteState ( ctx context . Context , sessionId string , windowId string , remotePtr RemotePtrType ) ( * RemoteState , error ) {
2022-07-16 02:37:32 +02:00
var remoteState * RemoteState
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
var ri RemoteInstance
2022-08-24 22:21:54 +02:00
query := ` SELECT * FROM remote_instance WHERE sessionid = ? AND windowid = ? AND remoteownerid = ? AND remoteid = ? AND name = ? `
found := tx . GetWrap ( & ri , query , sessionId , windowId , remotePtr . OwnerId , remotePtr . RemoteId , remotePtr . Name )
2022-07-16 02:37:32 +02:00
if found {
remoteState = & ri . State
return nil
}
return nil
} )
2022-08-24 11:14:16 +02:00
return remoteState , txErr
2022-07-16 02:37:32 +02:00
}
2022-08-09 23:24:57 +02:00
2022-08-24 11:14:16 +02: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 23:24:57 +02:00
query := ` SELECT windowid FROM window WHERE sessionid = ? AND windowid = ? `
if ! tx . Exists ( query , sessionId , windowId ) {
2022-08-24 11:14:16 +02:00
return fmt . Errorf ( "no window found" )
}
return nil
}
}
func UpdateRemoteState ( ctx context . Context , sessionId string , windowId string , remotePtr RemotePtrType , state RemoteState ) ( * RemoteInstance , error ) {
if remotePtr . IsSessionScope ( ) {
windowId = ""
}
var ri RemoteInstance
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
err := validateSessionWindow ( tx , sessionId , windowId )
if err != nil {
return fmt . Errorf ( "cannot update remote instance cwd: %w" , err )
2022-08-09 23:24:57 +02:00
}
2022-08-24 22:21:54 +02:00
query := ` SELECT * FROM remote_instance WHERE sessionid = ? AND windowid = ? AND remoteownerid = ? AND remoteid = ? AND name = ? `
found := tx . GetWrap ( & ri , query , sessionId , windowId , remotePtr . OwnerId , remotePtr . RemoteId , remotePtr . Name )
2022-08-09 23:24:57 +02:00
if ! found {
ri = RemoteInstance {
2022-08-24 22:21:54 +02:00
RIId : uuid . New ( ) . String ( ) ,
Name : remotePtr . Name ,
SessionId : sessionId ,
WindowId : windowId ,
RemoteOwnerId : remotePtr . OwnerId ,
RemoteId : remotePtr . RemoteId ,
State : state ,
2022-08-09 23:24:57 +02:00
}
2022-08-24 22:21:54 +02:00
query = ` INSERT INTO remote_instance ( riid , name , sessionid , windowid , remoteownerid , remoteid , state )
VALUES ( : riid , : name , : sessionid , : windowid , : remoteownerid , : remoteid , : state ) `
2022-08-09 23:24:57 +02:00
tx . NamedExecWrap ( query , ri )
return nil
}
2022-08-24 22:21:54 +02:00
query = ` UPDATE remote_instance SET state = ? WHERE sessionid = ? AND windowid = ? AND remoteownerid = ? AND remoteid = ? AND name = ? `
2022-08-23 03:38:52 +02:00
ri . State = state
2022-08-24 22:21:54 +02:00
tx . ExecWrap ( query , ri . State , ri . SessionId , ri . WindowId , remotePtr . OwnerId , remotePtr . RemoteId , remotePtr . Name )
2022-08-09 23:24:57 +02:00
return nil
} )
return & ri , txErr
}
2022-08-17 21:24:09 +02:00
2022-08-24 11:14:16 +02:00
func UpdateCurRemote ( ctx context . Context , sessionId string , windowId string , remotePtr RemotePtrType ) error {
2022-08-17 21:24:09 +02: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 11:14:16 +02:00
return fmt . Errorf ( "cannot update curremote: no window found" )
2022-08-17 21:24:09 +02:00
}
2022-08-24 22:21:54 +02:00
query = ` UPDATE window SET curremoteownerid = ?, curremoteid = ?, curremotename = ? WHERE sessionid = ? AND windowid = ? `
tx . ExecWrap ( query , remotePtr . OwnerId , remotePtr . RemoteId , remotePtr . Name , sessionId , windowId )
2022-08-17 21:24:09 +02:00
return nil
} )
return txErr
}
2022-08-26 22:12:17 +02: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-08-27 01:21:19 +02:00
query := ` SELECT sessionid FROM session ORDER BY sessionidx, name, sessionid `
2022-08-26 22:12:17 +02:00
ids := tx . SelectStrings ( query )
if sessionId != "" {
ids = reorderStrings ( ids , sessionId , newIndex )
}
2022-08-27 01:21:19 +02:00
query = ` UPDATE session SET sessionid = ? WHERE sessionid = ? `
2022-08-26 22:12:17 +02:00
for idx , id := range ids {
tx . ExecWrap ( query , id , idx + 1 )
}
return nil
} )
return txErr
}
func SetSessionName ( ctx context . Context , sessionId string , name string ) error {
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-08-27 01:21:19 +02:00
query := ` SELECT sessionid FROM session WHERE sessionid = ? `
2022-08-26 22:12:17 +02:00
if ! tx . Exists ( query , sessionId ) {
return fmt . Errorf ( "session does not exist" )
}
2022-08-27 02:17:33 +02:00
query = ` SELECT sessionid FROM session WHERE name = ? `
dupSessionId := tx . GetString ( query , name )
if dupSessionId == sessionId {
return nil
}
2022-08-27 02:51:28 +02:00
if dupSessionId != "" {
2022-08-27 02:17:33 +02:00
return fmt . Errorf ( "invalid duplicate session name '%s'" , name )
}
2022-08-27 01:21:19 +02:00
query = ` UPDATE session SET name = ? WHERE sessionid = ? `
2022-08-26 22:12:17 +02:00
tx . ExecWrap ( query , name , sessionId )
return nil
} )
return txErr
}
2022-08-27 02:51:28 +02: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 = ` SELECT screenid FROM screen WHERE sessionid = ? AND name = ? `
dupScreenId := tx . GetString ( query , sessionId , name )
if dupScreenId == screenId {
return nil
}
if dupScreenId != "" {
return fmt . Errorf ( "invalid duplicate screen name '%s'" , name )
}
query = ` UPDATE screen SET name = ? WHERE sessionid = ? AND screenid = ? `
tx . ExecWrap ( query , name , sessionId , screenId )
return nil
} )
return txErr
}
2022-08-27 06:44:18 +02: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 = ? `
tx . ExecWrap ( query , opts , sessionId , screenId )
return nil
} )
return txErr
}
2022-08-27 07:01:29 +02:00
func ClearWindow ( ctx context . Context , sessionId string , windowId string ) ( * ModelUpdate , error ) {
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 = ? `
tx . ExecWrap ( query , sessionId , windowId )
2022-09-21 02:01:25 +02:00
query = ` UPDATE window SET nextlinenum = 1 WHERE sessionid = ? AND windowid = ? `
tx . ExecWrap ( query , sessionId , windowId )
2022-08-27 07:01:29 +02:00
return nil
} )
if txErr != nil {
return nil , txErr
}
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 )
}
return & ModelUpdate { Window : win } , nil
}
2022-09-06 05:08:59 +02: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 = ? `
tx . ExecWrap ( query , termOpts , sessionId , cmdId )
return nil
} )
return txErr
}
2022-09-13 21:06:12 +02:00
func DeleteSession ( ctx context . Context , sessionId string ) error {
return nil
}
2022-09-20 23:15:20 +02: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" )
}
query = ` SELECT count(*) FROM screen WHERE sessionid = ? `
rtn . NumScreens = tx . GetInt ( query , sessionId )
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
}