2022-07-01 23:07:13 +02:00
package sstore
import (
"context"
2023-03-25 20:54:56 +01:00
"errors"
2022-07-01 23:07:13 +02:00
"fmt"
2022-09-21 02:37:49 +02:00
"strconv"
2022-07-13 06:51:17 +02:00
"strings"
2023-02-15 01:17:54 +01:00
"sync"
2022-11-28 09:13:00 +01:00
"time"
2022-07-01 23:07:13 +02:00
"github.com/google/uuid"
2023-02-15 01:17:54 +01:00
"github.com/jmoiron/sqlx"
"github.com/sawka/txwrap"
2022-11-27 23:12:15 +01:00
"github.com/scripthaus-dev/mshell/pkg/base"
2022-07-08 01:29:14 +02:00
"github.com/scripthaus-dev/mshell/pkg/packet"
2022-11-28 09:13:00 +01:00
"github.com/scripthaus-dev/mshell/pkg/shexec"
2023-03-27 23:11:02 +02:00
"github.com/scripthaus-dev/sh2-server/pkg/dbutil"
2022-09-21 02:37:49 +02:00
"github.com/scripthaus-dev/sh2-server/pkg/scbase"
2022-07-01 23:07:13 +02:00
)
2023-03-18 05:36:49 +01:00
const HistoryCols = "h.historyid, h.ts, h.userid, h.sessionid, h.screenid, h.lineid, h.cmdid, h.haderror, h.cmdstr, h.remoteownerid, h.remoteid, h.remotename, h.ismetacmd, h.incognito, h.linenum"
2022-08-30 04:18:02 +02:00
const DefaultMaxHistoryItems = 1000
2023-03-27 08:07:30 +02:00
var updateWriterCVar = sync . NewCond ( & sync . Mutex { } )
var updateWriterMoreData = false
2023-03-28 09:24:37 +02:00
var WebScreenPtyPosLock = & sync . Mutex { }
var WebScreenPtyPosDelIntent = make ( map [ string ] bool ) // map[screenid + ":" + lineid] -> bool
2023-03-27 08:07:30 +02:00
2023-02-15 01:17:54 +01: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 )
}
2023-03-27 08:07:30 +02:00
func NotifyUpdateWriter ( ) {
updateWriterCVar . L . Lock ( )
defer updateWriterCVar . L . Unlock ( )
updateWriterMoreData = true
updateWriterCVar . Signal ( )
}
func UpdateWriterCheckMoreData ( ) {
updateWriterCVar . L . Lock ( )
defer updateWriterCVar . L . Unlock ( )
for {
if updateWriterMoreData {
updateWriterMoreData = false
break
}
updateWriterCVar . Wait ( )
}
}
2022-07-01 23:07:13 +02:00
func NumSessions ( ctx context . Context ) ( int , error ) {
2022-10-11 02:30:48 +02: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 23:07:13 +02:00
}
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 {
2023-03-27 23:11:02 +02:00
rtn = append ( rtn , dbutil . FromMap [ * RemoteType ] ( m ) )
2022-07-07 22:26:46 +02:00
}
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 )
2023-03-27 23:11:02 +02:00
remote = dbutil . FromMap [ * RemoteType ] ( m )
2022-08-17 21:24:09 +02:00
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 )
2023-03-27 23:11:02 +02:00
remote = dbutil . FromMap [ * RemoteType ] ( m )
2022-07-07 22:26:46 +02:00
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-10-04 20:45:24 +02:00
func GetLocalRemote ( ctx context . Context ) ( * RemoteType , error ) {
var remote * RemoteType
err := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT * FROM remote WHERE local `
m := tx . GetMap ( query )
2023-03-27 23:11:02 +02:00
remote = dbutil . FromMap [ * RemoteType ] ( m )
2022-10-04 20:45:24 +02:00
return nil
} )
if err != nil {
return nil , err
}
return remote , nil
}
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 = ? `
2023-03-27 23:11:02 +02:00
remote = dbutil . GetMapGen [ * RemoteType ] ( tx , query , cname )
2022-09-14 02:11:36 +02:00
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 = ? `
2023-03-27 23:11:02 +02:00
remote = dbutil . GetMapGen [ * RemoteType ] ( tx , query , physicalId )
2022-07-07 22:26:46 +02:00
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 ) {
2023-02-15 01:17:54 +01:00
tx . Exec ( ` 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-10-01 01:23:40 +02:00
query = ` SELECT remoteid FROM remote WHERE remotealias = ? `
2022-10-01 01:05:48 +02:00
if r . RemoteAlias != "" && tx . Exists ( query , r . RemoteAlias ) {
return fmt . Errorf ( "remote has duplicate alias '%s', cannot create" , r . RemoteAlias )
}
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-11-28 09:13:00 +01: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-15 01:17:54 +01:00
tx . NamedExec ( 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" )
}
2022-10-11 02:30:48 +02:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` INSERT INTO history
2023-03-18 05:36:49 +01:00
( historyid , ts , userid , sessionid , screenid , lineid , cmdid , haderror , cmdstr , remoteownerid , remoteid , remotename , ismetacmd , incognito , linenum ) VALUES
( : historyid , : ts , : userid , : sessionid , : screenid , : lineid , : cmdid , : haderror , : cmdstr , : remoteownerid , : remoteid , : remotename , : ismetacmd , : incognito , : linenum ) `
2023-02-15 01:17:54 +01:00
tx . NamedExec ( query , hitem . ToMap ( ) )
2022-10-11 02:30:48 +02:00
return nil
} )
return txErr
2022-08-11 21:07:41 +02:00
}
2022-12-20 03:52:08 +01:00
func IsIncognitoScreen ( ctx context . Context , sessionId string , screenId string ) ( bool , error ) {
2023-03-13 23:54:35 +01:00
return false , nil
2022-12-20 03:52:08 +01:00
}
2023-03-06 20:47:44 +01:00
const HistoryQueryChunkSize = 1000
func _getNextHistoryItem ( items [ ] * HistoryItemType , index int , filterFn func ( * HistoryItemType ) bool ) ( * HistoryItemType , int ) {
for ; index < len ( items ) ; index ++ {
item := items [ index ]
if filterFn ( item ) {
return item , index
}
}
return nil , index
}
2023-03-06 22:54:38 +01:00
// returns true if done, false if we still need to process more items
2023-03-06 20:47:44 +01:00
func ( result * HistoryQueryResult ) processItem ( item * HistoryItemType , rawOffset int ) bool {
2023-03-07 00:42:50 +01:00
if result . prevItems < result . Offset {
2023-03-06 22:54:38 +01:00
result . prevItems ++
return false
}
2023-03-06 20:47:44 +01:00
if len ( result . Items ) == result . MaxItems {
result . HasMore = true
result . NextRawOffset = rawOffset
2023-03-06 22:54:38 +01:00
return true
2023-03-06 20:47:44 +01:00
}
2023-03-07 00:42:50 +01:00
if len ( result . Items ) == 0 {
result . RawOffset = rawOffset
}
2023-03-06 20:47:44 +01:00
result . Items = append ( result . Items , item )
2023-03-06 22:54:38 +01:00
return false
2023-03-06 20:47:44 +01:00
}
2023-03-06 22:54:38 +01:00
func runHistoryQueryWithFilter ( tx * TxWrap , opts HistoryQueryOpts ) ( * HistoryQueryResult , error ) {
2023-03-06 20:47:44 +01:00
if opts . MaxItems == 0 {
return nil , fmt . Errorf ( "invalid query, maxitems is 0" )
}
2023-03-07 00:42:50 +01:00
rtn := & HistoryQueryResult { Offset : opts . Offset , MaxItems : opts . MaxItems }
2023-03-06 22:54:38 +01:00
var rawOffset int
if opts . RawOffset >= opts . Offset {
rtn . prevItems = opts . Offset
rawOffset = opts . RawOffset
} else {
rawOffset = 0
2023-03-06 20:47:44 +01:00
}
for {
resultItems , err := runHistoryQuery ( tx , opts , rawOffset , HistoryQueryChunkSize )
if err != nil {
return nil , err
}
isDone := false
for resultIdx := 0 ; resultIdx < len ( resultItems ) ; resultIdx ++ {
2023-03-06 22:54:38 +01:00
if opts . FilterFn != nil && ! opts . FilterFn ( resultItems [ resultIdx ] ) {
2023-03-06 20:47:44 +01:00
continue
}
isDone = rtn . processItem ( resultItems [ resultIdx ] , rawOffset + resultIdx )
if isDone {
break
}
}
if isDone {
break
}
if len ( resultItems ) < HistoryQueryChunkSize {
break
}
2023-03-06 22:54:38 +01:00
rawOffset += HistoryQueryChunkSize
2023-03-06 20:47:44 +01:00
}
return rtn , nil
}
func runHistoryQuery ( tx * TxWrap , opts HistoryQueryOpts , realOffset int , itemLimit int ) ( [ ] * HistoryItemType , error ) {
2023-03-15 00:37:22 +01:00
// check sessionid/screenid format because we are directly inserting them into the SQL
2023-03-04 03:12:24 +01:00
if opts . SessionId != "" {
_ , err := uuid . Parse ( opts . SessionId )
2022-08-30 04:18:02 +02:00
if err != nil {
return nil , fmt . Errorf ( "malformed sessionid" )
}
}
2023-03-15 00:37:22 +01:00
if opts . ScreenId != "" {
_ , err := uuid . Parse ( opts . ScreenId )
2022-08-30 04:18:02 +02:00
if err != nil {
2023-03-15 00:37:22 +01:00
return nil , fmt . Errorf ( "malformed screenid" )
2022-08-30 04:18:02 +02:00
}
}
2023-03-04 03:12:24 +01:00
if opts . RemoteId != "" {
_ , err := uuid . Parse ( opts . RemoteId )
if err != nil {
return nil , fmt . Errorf ( "malformed remoteid" )
}
}
whereClause := "WHERE 1"
2023-03-02 09:31:19 +01:00
var queryArgs [ ] interface { }
2023-03-15 20:27:17 +01:00
hNumStr := ""
2023-03-15 00:37:22 +01:00
if opts . SessionId != "" && opts . ScreenId != "" {
2023-03-15 20:27:17 +01:00
whereClause += fmt . Sprintf ( " AND h.sessionid = '%s' AND h.screenid = '%s'" , opts . SessionId , opts . ScreenId )
hNumStr = ""
2023-03-04 03:12:24 +01:00
} else if opts . SessionId != "" {
2023-03-15 20:27:17 +01:00
whereClause += fmt . Sprintf ( " AND h.sessionid = '%s'" , opts . SessionId )
hNumStr = "s"
2022-08-30 04:18:02 +02:00
} else {
2023-03-15 20:27:17 +01:00
hNumStr = "g"
2022-08-30 04:18:02 +02:00
}
2023-03-02 09:31:19 +01:00
if opts . SearchText != "" {
2023-03-15 20:27:17 +01:00
whereClause += " AND h.cmdstr LIKE ? ESCAPE '\\'"
2023-03-02 09:31:19 +01:00
likeArg := opts . SearchText
likeArg = strings . ReplaceAll ( likeArg , "%" , "\\%" )
likeArg = strings . ReplaceAll ( likeArg , "_" , "\\_" )
queryArgs = append ( queryArgs , "%" + likeArg + "%" )
}
2023-03-04 03:12:24 +01:00
if opts . FromTs > 0 {
2023-03-15 20:27:17 +01:00
whereClause += fmt . Sprintf ( " AND h.ts <= %d" , opts . FromTs )
2023-03-04 03:12:24 +01:00
}
if opts . RemoteId != "" {
2023-03-15 20:27:17 +01:00
whereClause += fmt . Sprintf ( " AND h.remoteid = '%s'" , opts . RemoteId )
2023-03-04 03:12:24 +01:00
}
if opts . NoMeta {
2023-03-15 20:27:17 +01:00
whereClause += " AND NOT h.ismetacmd"
2023-03-04 03:12:24 +01:00
}
2023-03-18 05:36:49 +01:00
query := fmt . Sprintf ( "SELECT %s, ('%s' || CAST((row_number() OVER win) as text)) historynum FROM history h %s WINDOW win AS (ORDER BY h.ts, h.historyid) ORDER BY h.ts DESC, h.historyid DESC LIMIT %d OFFSET %d" , HistoryCols , hNumStr , whereClause , itemLimit , realOffset )
2023-03-02 09:31:19 +01:00
marr := tx . SelectMaps ( query , queryArgs ... )
2022-08-30 04:18:02 +02:00
rtn := make ( [ ] * HistoryItemType , len ( marr ) )
for idx , m := range marr {
2023-03-27 23:11:02 +02:00
hitem := dbutil . FromMap [ * HistoryItemType ] ( m )
2022-08-30 04:18:02 +02:00
rtn [ idx ] = hitem
}
return rtn , nil
}
2023-03-06 20:47:44 +01:00
func GetHistoryItems ( ctx context . Context , opts HistoryQueryOpts ) ( * HistoryQueryResult , error ) {
var rtn * HistoryQueryResult
2022-08-30 04:18:02 +02:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
var err error
2023-03-06 22:54:38 +01:00
rtn , err = runHistoryQueryWithFilter ( tx , opts )
2022-08-30 04:18:02 +02:00
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
}
2023-03-20 20:19:48 +01:00
func GetHistoryItemByLineNum ( ctx context . Context , screenId string , lineNum int ) ( * HistoryItemType , error ) {
return WithTxRtn ( ctx , func ( tx * TxWrap ) ( * HistoryItemType , error ) {
query := ` SELECT * FROM history WHERE screenid = ? AND linenum = ? `
2023-03-27 23:11:02 +02:00
hitem := dbutil . GetMapGen [ * HistoryItemType ] ( tx , query , screenId , lineNum )
2023-03-20 20:19:48 +01:00
return hitem , nil
} )
}
func GetLastHistoryLineNum ( ctx context . Context , screenId string ) ( int , error ) {
return WithTxRtn ( ctx , func ( tx * TxWrap ) ( int , error ) {
query := ` SELECT COALESCE(max(linenum), 0) FROM history WHERE screenid = ? `
maxLineNum := tx . GetInt ( query , screenId )
return maxLineNum , nil
} )
}
2022-12-25 22:03:11 +01:00
// includes archived sessions
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-12-27 01:09:21 +01:00
query := ` SELECT * FROM session ORDER BY archived, sessionidx, archivedts `
2023-02-15 01:17:54 +01:00
tx . Select ( & rtn , query )
2022-08-09 01:21:46 +02:00
return nil
} )
if err != nil {
return nil , err
}
return rtn , nil
}
2022-12-27 01:09:21 +01:00
// does not include archived, finds lowest sessionidx (for resetting active session)
func GetFirstSessionId ( ctx context . Context ) ( string , error ) {
2022-08-27 01:21:19 +02:00
var rtn [ ] string
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-12-25 22:03:11 +01:00
query := ` SELECT sessionid from session WHERE NOT archived ORDER by sessionidx `
2022-08-27 01:21:19 +02:00
rtn = tx . SelectStrings ( query )
return nil
} )
if txErr != nil {
2022-12-27 01:09:21 +01:00
return "" , txErr
2022-08-27 01:21:19 +02:00
}
2022-12-27 01:09:21 +01:00
if len ( rtn ) == 0 {
return "" , nil
}
return rtn [ 0 ] , nil
2022-08-27 01:21:19 +02:00
}
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 = ? `
2023-02-15 01:17:54 +01:00
tx . Get ( & rtn , query , sessionId )
2022-08-26 22:12:17 +02:00
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 ) {
2023-03-13 18:50:29 +01:00
return WithTxRtn ( ctx , func ( tx * TxWrap ) ( * ModelUpdate , error ) {
update := & ModelUpdate { }
2022-12-27 01:09:21 +01:00
query := ` SELECT * FROM session ORDER BY archived, sessionidx, archivedts `
2023-03-13 18:50:29 +01:00
tx . Select ( & update . Sessions , query )
2022-08-11 03:33:32 +02:00
sessionMap := make ( map [ string ] * SessionType )
2023-03-13 18:50:29 +01:00
for _ , session := range update . Sessions {
2022-08-11 03:33:32 +02:00
sessionMap [ session . SessionId ] = session
2022-08-09 01:21:46 +02:00
session . Full = true
}
2022-12-25 22:21:48 +01:00
query = ` SELECT * FROM screen ORDER BY archived, screenidx, archivedts `
2023-03-27 23:11:02 +02:00
update . Screens = dbutil . SelectMapsGen [ * ScreenType ] ( tx , query )
2023-03-13 20:10:23 +01:00
for _ , screen := range update . Screens {
screen . Full = true
}
2022-08-24 11:14:16 +02:00
query = ` SELECT * FROM remote_instance `
2023-03-27 23:11:02 +02:00
riArr := dbutil . SelectMapsGen [ * RemoteInstance ] ( tx , query )
2023-03-10 03:44:01 +01:00
for _ , ri := range riArr {
2022-08-11 03:33:32 +02:00
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 `
2023-03-13 18:50:29 +01:00
update . ActiveSessionId = tx . GetString ( query )
return update , nil
2022-07-12 22:50:44 +02:00
} )
}
2023-03-13 18:50:29 +01:00
func GetScreenLinesById ( ctx context . Context , screenId string ) ( * ScreenLinesType , error ) {
return WithTxRtn ( ctx , func ( tx * TxWrap ) ( * ScreenLinesType , error ) {
2023-03-16 02:12:55 +01:00
query := ` SELECT screenid FROM screen WHERE screenid = ? `
2023-03-27 23:11:02 +02:00
screen := dbutil . GetMappable [ * ScreenLinesType ] ( tx , query , screenId )
2023-03-13 09:52:30 +01:00
if screen == nil {
return nil , nil
2022-07-12 22:50:44 +02:00
}
2023-03-21 03:20:57 +01:00
query = ` SELECT * FROM line WHERE screenid = ? ORDER BY linenum `
tx . Select ( & screen . Lines , query , screen . ScreenId )
query = ` SELECT * FROM cmd WHERE cmdid IN (SELECT cmdid FROM line WHERE screenid = ?) `
2023-03-27 23:11:02 +02:00
screen . Cmds = dbutil . SelectMapsGen [ * CmdType ] ( tx , query , screen . ScreenId )
2023-03-13 09:52:30 +01:00
return screen , nil
2022-07-12 22:50:44 +02:00
} )
2022-07-08 22:23:45 +02:00
}
2023-03-15 00:37:22 +01:00
// includes archived screens
2023-03-13 20:10:23 +01:00
func GetSessionScreens ( ctx context . Context , sessionId string ) ( [ ] * ScreenType , error ) {
return WithTxRtn ( ctx , func ( tx * TxWrap ) ( [ ] * ScreenType , error ) {
2022-12-25 22:21:48 +01:00
query := ` SELECT * FROM screen WHERE sessionid = ? ORDER BY archived, screenidx, archivedts `
2023-03-27 23:11:02 +02:00
rtn := dbutil . SelectMapsGen [ * ScreenType ] ( tx , query , sessionId )
2023-03-13 20:10:23 +01:00
for _ , screen := range rtn {
screen . Full = true
}
return rtn , nil
2022-12-24 00:56:29 +01:00
} )
}
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-10-11 02:30:48 +02: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-05 07:18:01 +02:00
}
2022-10-11 02:30:48 +02: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-05 07:18:01 +02:00
}
2022-10-11 02:30:48 +02:00
return session , nil
2022-07-05 07:18:01 +02:00
}
2023-03-10 03:44:01 +01:00
func InsertCloudSession ( ctx context . Context , sessionName string , shareMode string , activate bool ) ( * ModelUpdate , error ) {
var updateRtn * ModelUpdate
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
var err error
updateRtn , err = InsertSessionWithName ( tx . Context ( ) , sessionName , shareMode , activate )
if err != nil {
return err
}
sessionId := updateRtn . Sessions [ 0 ] . SessionId
fmt . Printf ( "sessionid: %v\n" , sessionId )
return nil
} )
if txErr != nil {
return nil , txErr
}
return updateRtn , nil
}
2023-03-15 00:37:22 +01:00
// returns sessionId
2022-07-08 22:23:45 +02:00
// if sessionName == "", it will be generated
2023-03-10 03:44:01 +01:00
func InsertSessionWithName ( ctx context . Context , sessionName string , shareMode string , activate bool ) ( * ModelUpdate , error ) {
2023-03-13 20:23:36 +01:00
var newScreen * ScreenType
2022-12-20 03:52:08 +01:00
newSessionId := scbase . GenPromptUUID ( )
2022-07-08 22:23:45 +02:00
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 ` )
2023-03-09 02:16:06 +01:00
query := ` INSERT INTO session ( sessionid , name , activescreenid , sessionidx , notifynum , archived , archivedts , sharemode )
VALUES ( ? , ? , ' ' , ? , ? , 0 , 0 , ' local ' ) `
2023-02-15 01:17:54 +01:00
tx . Exec ( query , newSessionId , sessionName , maxSessionIdx + 1 , 0 )
2023-03-13 20:23:36 +01:00
screenUpdate , 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
}
2023-03-13 20:23:36 +01:00
newScreen = screenUpdate . Screens [ 0 ]
2022-08-27 01:21:19 +02:00
if activate {
query = ` UPDATE client SET activesessionid = ? `
2023-02-15 01:17:54 +01:00
tx . Exec ( query , newSessionId )
2022-08-27 01:21:19 +02:00
}
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 } ,
2023-03-13 20:23:36 +01:00
Screens : [ ] * ScreenType { newScreen } ,
2022-08-09 01:21:46 +02:00
}
if activate {
update . ActiveSessionId = newSessionId
2022-07-15 03:39:40 +02:00
}
2023-03-10 03:44:01 +01: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 {
2022-12-27 01:09:21 +01:00
query := ` SELECT sessionid FROM session WHERE sessionid = ? `
2022-08-30 01:31:06 +02:00
if ! tx . Exists ( query , sessionId ) {
return fmt . Errorf ( "cannot switch to session, not found" )
}
query = ` UPDATE client SET activesessionid = ? `
2023-02-15 01:17:54 +01:00
tx . Exec ( query , sessionId )
2022-08-30 01:31:06 +02:00
return nil
} )
return txErr
}
2022-12-24 00:56:29 +01: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 09:26:33 +02:00
func SetWinSize ( ctx context . Context , winSize ClientWinSizeType ) error {
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` UPDATE client SET winsize = ? `
2023-02-15 01:17:54 +01:00
tx . Exec ( query , quickJson ( winSize ) )
2022-09-25 09:26:33 +02:00
return nil
} )
return txErr
}
2023-02-26 23:33:01 +01:00
func UpdateClientFeOpts ( ctx context . Context , feOpts FeOptsType ) error {
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` UPDATE client SET feopts = ? `
tx . Exec ( query , quickJson ( feOpts ) )
return nil
} )
return txErr
}
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
}
2023-03-13 20:23:36 +01:00
func InsertScreen ( ctx context . Context , sessionId string , origScreenName string , activate bool ) ( * ModelUpdate , 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-12-25 22:03:11 +01:00
query := ` SELECT sessionid FROM session WHERE sessionid = ? AND NOT archived `
2022-07-13 06:51:17 +02:00
if ! tx . Exists ( query , sessionId ) {
2022-12-27 01:09:21 +01:00
return fmt . Errorf ( "cannot create screen, no session found (or session archived)" )
2022-07-08 22:23:45 +02:00
}
2023-03-13 09:52:30 +01:00
localRemoteId := tx . GetString ( ` SELECT remoteid FROM remote WHERE remotealias = ? ` , LocalRemoteAlias )
if localRemoteId == "" {
2022-08-24 11:14:16 +02:00
return fmt . Errorf ( "cannot create screen, no local remote found" )
}
2022-12-25 22:03:11 +01:00
maxScreenIdx := tx . GetInt ( ` SELECT COALESCE(max(screenidx), 0) FROM screen WHERE sessionid = ? AND NOT archived ` , sessionId )
2022-12-26 21:18:13 +01: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-20 03:52:08 +01:00
newScreenId = scbase . GenPromptUUID ( )
2023-03-13 09:52:30 +01:00
screen := & ScreenType {
SessionId : sessionId ,
ScreenId : newScreenId ,
Name : screenName ,
ScreenIdx : int64 ( maxScreenIdx ) + 1 ,
ScreenOpts : ScreenOptsType { } ,
OwnerId : "" ,
ShareMode : ShareModeLocal ,
CurRemote : RemotePtrType { RemoteId : localRemoteId } ,
NextLineNum : 1 ,
SelectedLine : 0 ,
Anchor : ScreenAnchorType { } ,
FocusType : ScreenFocusInput ,
Archived : false ,
ArchivedTs : 0 ,
}
2023-03-24 18:34:07 +01:00
query = ` INSERT INTO screen ( sessionid , screenid , name , screenidx , screenopts , ownerid , sharemode , webshareopts , curremoteownerid , curremoteid , curremotename , nextlinenum , selectedline , anchor , focustype , archived , archivedts )
VALUES ( : sessionid , : screenid , : name , : screenidx , : screenopts , : ownerid , : sharemode , : webshareopts , : curremoteownerid , : curremoteid , : curremotename , : nextlinenum , : selectedline , : anchor , : focustype , : archived , : archivedts ) `
2023-03-13 09:52:30 +01:00
tx . NamedExec ( query , screen . ToMap ( ) )
2022-07-15 03:39:40 +02:00
if activate {
query = ` UPDATE session SET activescreenid = ? WHERE sessionid = ? `
2023-02-15 01:17:54 +01:00
tx . Exec ( query , newScreenId , sessionId )
2022-07-15 03:39:40 +02:00
}
2022-07-08 22:23:45 +02:00
return nil
} )
2022-12-27 01:09:21 +01:00
if txErr != nil {
return nil , txErr
}
2023-03-13 09:52:30 +01:00
newScreen , err := GetScreenById ( ctx , newScreenId )
2022-07-15 10:57:45 +02:00
if err != nil {
2022-07-16 02:53:23 +02:00
return nil , err
2022-07-15 10:57:45 +02:00
}
2023-03-13 20:23:36 +01:00
update := & ModelUpdate { Screens : [ ] * ScreenType { newScreen } }
2023-03-13 18:50:29 +01:00
if activate {
bareSession , err := GetBareSessionById ( ctx , sessionId )
if err != nil {
return nil , txErr
}
update . Sessions = [ ] * SessionType { bareSession }
2022-07-15 10:57:45 +02:00
}
2023-03-13 18:50:29 +01:00
return update , nil
2022-07-01 23:07:13 +02:00
}
2022-07-02 22:31:56 +02:00
2023-03-13 09:52:30 +01:00
func GetScreenById ( ctx context . Context , screenId string ) ( * ScreenType , error ) {
return WithTxRtn ( ctx , func ( tx * TxWrap ) ( * ScreenType , error ) {
query := ` SELECT * FROM screen WHERE screenid = ? `
2023-03-27 23:11:02 +02:00
screen := dbutil . GetMapGen [ * ScreenType ] ( tx , query , screenId )
2023-03-13 20:10:23 +01:00
screen . Full = true
2023-03-13 09:52:30 +01:00
return screen , nil
2022-07-15 03:39:40 +02:00
} )
2022-07-12 22:50:44 +02:00
}
2023-03-21 03:20:57 +01:00
func FindLineIdByArg ( ctx context . Context , screenId string , lineArg string ) ( string , error ) {
2022-09-21 02:37:49 +02:00
var lineId string
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
lineNum , err := strconv . Atoi ( lineArg )
if err == nil {
// valid linenum
2023-03-21 03:20:57 +01:00
query := ` SELECT lineid FROM line WHERE screenid = ? AND linenum = ? `
lineId = tx . GetString ( query , screenId , lineNum )
2022-09-21 02:37:49 +02:00
} else if len ( lineArg ) == 8 {
// prefix id string match
2023-03-21 03:20:57 +01:00
query := ` SELECT lineid FROM line WHERE screenid = ? AND substr(lineid, 1, 8) = ? `
lineId = tx . GetString ( query , screenId , lineArg )
2022-09-21 02:37:49 +02:00
} else {
// id match
2023-03-21 03:20:57 +01:00
query := ` SELECT lineid FROM line WHERE screenid = ? AND lineid = ? `
lineId = tx . GetString ( query , screenId , lineArg )
2022-09-21 02:37:49 +02:00
}
return nil
} )
if txErr != nil {
return "" , txErr
}
return lineId , nil
}
2023-03-26 22:21:58 +02:00
func GetCmdIdFromLineId ( ctx context . Context , screenId string , lineId string ) ( string , error ) {
return WithTxRtn ( ctx , func ( tx * TxWrap ) ( string , error ) {
query := ` SELECT cmdid FROM line WHERE screenid = ? AND lineid = ? `
cmdId := tx . GetString ( query , screenId , lineId )
return cmdId , nil
} )
}
2023-03-21 03:20:57 +01:00
func GetLineCmdByLineId ( ctx context . Context , screenId string , lineId string ) ( * LineType , * CmdType , error ) {
2023-03-13 10:09:29 +01:00
return WithTxRtn3 ( ctx , func ( tx * TxWrap ) ( * LineType , * CmdType , error ) {
2022-09-21 02:37:49 +02:00
var lineVal LineType
2023-03-21 03:20:57 +01:00
query := ` SELECT * FROM line WHERE screenid = ? AND lineid = ? `
found := tx . Get ( & lineVal , query , screenId , lineId )
2022-09-21 02:37:49 +02:00
if ! found {
2023-03-13 10:09:29 +01:00
return nil , nil , nil
2022-09-21 02:37:49 +02:00
}
2023-03-13 10:09:29 +01:00
var cmdRtn * CmdType
2022-09-21 02:37:49 +02:00
if lineVal . CmdId != "" {
2023-03-21 03:20:57 +01:00
query = ` SELECT * FROM cmd WHERE screenid = ? AND cmdid = ? `
2023-03-27 23:11:02 +02:00
cmdRtn = dbutil . GetMapGen [ * CmdType ] ( tx , query , screenId , lineVal . CmdId )
2022-09-21 02:37:49 +02:00
}
2023-03-13 10:09:29 +01:00
return & lineVal , cmdRtn , nil
2022-09-21 02:37:49 +02:00
} )
}
2023-03-21 03:20:57 +01:00
func GetLineCmdByCmdId ( ctx context . Context , screenId string , cmdId string ) ( * LineType , * CmdType , error ) {
2023-03-13 10:09:29 +01:00
return WithTxRtn3 ( ctx , func ( tx * TxWrap ) ( * LineType , * CmdType , error ) {
2022-10-28 07:00:10 +02:00
var lineVal LineType
2023-03-21 03:20:57 +01:00
query := ` SELECT * FROM line WHERE screenid = ? AND cmdid = ? `
found := tx . Get ( & lineVal , query , screenId , cmdId )
2022-10-28 07:00:10 +02:00
if ! found {
2023-03-13 10:09:29 +01:00
return nil , nil , nil
2022-10-28 07:00:10 +02:00
}
2023-03-21 03:20:57 +01:00
query = ` SELECT * FROM cmd WHERE screenid = ? AND cmdid = ? `
2023-03-27 23:11:02 +02:00
cmdRtn := dbutil . GetMapGen [ * CmdType ] ( tx , query , screenId , cmdId )
2023-03-13 10:09:29 +01:00
return & lineVal , cmdRtn , nil
2022-10-28 07:00:10 +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" )
}
2023-03-16 02:12:55 +01:00
if cmd . ScreenId == "" {
return fmt . Errorf ( "cmd should have screenid set" )
}
2022-07-02 22:31:56 +02:00
return WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-21 03:20:57 +01:00
query := ` SELECT screenid FROM screen WHERE screenid = ? `
if ! tx . Exists ( query , line . ScreenId ) {
return fmt . Errorf ( "screen not found, cannot insert line[%s]" , line . ScreenId )
2022-07-02 22:31:56 +02:00
}
2023-03-21 03:20:57 +01:00
query = ` SELECT nextlinenum FROM screen WHERE screenid = ? `
nextLineNum := tx . GetInt ( query , line . ScreenId )
2022-09-21 02:01:25 +02:00
line . LineNum = int64 ( nextLineNum )
2023-03-24 18:34:07 +01:00
query = ` INSERT INTO line ( screenid , userid , lineid , ts , linenum , linenumtemp , linelocal , linetype , text , cmdid , renderer , ephemeral , contentheight , star , archived )
VALUES ( : screenid , : userid , : lineid , : ts , : linenum , : linenumtemp , : linelocal , : linetype , : text , : cmdid , : renderer , : ephemeral , : contentheight , : star , : archived ) `
2023-02-15 01:17:54 +01:00
tx . NamedExec ( query , line )
2023-03-21 03:20:57 +01:00
query = ` UPDATE screen SET nextlinenum = ? WHERE screenid = ? `
tx . Exec ( query , nextLineNum + 1 , line . ScreenId )
2022-07-08 06:39:25 +02:00
if cmd != nil {
2022-10-07 03:33:54 +02:00
cmd . OrigTermOpts = cmd . TermOpts
2022-07-08 06:39:25 +02:00
cmdMap := cmd . ToMap ( )
query = `
2023-03-21 03:20:57 +01:00
INSERT INTO cmd ( screenid , cmdid , remoteownerid , remoteid , remotename , cmdstr , rawcmdstr , festate , statebasehash , statediffhasharr , termopts , origtermopts , status , startpk , doneinfo , rtnstate , runout , rtnbasehash , rtndiffhasharr )
VALUES ( : screenid , : cmdid , : remoteownerid , : remoteid , : remotename , : cmdstr , : rawcmdstr , : festate , : statebasehash , : statediffhasharr , : termopts , : origtermopts , : status , : startpk , : doneinfo , : rtnstate , : runout , : rtnbasehash , : rtndiffhasharr )
2022-07-07 09:10:37 +02:00
`
2023-02-15 01:17:54 +01:00
tx . NamedExec ( query , cmdMap )
2022-07-08 06:39:25 +02:00
}
2023-03-25 20:54:56 +01:00
if isWebShare ( tx , line . ScreenId ) {
2023-03-26 22:21:58 +02:00
insertScreenLineUpdate ( tx , line . ScreenId , line . LineId , UpdateType_LineNew )
2023-03-25 20:54:56 +01:00
}
2022-07-07 09:10:37 +02:00
return nil
} )
}
2023-03-21 03:20:57 +01:00
func GetCmdByScreenId ( ctx context . Context , screenId string , cmdId string ) ( * CmdType , error ) {
2022-07-07 22:26:46 +02:00
var cmd * CmdType
err := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-21 03:20:57 +01:00
query := ` SELECT * FROM cmd WHERE screenid = ? AND cmdid = ? `
2023-03-27 23:11:02 +02:00
cmd = dbutil . GetMapGen [ * CmdType ] ( tx , query , screenId , cmdId )
2022-07-07 22:26:46 +02:00
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-11-29 03:03:02 +01: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-08 01:29:14 +02:00
}
2023-03-25 20:54:56 +01:00
screenId := ck . GetGroupId ( )
2022-08-20 02:14:53 +02:00
var rtnCmd * CmdType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-21 03:20:57 +01:00
query := ` UPDATE cmd SET status = ?, doneinfo = ? WHERE screenid = ? AND cmdid = ? `
2023-03-25 20:54:56 +01:00
tx . Exec ( query , CmdStatusDone , quickJson ( doneInfo ) , screenId , ck . GetCmdId ( ) )
2022-08-20 02:14:53 +02:00
var err error
2023-03-25 20:54:56 +01:00
rtnCmd , err = GetCmdByScreenId ( tx . Context ( ) , screenId , ck . GetCmdId ( ) )
2022-08-20 02:14:53 +02:00
if err != nil {
return err
}
2023-03-25 20:54:56 +01:00
if isWebShare ( tx , screenId ) {
2023-03-26 22:21:58 +02:00
insertScreenCmdUpdate ( tx , screenId , ck . GetCmdId ( ) , UpdateType_CmdDoneInfo )
insertScreenCmdUpdate ( tx , screenId , ck . GetCmdId ( ) , UpdateType_CmdStatus )
2023-03-25 20:54:56 +01:00
}
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 {
2022-11-29 03:03:02 +01:00
return nil , fmt . Errorf ( "cmd data not found for ck[%s]" , ck )
2022-08-20 02:14:53 +02:00
}
2022-10-12 08:11:43 +02:00
return & ModelUpdate { Cmd : rtnCmd } , nil
2022-07-08 01:29:14 +02:00
}
2022-11-29 03:03:02 +01:00
func UpdateCmdRtnState ( ctx context . Context , ck base . CommandKey , statePtr ShellStatePtr ) error {
if ck . IsEmpty ( ) {
return fmt . Errorf ( "cannot update cmdrtnstate, empty ck" )
}
2023-03-25 20:54:56 +01:00
screenId := ck . GetGroupId ( )
2022-11-29 03:03:02 +01:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-21 03:20:57 +01:00
query := ` UPDATE cmd SET rtnbasehash = ?, rtndiffhasharr = ? WHERE screenid = ? AND cmdid = ? `
2023-03-25 20:54:56 +01:00
tx . Exec ( query , statePtr . BaseHash , quickJsonArr ( statePtr . DiffHashArr ) , screenId , ck . GetCmdId ( ) )
if isWebShare ( tx , screenId ) {
2023-03-26 22:21:58 +02:00
insertScreenCmdUpdate ( tx , screenId , ck . GetCmdId ( ) , UpdateType_CmdRtnState )
2023-03-25 20:54:56 +01:00
}
2022-11-29 03:03:02 +01:00
return nil
} )
if txErr != nil {
return txErr
}
return 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)" )
}
2023-03-25 20:54:56 +01:00
screenId := errPk . CK . GetGroupId ( )
2022-07-08 01:29:14 +02:00
return WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-21 03:20:57 +01:00
query := ` UPDATE cmd SET runout = json_insert(runout, '$[#]', ?) WHERE screenid = ? AND cmdid = ? `
2023-03-25 20:54:56 +01:00
tx . Exec ( query , quickJson ( errPk ) , screenId , errPk . CK . GetCmdId ( ) )
2022-07-08 01:29:14 +02:00
return nil
} )
}
2023-01-12 05:53:46 +01:00
func ReInitFocus ( ctx context . Context ) error {
return WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-13 10:09:29 +01:00
query := ` UPDATE screen SET focustype = 'input' `
2023-02-15 01:17:54 +01:00
tx . Exec ( query )
2023-01-12 05:53:46 +01:00
return nil
} )
}
2022-07-08 01:29:14 +02:00
func HangupAllRunningCmds ( ctx context . Context ) error {
return WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-26 22:21:58 +02:00
var cmdPtrs [ ] CmdPtr
query := ` SELECT screenid, cmdid FROM cmd WHERE status = ? `
tx . Select ( & cmdPtrs , query , CmdStatusRunning )
query = ` UPDATE cmd SET status = ? WHERE status = ? `
2023-02-15 01:17:54 +01:00
tx . Exec ( query , CmdStatusHangup , CmdStatusRunning )
2023-03-26 22:21:58 +02:00
for _ , cmdPtr := range cmdPtrs {
insertScreenCmdUpdate ( tx , cmdPtr . ScreenId , cmdPtr . CmdId , UpdateType_CmdStatus )
}
2022-07-08 01:29:14 +02:00
return nil
} )
}
func HangupRunningCmdsByRemoteId ( ctx context . Context , remoteId string ) error {
return WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-26 22:21:58 +02:00
var cmdPtrs [ ] CmdPtr
query := ` SELECT screenid, cmdid FROM cmd WHERE status = ? AND remoteid = ? `
tx . Select ( & cmdPtrs , query , CmdStatusRunning , remoteId )
query = ` UPDATE cmd SET status = ? WHERE status = ? AND remoteid = ? `
2023-02-15 01:17:54 +01:00
tx . Exec ( query , CmdStatusHangup , CmdStatusRunning , remoteId )
2023-03-26 22:21:58 +02:00
for _ , cmdPtr := range cmdPtrs {
insertScreenCmdUpdate ( tx , cmdPtr . ScreenId , cmdPtr . CmdId , UpdateType_CmdStatus )
}
2022-07-08 01:29:14 +02:00
return nil
} )
}
2022-07-15 03:39:40 +02:00
2022-11-27 23:12:15 +01:00
func HangupCmd ( ctx context . Context , ck base . CommandKey ) error {
return WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-21 03:20:57 +01:00
query := ` UPDATE cmd SET status = ? WHERE screenid = ? AND cmdid = ? `
tx . Exec ( query , CmdStatusHangup , ck . GetGroupId ( ) , ck . GetCmdId ( ) )
2023-03-26 22:21:58 +02:00
insertScreenCmdUpdate ( tx , ck . GetGroupId ( ) , ck . GetCmdId ( ) , UpdateType_CmdStatus )
2022-11-27 23:12:15 +01:00
return nil
} )
}
2022-07-15 10:57:45 +02: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 ]
}
2023-03-03 19:15:57 +01:00
func SwitchScreenById ( ctx context . Context , sessionId string , screenId string ) ( * ModelUpdate , error ) {
2022-07-15 03:39:40 +02:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-12-26 21:18:13 +01:00
query := ` SELECT screenid FROM screen WHERE sessionid = ? AND screenid = ? `
2022-07-15 03:39:40 +02: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-15 01:17:54 +01:00
tx . Exec ( query , screenId , sessionId )
2022-07-15 03:39:40 +02:00
return nil
} )
2022-12-27 01:09:21 +01:00
if txErr != nil {
return nil , txErr
}
bareSession , err := GetBareSessionById ( ctx , sessionId )
if err != nil {
return nil , err
}
2023-03-03 19:15:57 +01:00
return & ModelUpdate { ActiveSessionId : sessionId , Sessions : [ ] * SessionType { bareSession } } , nil
2022-07-15 03:39:40 +02:00
}
2022-07-15 10:57:45 +02:00
2023-03-21 03:20:57 +01:00
// screen may not exist at this point (so don't query screen table)
func cleanScreenCmds ( ctx context . Context , screenId string ) error {
2023-01-25 23:29:12 +01:00
var removedCmds [ ] string
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-21 03:20:57 +01:00
query := ` SELECT cmdid FROM cmd WHERE screenid = ? AND cmdid NOT IN (SELECT cmdid FROM line WHERE screenid = ?) `
removedCmds = tx . SelectStrings ( query , screenId , screenId )
query = ` DELETE FROM cmd WHERE screenid = ? AND cmdid NOT IN (SELECT cmdid FROM line WHERE screenid = ?) `
tx . Exec ( query , screenId , screenId )
2023-01-02 21:09:01 +01:00
return nil
} )
if txErr != nil {
return txErr
}
2023-01-25 23:29:12 +01:00
for _ , cmdId := range removedCmds {
2023-03-21 03:20:57 +01:00
DeletePtyOutFile ( ctx , screenId , cmdId )
2023-01-25 23:29:12 +01:00
}
2023-01-02 21:09:01 +01:00
return nil
}
2022-12-25 22:21:48 +01:00
func ArchiveScreen ( ctx context . Context , sessionId string , screenId string ) ( UpdatePacket , error ) {
2023-03-13 18:50:29 +01:00
var isActive bool
2022-12-24 00:56:29 +01: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 22:03:11 +01:00
query = ` SELECT archived FROM screen WHERE sessionid = ? AND screenid = ? `
2022-12-24 00:56:29 +01:00
closeVal := tx . GetBool ( query , sessionId , screenId )
if closeVal {
return nil
}
2022-12-25 22:03:11 +01:00
query = ` SELECT count(*) FROM screen WHERE sessionid = ? AND NOT archived `
2022-12-24 00:56:29 +01:00
numScreens := tx . GetInt ( query , sessionId )
if numScreens <= 1 {
2022-12-26 21:18:13 +01:00
return fmt . Errorf ( "cannot archive the last screen in a session" )
2022-12-24 00:56:29 +01:00
}
2022-12-25 22:21:48 +01:00
query = ` UPDATE screen SET archived = 1, archivedts = ?, screenidx = 0 WHERE sessionid = ? AND screenid = ? `
2023-02-15 01:17:54 +01:00
tx . Exec ( query , time . Now ( ) . UnixMilli ( ) , sessionId , screenId )
2023-03-13 18:50:29 +01:00
isActive = tx . Exists ( ` SELECT sessionid FROM session WHERE sessionid = ? AND activescreenid = ? ` , sessionId , screenId )
2022-12-24 00:56:29 +01:00
if isActive {
2022-12-25 22:03:11 +01:00
screenIds := tx . SelectStrings ( ` SELECT screenid FROM screen WHERE sessionid = ? AND NOT archived ORDER BY screenidx ` , sessionId )
2022-12-24 00:56:29 +01:00
nextId := getNextId ( screenIds , screenId )
2023-02-15 01:17:54 +01:00
tx . Exec ( ` UPDATE session SET activescreenid = ? WHERE sessionid = ? ` , nextId , sessionId )
2022-12-24 00:56:29 +01:00
}
return nil
} )
if txErr != nil {
return nil , txErr
}
2023-03-13 18:50:29 +01:00
newScreen , err := GetScreenById ( ctx , screenId )
2022-12-27 01:09:21 +01:00
if err != nil {
2023-03-13 18:50:29 +01:00
return nil , fmt . Errorf ( "cannot retrive archived screen: %w" , err )
2022-12-27 01:09:21 +01:00
}
2023-03-13 18:50:29 +01:00
update := ModelUpdate { Screens : [ ] * ScreenType { newScreen } }
if isActive {
bareSession , err := GetBareSessionById ( ctx , sessionId )
if err != nil {
return nil , err
}
update . Sessions = [ ] * SessionType { bareSession }
2022-12-26 21:38:47 +01:00
}
2022-12-24 00:56:29 +01:00
return update , nil
}
2022-12-25 22:21:48 +01:00
func UnArchiveScreen ( ctx context . Context , sessionId string , screenId string ) error {
2022-12-24 00:56:29 +01:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-12-25 22:03:11 +01:00
query := ` SELECT screenid FROM screen WHERE sessionid = ? AND screenid = ? AND archived `
2022-12-24 00:56:29 +01:00
if ! tx . Exists ( query , sessionId , screenId ) {
2022-12-25 22:03:11 +01:00
return fmt . Errorf ( "cannot re-open screen (not found or not archived)" )
2022-12-24 00:56:29 +01:00
}
2022-12-25 22:03:11 +01:00
maxScreenIdx := tx . GetInt ( ` SELECT COALESCE(max(screenidx), 0) FROM screen WHERE sessionid = ? AND NOT archived ` , sessionId )
2022-12-26 21:18:13 +01:00
query = ` UPDATE screen SET archived = 0, screenidx = ? WHERE sessionid = ? AND screenid = ? `
2023-02-15 01:17:54 +01:00
tx . Exec ( query , maxScreenIdx + 1 , sessionId , screenId )
2022-12-24 00:56:29 +01:00
return nil
} )
return txErr
}
2023-03-21 03:20:57 +01:00
func PurgeScreen ( ctx context . Context , screenId string , sessionDel bool ) ( UpdatePacket , error ) {
2023-03-15 00:37:22 +01:00
var sessionId string
2023-03-13 18:50:29 +01:00
var isActive bool
2022-07-15 10:57:45 +02:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-13 10:09:29 +01:00
query := ` SELECT screenid FROM screen WHERE screenid = ? `
if ! tx . Exists ( query , screenId ) {
2022-12-25 22:03:11 +01:00
return fmt . Errorf ( "cannot purge screen (not found)" )
}
2023-03-21 03:20:57 +01:00
if ! sessionDel {
query = ` SELECT sessionid FROM screen WHERE screenid = ? `
sessionId = tx . GetString ( query , screenId )
if sessionId == "" {
return fmt . Errorf ( "cannot purge screen (no sessionid)" )
}
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" )
}
isActive = tx . Exists ( ` SELECT sessionid FROM session WHERE sessionid = ? AND activescreenid = ? ` , sessionId , screenId )
if isActive {
screenIds := tx . SelectStrings ( ` SELECT screenid FROM screen WHERE sessionid = ? AND NOT archived ORDER BY screenidx ` , sessionId )
nextId := getNextId ( screenIds , screenId )
tx . Exec ( ` UPDATE session SET activescreenid = ? WHERE sessionid = ? ` , nextId , sessionId )
}
2022-07-15 10:57:45 +02:00
}
2023-03-13 10:09:29 +01:00
query = ` DELETE FROM screen WHERE screenid = ? `
tx . Exec ( query , screenId )
2023-03-21 03:20:57 +01:00
query = ` DELETE FROM history WHERE screenid = ? `
tx . Exec ( query , screenId )
query = ` DELETE FROM line WHERE screenid = ? `
tx . Exec ( query , screenId )
2022-07-15 10:57:45 +02:00
return nil
} )
if txErr != nil {
2022-07-16 02:53:23 +02:00
return nil , txErr
2022-07-15 10:57:45 +02:00
}
2023-03-21 03:20:57 +01:00
go cleanScreenCmds ( context . Background ( ) , screenId )
if sessionDel {
return nil , nil
}
2023-03-13 18:50:29 +01:00
update := ModelUpdate { }
2023-03-15 00:37:22 +01:00
update . Screens = [ ] * ScreenType { & ScreenType { SessionId : sessionId , ScreenId : screenId , Remove : true } }
2023-03-13 18:50:29 +01:00
if isActive {
2023-03-15 00:37:22 +01:00
bareSession , err := GetBareSessionById ( ctx , sessionId )
2023-03-13 18:50:29 +01:00
if err != nil {
return nil , err
}
update . Sessions = [ ] * SessionType { bareSession }
2022-12-27 01:09:21 +01:00
}
2022-12-28 22:56:19 +01:00
return update , nil
2022-07-15 10:57:45 +02:00
}
2022-07-16 02:37:32 +02:00
2023-03-15 00:37:22 +01:00
func GetRemoteState ( ctx context . Context , sessionId string , screenId string , remotePtr RemotePtrType ) ( * packet . ShellState , * ShellStatePtr , error ) {
ssptr , err := GetRemoteStatePtr ( ctx , sessionId , screenId , remotePtr )
2022-11-29 03:03:02 +01:00
if err != nil {
return nil , nil , err
}
if ssptr == nil {
return nil , nil , nil
}
state , err := GetFullState ( ctx , * ssptr )
if err != nil {
return nil , nil , err
}
return state , ssptr , err
}
2023-03-15 00:37:22 +01:00
func GetRemoteStatePtr ( ctx context . Context , sessionId string , screenId string , remotePtr RemotePtrType ) ( * ShellStatePtr , error ) {
2022-11-29 03:03:02 +01:00
var ssptr * ShellStatePtr
2022-07-16 02:37:32 +02:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-15 00:37:22 +01:00
ri , err := GetRemoteInstance ( tx . Context ( ) , sessionId , screenId , remotePtr )
2022-11-28 09:13:00 +01:00
if err != nil {
return err
}
if ri == nil {
2022-07-16 02:37:32 +02:00
return nil
}
2022-11-29 03:03:02 +01:00
ssptr = & ShellStatePtr { ri . StateBaseHash , ri . StateDiffHashArr }
2022-07-16 02:37:32 +02:00
return nil
} )
2022-11-28 09:13:00 +01:00
if txErr != nil {
return nil , txErr
}
2022-11-29 03:03:02 +01:00
return ssptr , nil
2022-07-16 02:37:32 +02:00
}
2022-08-09 23:24:57 +02:00
2023-03-15 00:37:22 +01:00
func validateSessionScreen ( tx * TxWrap , sessionId string , screenId string ) error {
if screenId == "" {
2022-08-24 11:14:16 +02:00
query := ` SELECT sessionid FROM session WHERE sessionid = ? `
if ! tx . Exists ( query , sessionId ) {
return fmt . Errorf ( "no session found" )
}
return nil
} else {
2023-03-15 00:37:22 +01:00
query := ` SELECT screenid FROM screen WHERE sessionid = ? AND screenid = ? `
if ! tx . Exists ( query , sessionId , screenId ) {
2023-03-13 10:09:29 +01:00
return fmt . Errorf ( "no screen found" )
2022-08-24 11:14:16 +02:00
}
return nil
}
}
2023-03-15 00:37:22 +01:00
func GetRemoteInstance ( ctx context . Context , sessionId string , screenId string , remotePtr RemotePtrType ) ( * RemoteInstance , error ) {
2022-11-28 09:13:00 +01:00
if remotePtr . IsSessionScope ( ) {
2023-03-15 00:37:22 +01:00
screenId = ""
2022-11-28 09:13:00 +01:00
}
var ri * RemoteInstance
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-15 00:37:22 +01:00
query := ` SELECT * FROM remote_instance WHERE sessionid = ? AND screenid = ? AND remoteownerid = ? AND remoteid = ? AND name = ? `
2023-03-27 23:11:02 +02:00
ri = dbutil . GetMapGen [ * RemoteInstance ] ( tx , query , sessionId , screenId , remotePtr . OwnerId , remotePtr . RemoteId , remotePtr . Name )
2022-11-28 09:13:00 +01:00
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-29 03:03:02 +01:00
ri . StateDiffHashArr = nil
2022-11-28 09:13:00 +01: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
}
2023-03-15 00:37:22 +01:00
func UpdateRemoteState ( ctx context . Context , sessionId string , screenId string , remotePtr RemotePtrType , feState FeStateType , stateBase * packet . ShellState , stateDiff * packet . ShellStateDiff ) ( * RemoteInstance , error ) {
2022-11-28 09:13:00 +01:00
if stateBase == nil && stateDiff == nil {
return nil , fmt . Errorf ( "UpdateRemoteState, must set state or diff" )
}
if stateBase != nil && stateDiff != nil {
return nil , fmt . Errorf ( "UpdateRemoteState, cannot set state and diff" )
}
2022-08-24 11:14:16 +02:00
if remotePtr . IsSessionScope ( ) {
2023-03-15 00:37:22 +01:00
screenId = ""
2022-08-24 11:14:16 +02:00
}
2022-10-17 08:51:04 +02:00
var ri * RemoteInstance
2022-08-24 11:14:16 +02:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-15 00:37:22 +01:00
err := validateSessionScreen ( tx , sessionId , screenId )
2022-08-24 11:14:16 +02:00
if err != nil {
2022-11-28 09:13:00 +01:00
return fmt . Errorf ( "cannot update remote instance state: %w" , err )
2022-08-09 23:24:57 +02:00
}
2023-03-15 00:37:22 +01:00
query := ` SELECT * FROM remote_instance WHERE sessionid = ? AND screenid = ? AND remoteownerid = ? AND remoteid = ? AND name = ? `
2023-03-27 23:11:02 +02:00
ri = dbutil . GetMapGen [ * RemoteInstance ] ( tx , query , sessionId , screenId , remotePtr . OwnerId , remotePtr . RemoteId , remotePtr . Name )
2022-10-17 08:51:04 +02:00
if ri == nil {
ri = & RemoteInstance {
2022-12-20 03:52:08 +01:00
RIId : scbase . GenPromptUUID ( ) ,
2022-08-24 22:21:54 +02:00
Name : remotePtr . Name ,
SessionId : sessionId ,
2023-03-15 00:37:22 +01:00
ScreenId : screenId ,
2022-08-24 22:21:54 +02:00
RemoteOwnerId : remotePtr . OwnerId ,
RemoteId : remotePtr . RemoteId ,
2022-11-28 09:13:00 +01:00
FeState : feState ,
}
err = updateRIWithState ( tx . Context ( ) , ri , stateBase , stateDiff )
if err != nil {
return err
2022-08-09 23:24:57 +02:00
}
2023-03-15 00:37:22 +01:00
query = ` INSERT INTO remote_instance ( riid , name , sessionid , screenid , remoteownerid , remoteid , festate , statebasehash , statediffhasharr )
VALUES ( : riid , : name , : sessionid , : screenid , : remoteownerid , : remoteid , : festate , : statebasehash , : statediffhasharr ) `
2023-02-15 01:17:54 +01:00
tx . NamedExec ( query , ri . ToMap ( ) )
2022-08-09 23:24:57 +02:00
return nil
2022-11-28 09:13:00 +01:00
} else {
2022-11-29 03:03:02 +01:00
query = ` UPDATE remote_instance SET festate = ?, statebasehash = ?, statediffhasharr = ? WHERE riid = ? `
2022-11-28 09:13:00 +01:00
ri . FeState = feState
err = updateRIWithState ( tx . Context ( ) , ri , stateBase , stateDiff )
if err != nil {
return err
}
2023-02-15 01:17:54 +01:00
tx . Exec ( query , quickJson ( ri . FeState ) , ri . StateBaseHash , quickJsonArr ( ri . StateDiffHashArr ) , ri . RIId )
2022-11-28 09:13:00 +01:00
return nil
2022-08-09 23:24:57 +02:00
}
} )
2022-10-17 08:51:04 +02:00
return ri , txErr
2022-08-09 23:24:57 +02:00
}
2022-08-17 21:24:09 +02:00
2023-03-13 09:52:30 +01:00
func UpdateCurRemote ( ctx context . Context , screenId string , remotePtr RemotePtrType ) error {
return WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT screenid FROM screen WHERE screenid = ? `
if ! tx . Exists ( query , screenId ) {
return fmt . Errorf ( "cannot update curremote: no screen found" )
2022-08-17 21:24:09 +02:00
}
2023-03-13 09:52:30 +01:00
query = ` UPDATE screen SET curremoteownerid = ?, curremoteid = ?, curremotename = ? WHERE screenid = ? `
tx . Exec ( query , remotePtr . OwnerId , remotePtr . RemoteId , remotePtr . Name , screenId )
2022-08-17 21:24:09 +02:00
return nil
} )
}
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-12-27 01:09:21 +01:00
query := ` SELECT sessionid FROM session WHERE NOT archived 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 {
2023-02-15 01:17:54 +01:00
tx . Exec ( query , id , idx + 1 )
2022-08-26 22:12:17 +02:00
}
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-12-27 01:09:21 +01: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-27 02:17:33 +02:00
}
2022-08-27 01:21:19 +02:00
query = ` UPDATE session SET name = ? WHERE sessionid = ? `
2023-02-15 01:17:54 +01:00
tx . Exec ( query , name , sessionId )
2022-08-26 22:12:17 +02:00
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 = ` UPDATE screen SET name = ? WHERE sessionid = ? AND screenid = ? `
2023-02-15 01:17:54 +01:00
tx . Exec ( query , name , sessionId , screenId )
2022-08-27 02:51:28 +02:00
return nil
} )
return txErr
}
2022-08-27 06:44:18 +02:00
2023-03-13 09:52:30 +01:00
func ArchiveScreenLines ( ctx context . Context , screenId string ) ( * ModelUpdate , error ) {
2022-08-27 06:44:18 +02:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-21 03:20:57 +01:00
query := ` SELECT screenid FROM screen WHERE screenid = ? `
if ! tx . Exists ( query , screenId ) {
return fmt . Errorf ( "screen does not exist" )
2023-01-02 21:09:01 +01:00
}
2023-03-21 03:20:57 +01:00
query = ` UPDATE line SET archived = 1 WHERE screenid = ? `
tx . Exec ( query , screenId )
2023-01-02 21:09:01 +01:00
return nil
} )
if txErr != nil {
return nil , txErr
}
2023-03-13 18:50:29 +01:00
screenLines , err := GetScreenLinesById ( ctx , screenId )
2023-01-02 21:09:01 +01:00
if err != nil {
return nil , err
}
2023-03-13 18:50:29 +01:00
return & ModelUpdate { ScreenLines : screenLines } , nil
2023-01-02 21:09:01 +01:00
}
2023-03-13 09:52:30 +01:00
func PurgeScreenLines ( ctx context . Context , screenId string ) ( * ModelUpdate , error ) {
2022-08-27 07:01:29 +02:00
var lineIds [ ] string
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-21 03:20:57 +01:00
query := ` SELECT lineid FROM line WHERE screenid = ? `
lineIds = tx . SelectStrings ( query , screenId )
query = ` DELETE FROM line WHERE screenid = ? `
tx . Exec ( query , screenId )
query = ` DELETE FROM history WHERE screenid = ? `
tx . Exec ( query , screenId )
2023-03-13 09:52:30 +01:00
query = ` UPDATE screen SET nextlinenum = 1 WHERE screenid = ? `
tx . Exec ( query , screenId )
2022-08-27 07:01:29 +02:00
return nil
} )
if txErr != nil {
return nil , txErr
}
2023-03-21 03:20:57 +01:00
go cleanScreenCmds ( context . Background ( ) , screenId )
2023-03-13 18:50:29 +01:00
screen , err := GetScreenById ( ctx , screenId )
if err != nil {
return nil , err
}
screenLines , err := GetScreenLinesById ( ctx , screenId )
2022-08-27 07:01:29 +02:00
if err != nil {
return nil , err
}
for _ , lineId := range lineIds {
line := & LineType {
2023-03-21 03:20:57 +01:00
ScreenId : screenId ,
LineId : lineId ,
Remove : true ,
2022-08-27 07:01:29 +02:00
}
2023-03-13 18:50:29 +01:00
screenLines . Lines = append ( screenLines . Lines , line )
2022-08-27 07:01:29 +02:00
}
2023-03-13 18:50:29 +01:00
return & ModelUpdate { Screens : [ ] * ScreenType { screen } , ScreenLines : screenLines } , nil
2022-08-27 07:01:29 +02:00
}
2022-09-06 05:08:59 +02:00
2023-03-21 03:20:57 +01:00
func GetRunningScreenCmds ( ctx context . Context , screenId string ) ( [ ] * CmdType , error ) {
2022-09-06 05:08:59 +02:00
var rtn [ ] * CmdType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-21 03:20:57 +01:00
query := ` SELECT * from cmd WHERE cmdid IN (SELECT cmdid FROM line WHERE screenid = ?) AND status = ? `
2023-03-27 23:11:02 +02:00
rtn = dbutil . SelectMapsGen [ * CmdType ] ( tx , query , screenId , CmdStatusRunning )
2022-09-06 05:08:59 +02:00
return nil
} )
if txErr != nil {
return nil , txErr
}
return rtn , nil
}
2023-03-21 03:20:57 +01:00
func UpdateCmdTermOpts ( ctx context . Context , screenId string , cmdId string , termOpts TermOpts ) error {
2022-09-06 05:08:59 +02:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-21 03:20:57 +01:00
query := ` UPDATE cmd SET termopts = ? WHERE screenid = ? AND cmdid = ? `
tx . Exec ( query , termOpts , screenId , cmdId )
2023-03-27 03:48:43 +02:00
insertScreenCmdUpdate ( tx , screenId , cmdId , UpdateType_CmdTermOpts )
2022-09-06 05:08:59 +02:00
return nil
} )
return txErr
}
2022-09-13 21:06:12 +02:00
2023-02-01 02:56:56 +01:00
// returns riids of deleted RIs
2023-03-13 09:52:30 +01:00
func ScreenReset ( ctx context . Context , screenId string ) ( [ ] * RemoteInstance , error ) {
return WithTxRtn ( ctx , func ( tx * TxWrap ) ( [ ] * RemoteInstance , error ) {
2023-03-15 00:37:22 +01:00
query := ` SELECT sessionid FROM screen WHERE screenid = ? `
sessionId := tx . GetString ( query , screenId )
if sessionId == "" {
2023-03-13 09:52:30 +01:00
return nil , fmt . Errorf ( "screen does not exist" )
2023-02-01 02:56:56 +01:00
}
2023-03-15 00:37:22 +01:00
query = ` SELECT riid FROM remote_instance WHERE sessionid = ? AND screenid = ? `
riids := tx . SelectStrings ( query , sessionId , screenId )
2023-03-13 09:52:30 +01:00
var delRis [ ] * RemoteInstance
2023-02-01 02:56:56 +01:00
for _ , riid := range riids {
2023-03-15 00:37:22 +01:00
ri := & RemoteInstance { SessionId : sessionId , ScreenId : screenId , RIId : riid , Remove : true }
2023-02-01 02:56:56 +01:00
delRis = append ( delRis , ri )
}
2023-03-15 00:37:22 +01:00
query = ` DELETE FROM remote_instance WHERE sessionid = ? AND screenid = ? `
tx . Exec ( query , sessionId , screenId )
2023-03-13 09:52:30 +01:00
return delRis , nil
2023-02-01 02:56:56 +01:00
} )
}
2023-03-21 03:20:57 +01:00
func PurgeSession ( ctx context . Context , sessionId string ) ( UpdatePacket , error ) {
2022-12-27 01:09:21 +01:00
var newActiveSessionId string
2023-03-21 03:20:57 +01:00
var screenIds [ ] string
2022-12-27 01:09:21 +01:00
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" )
}
2023-03-21 03:20:57 +01:00
query = ` SELECT screenid FROM screen WHERE sessionid = ? `
screenIds = tx . SelectStrings ( query , sessionId )
for _ , screenId := range screenIds {
_ , err := PurgeScreen ( ctx , screenId , true )
if err != nil {
return fmt . Errorf ( "error purging screen[%s]: %v" , screenId , err )
}
}
2022-12-27 01:09:21 +01:00
query = ` DELETE FROM session WHERE sessionid = ? `
2023-02-15 01:17:54 +01:00
tx . Exec ( query , sessionId )
2022-12-27 01:09:21 +01: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 } )
2023-03-21 03:20:57 +01:00
for _ , screenId := range screenIds {
update . Screens = append ( update . Screens , & ScreenType { ScreenId : screenId , Remove : true } )
}
2022-12-27 01:09:21 +01:00
return update , nil
}
func fixActiveSessionId ( ctx context . Context ) ( string , error ) {
var newActiveSessionId string
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
curActiveSessionId := tx . GetString ( "SELECT activesessionid FROM client" )
query := ` SELECT sessionid FROM session WHERE sessionid = ? AND NOT archived `
if tx . Exists ( query , curActiveSessionId ) {
return nil
}
var err error
newActiveSessionId , err = GetFirstSessionId ( tx . Context ( ) )
if err != nil {
return err
}
2023-02-15 01:17:54 +01:00
tx . Exec ( "UPDATE client SET activesessionid = ?" , newActiveSessionId )
2022-12-27 01:09:21 +01:00
return nil
} )
if txErr != nil {
return "" , txErr
}
return newActiveSessionId , nil
}
2022-12-27 03:42:55 +01:00
func ArchiveSession ( ctx context . Context , sessionId string ) ( * ModelUpdate , error ) {
2022-12-27 01:09:21 +01: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-15 01:17:54 +01:00
tx . Exec ( query , time . Now ( ) . UnixMilli ( ) , sessionId )
2022-12-27 01:09:21 +01:00
newActiveSessionId , _ = fixActiveSessionId ( tx . Context ( ) )
return nil
} )
if txErr != nil {
return nil , txErr
}
bareSession , _ := GetBareSessionById ( ctx , sessionId )
2022-12-27 03:42:55 +01:00
update := & ModelUpdate { }
2022-12-27 01:09:21 +01:00
if bareSession != nil {
update . Sessions = append ( update . Sessions , bareSession )
}
if newActiveSessionId != "" {
update . ActiveSessionId = newActiveSessionId
}
return update , nil
}
2022-12-27 03:42:55 +01: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-15 01:17:54 +01:00
tx . Exec ( query , sessionId )
2022-12-27 03:42:55 +01:00
if activate {
query = ` UPDATE client SET activesessionid = ? `
2023-02-15 01:17:54 +01:00
tx . Exec ( query , sessionId )
2022-12-27 03:42:55 +01: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 21:06:12 +02:00
}
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" )
}
2022-12-25 22:03:11 +01:00
query = ` SELECT count(*) FROM screen WHERE sessionid = ? AND NOT archived `
2022-09-20 23:15:20 +02:00
rtn . NumScreens = tx . GetInt ( query , sessionId )
2022-12-25 22:03:11 +01:00
query = ` SELECT count(*) FROM screen WHERE sessionid = ? AND archived `
rtn . NumArchivedScreens = tx . GetInt ( query , sessionId )
2022-09-20 23:15:20 +02:00
query = ` SELECT count(*) FROM line WHERE sessionid = ? `
rtn . NumLines = tx . GetInt ( query , sessionId )
2023-03-21 03:20:57 +01:00
query = ` SELECT count(*) FROM cmd WHERE screenid IN (select screenid FROM screen WHERE sessionid = ?) `
2022-09-20 23:15:20 +02:00
rtn . NumCmds = tx . GetInt ( query , sessionId )
return nil
} )
if txErr != nil {
return nil , txErr
}
diskSize , err := SessionDiskSize ( sessionId )
if err != nil {
return nil , err
}
rtn . DiskStats = diskSize
return rtn , nil
}
2022-10-03 03:52:55 +02: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-04 04:04:48 +02:00
if alias != "" && tx . Exists ( query , alias , remoteId ) {
2022-10-03 03:52:55 +02:00
return fmt . Errorf ( "remote has duplicate alias, cannot update" )
}
query = ` UPDATE remote SET remotealias = ? WHERE remoteid = ? `
2023-02-15 01:17:54 +01:00
tx . Exec ( query , alias , remoteId )
2022-10-03 03:52:55 +02:00
}
if mode , found := editMap [ RemoteField_ConnectMode ] ; found {
query = ` UPDATE remote SET connectmode = ? WHERE remoteid = ? `
2023-02-15 01:17:54 +01:00
tx . Exec ( query , mode , remoteId )
2022-10-03 03:52:55 +02:00
}
if autoInstall , found := editMap [ RemoteField_AutoInstall ] ; found {
query = ` UPDATE remote SET autoinstall = ? WHERE remoteid = ? `
2023-02-15 01:17:54 +01:00
tx . Exec ( query , autoInstall , remoteId )
2022-10-03 03:52:55 +02:00
}
if sshKey , found := editMap [ RemoteField_SSHKey ] ; found {
query = ` UPDATE remote SET sshopts = json_set(sshopts, '$.sshidentity', ?) WHERE remoteid = ? `
2023-02-15 01:17:54 +01:00
tx . Exec ( query , sshKey , remoteId )
2022-10-03 03:52:55 +02:00
}
if sshPassword , found := editMap [ RemoteField_SSHPassword ] ; found {
query = ` UPDATE remote SET sshopts = json_set(sshopts, '$.sshpassword', ?) WHERE remoteid = ? `
2023-02-15 01:17:54 +01:00
tx . Exec ( query , sshPassword , remoteId )
2022-10-03 03:52:55 +02:00
}
if color , found := editMap [ RemoteField_Color ] ; found {
query = ` UPDATE remote SET remoteopts = json_set(remoteopts, '$.color', ?) WHERE remoteid = ? `
2023-02-15 01:17:54 +01:00
tx . Exec ( query , color , remoteId )
2022-10-03 03:52:55 +02: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-07 03:33:54 +02:00
const (
2023-03-13 09:52:30 +01:00
ScreenField_AnchorLine = "anchorline" // int
ScreenField_AnchorOffset = "anchoroffset" // int
ScreenField_SelectedLine = "selectedline" // int
ScreenField_Focus = "focustype" // string
ScreenField_TabColor = "tabcolor" // string
ScreenField_PTerm = "pterm" // string
ScreenField_Name = "name" // string
2022-10-07 03:33:54 +02:00
)
2023-03-13 09:52:30 +01:00
func UpdateScreen ( ctx context . Context , screenId string , editMap map [ string ] interface { } ) ( * ScreenType , error ) {
2022-10-07 03:33:54 +02:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-13 09:52:30 +01:00
query := ` SELECT screenid FROM screen WHERE screenid = ? `
if ! tx . Exists ( query , screenId ) {
return fmt . Errorf ( "screen not found" )
2022-10-07 03:33:54 +02:00
}
2023-03-13 09:52:30 +01:00
if anchorLine , found := editMap [ ScreenField_AnchorLine ] ; found {
query = ` UPDATE screen SET anchor = json_set(anchor, '$.anchorline', ?) WHERE screenid = ? `
tx . Exec ( query , anchorLine , screenId )
2022-10-11 10:11:04 +02:00
}
2023-03-13 09:52:30 +01:00
if anchorOffset , found := editMap [ ScreenField_AnchorOffset ] ; found {
query = ` UPDATE screen SET anchor = json_set(anchor, '$.anchoroffset', ?) WHERE screenid = ? `
tx . Exec ( query , anchorOffset , screenId )
2022-10-07 03:33:54 +02:00
}
2023-03-13 09:52:30 +01:00
if sline , found := editMap [ ScreenField_SelectedLine ] ; found {
query = ` UPDATE screen SET selectedline = ? WHERE screenid = ? `
tx . Exec ( query , sline , screenId )
2022-10-07 10:08:03 +02:00
}
2023-03-13 09:52:30 +01:00
if focusType , found := editMap [ ScreenField_Focus ] ; found {
query = ` UPDATE screen SET focustype = ? WHERE screenid = ? `
tx . Exec ( query , focusType , screenId )
2022-10-11 10:11:04 +02:00
}
2023-03-13 09:52:30 +01:00
if tabColor , found := editMap [ ScreenField_TabColor ] ; found {
query = ` UPDATE screen SET screenopts = json_set(screenopts, '$.tabcolor', ?) WHERE screenid = ? `
tx . Exec ( query , tabColor , screenId )
2022-10-07 03:33:54 +02:00
}
2023-03-13 09:52:30 +01:00
if pterm , found := editMap [ ScreenField_PTerm ] ; found {
query = ` UPDATE screen SET screenopts = json_set(screenopts, '$.pterm', ?) WHERE screenid = ? `
tx . Exec ( query , pterm , screenId )
}
if name , found := editMap [ ScreenField_Name ] ; found {
query = ` UPDATE screen SET name = ? WHERE screenid = ? `
tx . Exec ( query , name , screenId )
2022-10-07 10:08:03 +02:00
}
return nil
} )
if txErr != nil {
return nil , txErr
}
2023-03-13 09:52:30 +01:00
return GetScreenById ( ctx , screenId )
2022-10-07 10:08:03 +02:00
}
2023-03-21 03:20:57 +01:00
func GetLineResolveItems ( ctx context . Context , screenId string ) ( [ ] ResolveItem , error ) {
2022-10-07 08:58:38 +02:00
var rtn [ ] ResolveItem
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-21 03:20:57 +01:00
query := ` SELECT lineid as id, linenum as num FROM line WHERE screenid = ? ORDER BY linenum `
tx . Select ( & rtn , query , screenId )
2022-10-07 08:58:38 +02:00
return nil
} )
if txErr != nil {
return nil , txErr
}
return rtn , nil
}
2022-10-12 08:11:43 +02:00
2023-03-21 03:20:57 +01:00
func UpdateScreenWithCmdFg ( ctx context . Context , screenId string , cmdId string ) ( * ScreenType , error ) {
return WithTxRtn ( ctx , func ( tx * TxWrap ) ( * ScreenType , error ) {
query := ` SELECT screenid
FROM screen s
WHERE s . screenid = ? AND s . focustype = ' cmd - fg '
AND s . selectedline IN ( SELECT linenum FROM line l WHERE l . screenid = s . screenid AND l . cmdid = ? )
`
if ! tx . Exists ( query , screenId , cmdId ) {
return nil , nil
2022-10-12 08:11:43 +02:00
}
2023-03-21 03:20:57 +01:00
editMap := make ( map [ string ] interface { } )
editMap [ ScreenField_Focus ] = ScreenFocusInput
screen , err := UpdateScreen ( tx . Context ( ) , screenId , editMap )
if err != nil {
return nil , err
2022-10-12 08:11:43 +02:00
}
2023-03-21 03:20:57 +01:00
return screen , nil
2022-10-12 08:11:43 +02:00
} )
}
2022-11-28 09:13:00 +01: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-15 01:17:54 +01:00
tx . NamedExec ( query , stateBase )
2022-11-28 09:13:00 +01: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-15 01:17:54 +01:00
tx . NamedExec ( query , stateDiff . ToMap ( ) )
2022-11-28 09:13:00 +01:00
return nil
} )
if txErr != nil {
return txErr
}
return nil
}
// returns error when not found
2022-11-29 03:03:02 +01:00
func GetFullState ( ctx context . Context , ssPtr ShellStatePtr ) ( * packet . ShellState , error ) {
2022-11-28 09:13:00 +01:00
var state * packet . ShellState
2022-11-29 03:03:02 +01:00
if ssPtr . BaseHash == "" {
2022-11-28 09:13:00 +01: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-15 01:17:54 +01:00
found := tx . Get ( & stateBase , query , ssPtr . BaseHash )
2022-11-28 09:13:00 +01:00
if ! found {
2022-11-29 03:03:02 +01:00
return fmt . Errorf ( "ShellState %s not found" , ssPtr . BaseHash )
2022-11-28 09:13:00 +01:00
}
state = & packet . ShellState { }
err := state . DecodeShellState ( stateBase . Data )
if err != nil {
return err
}
2022-11-29 03:03:02 +01:00
for idx , diffHash := range ssPtr . DiffHashArr {
2022-11-28 09:13:00 +01:00
query = ` SELECT * FROM state_diff WHERE diffhash = ? `
2023-03-27 23:11:02 +02:00
stateDiff := dbutil . GetMapGen [ * StateDiff ] ( tx , query , diffHash )
2022-11-28 09:13:00 +01:00
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-06 07:59:00 +01: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-15 01:17:54 +01:00
tx . Exec ( query , starVal , lineId )
2022-12-06 07:59:00 +01:00
return nil
} )
if txErr != nil {
return txErr
}
return nil
}
2023-02-01 07:21:19 +01: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-15 01:17:54 +01:00
tx . Exec ( query , heightVal , lineId )
2023-02-01 07:21:19 +01:00
return nil
} )
if txErr != nil {
return txErr
}
return nil
}
2023-03-25 20:54:56 +01:00
func UpdateLineRenderer ( ctx context . Context , screenId string , lineId string , renderer string ) error {
2023-03-17 22:47:30 +01:00
return WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` UPDATE line SET renderer = ? WHERE lineid = ? `
tx . Exec ( query , renderer , lineId )
2023-03-25 20:54:56 +01:00
if isWebShare ( tx , screenId ) {
2023-03-26 22:21:58 +02:00
insertScreenLineUpdate ( tx , screenId , lineId , UpdateType_LineRenderer )
2023-03-25 20:54:56 +01:00
}
2023-03-17 22:47:30 +01:00
return nil
} )
}
2022-12-06 07:59:00 +01:00
// can return nil, nil if line is not found
2023-03-21 03:20:57 +01:00
func GetLineById ( ctx context . Context , screenId string , lineId string ) ( * LineType , error ) {
2022-12-06 07:59:00 +01:00
var rtn * LineType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
var line LineType
2023-03-21 03:20:57 +01:00
query := ` SELECT * FROM line WHERE screenid = ? AND lineid = ? `
found := tx . Get ( & line , query , screenId , lineId )
2022-12-06 07:59:00 +01:00
if found {
rtn = & line
}
return nil
} )
if txErr != nil {
return nil , txErr
}
return rtn , nil
}
2022-12-22 02:45:40 +01:00
2023-03-25 20:54:56 +01:00
func SetLineArchivedById ( ctx context . Context , screenId string , lineId string , archived bool ) error {
2022-12-22 02:45:40 +01:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-12-28 08:12:27 +01:00
query := ` UPDATE line SET archived = ? WHERE lineid = ? `
2023-02-15 01:17:54 +01:00
tx . Exec ( query , archived , lineId )
2023-03-25 20:54:56 +01:00
if isWebShare ( tx , screenId ) {
2023-03-26 22:21:58 +02:00
insertScreenLineUpdate ( tx , screenId , lineId , UpdateType_LineArchived )
2023-03-25 20:54:56 +01:00
}
2022-12-22 02:45:40 +01:00
return nil
} )
return txErr
}
2023-03-21 03:20:57 +01:00
func purgeCmdByScreenId ( ctx context . Context , screenId string , cmdId string ) error {
2022-12-22 02:45:40 +01:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-21 03:20:57 +01:00
query := ` DELETE FROM cmd WHERE screenid = ? AND cmdid = ? `
tx . Exec ( query , screenId , cmdId )
return DeletePtyOutFile ( tx . Context ( ) , screenId , cmdId )
2022-12-22 02:45:40 +01:00
} )
return txErr
}
2023-03-21 03:20:57 +01:00
func PurgeLinesByIds ( ctx context . Context , screenId string , lineIds [ ] string ) error {
2023-03-03 22:31:16 +01:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-25 20:54:56 +01:00
isWS := isWebShare ( tx , screenId )
2023-03-03 22:31:16 +01:00
for _ , lineId := range lineIds {
2023-03-21 03:20:57 +01:00
query := ` SELECT cmdid FROM line WHERE screenid = ? AND lineid = ? `
cmdId := tx . GetString ( query , screenId , lineId )
query = ` DELETE FROM line WHERE screenid = ? AND lineid = ? `
tx . Exec ( query , screenId , lineId )
query = ` DELETE FROM history WHERE screenid = ? AND lineid = ? `
tx . Exec ( query , screenId , lineId )
2023-03-03 22:31:16 +01:00
if cmdId != "" {
2023-03-21 03:20:57 +01:00
err := purgeCmdByScreenId ( tx . Context ( ) , screenId , cmdId )
if err != nil {
return err
2022-12-22 02:45:40 +01:00
}
}
2023-03-25 20:54:56 +01:00
if isWS {
2023-03-26 22:21:58 +02:00
insertScreenLineUpdate ( tx , screenId , lineId , UpdateType_LineDel )
2023-03-25 20:54:56 +01:00
}
2022-12-22 02:45:40 +01:00
}
return nil
} )
return txErr
}
2022-12-31 02:01:17 +01:00
2023-03-15 00:37:22 +01:00
func GetRIsForScreen ( ctx context . Context , sessionId string , screenId string ) ( [ ] * RemoteInstance , error ) {
2022-12-31 02:01:17 +01:00
var rtn [ ] * RemoteInstance
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-15 00:37:22 +01:00
query := ` SELECT * FROM remote_instance WHERE sessionid = ? AND (screenid = '' OR screenid = ?) `
2023-03-27 23:11:02 +02:00
rtn = dbutil . SelectMapsGen [ * RemoteInstance ] ( tx , query , sessionId , screenId )
2022-12-31 02:01:17 +01:00
return nil
} )
if txErr != nil {
return nil , txErr
}
return rtn , nil
}
2023-01-17 08:36:52 +01:00
2023-01-23 21:54:32 +01:00
func GetCurDayStr ( ) string {
2023-01-17 08:36:52 +01:00
now := time . Now ( )
dayStr := now . Format ( "2006-01-02" )
2023-01-23 21:54:32 +01:00
return dayStr
}
func UpdateCurrentActivity ( ctx context . Context , update ActivityUpdate ) error {
now := time . Now ( )
dayStr := GetCurDayStr ( )
2023-01-17 08:36:52 +01:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-01-23 21:54:32 +01:00
var tdata TelemetryData
query := ` SELECT tdata FROM activity WHERE day = ? `
2023-02-15 01:17:54 +01:00
found := tx . Get ( & tdata , query , dayStr )
2023-01-23 21:54:32 +01:00
if ! found {
2023-02-24 00:17:47 +01:00
query = ` INSERT INTO activity ( day , uploaded , tdata , tzname , tzoffset , clientversion , clientarch , buildtime , osrelease )
VALUES ( ? , 0 , ? , ? , ? , ? , ? , ? , ? ) `
2023-01-17 08:36:52 +01:00
tzName , tzOffset := now . Zone ( )
if len ( tzName ) > MaxTzNameLen {
tzName = tzName [ 0 : MaxTzNameLen ]
}
2023-02-24 00:17:47 +01:00
tx . Exec ( query , dayStr , tdata , tzName , tzOffset , scbase . PromptVersion , scbase . ClientArch ( ) , scbase . BuildTime , scbase . MacOSRelease ( ) )
2023-01-17 08:36:52 +01:00
}
2023-01-23 21:54:32 +01:00
tdata . NumCommands += update . NumCommands
tdata . FgMinutes += update . FgMinutes
tdata . ActiveMinutes += update . ActiveMinutes
tdata . OpenMinutes += update . OpenMinutes
2023-02-22 07:41:56 +01:00
tdata . ClickShared += update . ClickShared
tdata . HistoryView += update . HistoryView
tdata . BookmarksView += update . BookmarksView
if update . NumConns > 0 {
tdata . NumConns = update . NumConns
}
2023-01-18 01:02:44 +01:00
query = ` UPDATE activity
2023-01-23 21:54:32 +01:00
SET tdata = ? ,
2023-02-24 00:17:47 +01:00
clientversion = ? ,
buildtime = ?
2023-01-18 01:02:44 +01:00
WHERE day = ? `
2023-02-24 00:17:47 +01:00
tx . Exec ( query , tdata , scbase . PromptVersion , scbase . BuildTime , dayStr )
2023-01-17 08:36:52 +01: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-15 01:17:54 +01:00
tx . Select ( & rtn , query )
2023-01-17 08:36:52 +01:00
return nil
} )
if txErr != nil {
return nil , txErr
}
return rtn , nil
}
2023-01-23 21:54:32 +01:00
// note, will not mark the current day as uploaded
2023-01-17 08:36:52 +01:00
func MarkActivityAsUploaded ( ctx context . Context , activityArr [ ] * ActivityType ) error {
2023-01-23 21:54:32 +01:00
dayStr := GetCurDayStr ( )
2023-01-17 08:36:52 +01:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` UPDATE activity SET uploaded = 1 WHERE day = ? `
for _ , activity := range activityArr {
2023-01-23 21:54:32 +01:00
if activity . Day == dayStr {
continue
}
2023-02-15 01:17:54 +01:00
tx . Exec ( query , activity . Day )
2023-01-17 08:36:52 +01:00
}
return nil
} )
return txErr
}
2023-02-21 00:41:39 +01: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-21 06:39:29 +01:00
2023-02-21 07:00:07 +01:00
type bookmarkOrderType struct {
BookmarkId string
OrderIdx int64
}
func GetBookmarks ( ctx context . Context , tag string ) ( [ ] * BookmarkType , error ) {
var bms [ ] * BookmarkType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
var query string
if tag == "" {
query = ` SELECT * FROM bookmark `
2023-03-27 23:11:02 +02:00
bms = dbutil . SelectMapsGen [ * BookmarkType ] ( tx , query )
2023-02-21 07:00:07 +01:00
} else {
query = ` SELECT * FROM bookmark WHERE EXISTS (SELECT 1 FROM json_each(tags) WHERE value = ?) `
2023-03-27 23:11:02 +02:00
bms = dbutil . SelectMapsGen [ * BookmarkType ] ( tx , query , tag )
2023-02-21 07:00:07 +01:00
}
2023-03-27 23:11:02 +02:00
bmMap := dbutil . MakeGenMap ( bms )
2023-02-21 07:00:07 +01:00
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
}
}
return nil
} )
if txErr != nil {
return nil , txErr
}
return bms , nil
}
2023-02-22 03:03:13 +01: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 = ? `
2023-03-27 23:11:02 +02:00
rtn = dbutil . GetMapGen [ * BookmarkType ] ( tx , query , bookmarkId )
2023-02-22 03:03:13 +01:00
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 )
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-03-24 18:34:07 +01:00
func GetBookmarkIdsByCmdStr ( ctx context . Context , cmdStr string ) ( [ ] string , error ) {
return WithTxRtn ( ctx , func ( tx * TxWrap ) ( [ ] string , error ) {
query := ` SELECT bookmarkid FROM bookmark WHERE cmdstr = ? `
bmIds := tx . SelectStrings ( query , cmdStr )
return bmIds , nil
} )
}
2023-02-21 07:00:07 +01:00
// ignores OrderIdx field
2023-02-21 06:39:29 +01: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 )
}
return nil
} )
return txErr
}
2023-02-22 03:03:13 +01: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-21 06:39:29 +01: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 )
fixupBookmarkOrder ( tx )
return nil
} )
return txErr
}
2023-03-02 09:31:19 +01:00
func CreatePlaybook ( ctx context . Context , name string ) ( * PlaybookType , error ) {
2023-03-10 03:44:01 +01:00
return WithTxRtn ( ctx , func ( tx * TxWrap ) ( * PlaybookType , error ) {
2023-03-02 09:31:19 +01:00
query := ` SELECT playbookid FROM playbook WHERE name = ? `
if tx . Exists ( query , name ) {
2023-03-10 03:44:01 +01:00
return nil , fmt . Errorf ( "playbook %q already exists" , name )
2023-03-02 09:31:19 +01:00
}
2023-03-10 03:44:01 +01:00
rtn := & PlaybookType { }
2023-03-02 09:31:19 +01:00
rtn . PlaybookId = uuid . New ( ) . String ( )
rtn . PlaybookName = name
query = ` INSERT INTO playbook ( playbookid , playbookname , description , entryids )
VALUES ( : playbookid , : playbookname , : description , : entryids ) `
tx . Exec ( query , rtn . ToMap ( ) )
2023-03-10 03:44:01 +01:00
return rtn , nil
2023-03-02 09:31:19 +01:00
} )
}
func selectPlaybook ( tx * TxWrap , playbookId string ) * PlaybookType {
query := ` SELECT * FROM playbook where playbookid = ? `
2023-03-27 23:11:02 +02:00
playbook := dbutil . GetMapGen [ * PlaybookType ] ( tx , query , playbookId )
2023-03-02 09:31:19 +01:00
return playbook
}
func AddPlaybookEntry ( ctx context . Context , entry * PlaybookEntry ) error {
if entry . EntryId == "" {
return fmt . Errorf ( "invalid entryid" )
}
2023-03-10 03:44:01 +01:00
return WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-02 09:31:19 +01:00
playbook := selectPlaybook ( tx , entry . PlaybookId )
if playbook == nil {
return fmt . Errorf ( "cannot add entry, playbook does not exist" )
}
query := ` SELECT entryid FROM playbook_entry WHERE entryid = ? `
if tx . Exists ( query , entry . EntryId ) {
return fmt . Errorf ( "cannot add entry, entryid already exists" )
}
query = ` INSERT INTO playbook_entry ( entryid , playbookid , description , alias , cmdstr , createdts , updatedts )
VALUES ( : entryid , : playbookid , : description , : alias , : cmdstr , : createdts , : updatedts ) `
tx . Exec ( query , entry )
playbook . EntryIds = append ( playbook . EntryIds , entry . EntryId )
query = ` UPDATE playbook SET entryids = ? WHERE playbookid = ? `
tx . Exec ( query , quickJsonArr ( playbook . EntryIds ) , entry . PlaybookId )
return nil
} )
}
func RemovePlaybookEntry ( ctx context . Context , playbookId string , entryId string ) error {
2023-03-10 03:44:01 +01:00
return WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-02 09:31:19 +01:00
playbook := selectPlaybook ( tx , playbookId )
if playbook == nil {
return fmt . Errorf ( "cannot remove playbook entry, playbook does not exist" )
}
query := ` SELECT entryid FROM playbook_entry WHERE entryid = ? `
if ! tx . Exists ( query , entryId ) {
return fmt . Errorf ( "cannot remove playbook entry, entry does not exist" )
}
query = ` DELETE FROM playbook_entry WHERE entryid = ? `
tx . Exec ( query , entryId )
playbook . RemoveEntry ( entryId )
query = ` UPDATE playbook SET entryids = ? WHERE playbookid = ? `
tx . Exec ( query , quickJsonArr ( playbook . EntryIds ) , playbookId )
return nil
} )
}
func GetPlaybookById ( ctx context . Context , playbookId string ) ( * PlaybookType , error ) {
2023-03-10 03:44:01 +01:00
return WithTxRtn ( ctx , func ( tx * TxWrap ) ( * PlaybookType , error ) {
rtn := selectPlaybook ( tx , playbookId )
2023-03-02 09:31:19 +01:00
if rtn == nil {
2023-03-10 03:44:01 +01:00
return nil , nil
2023-03-02 09:31:19 +01:00
}
query := ` SELECT * FROM playbook_entry WHERE playbookid = ? `
tx . Select ( & rtn . Entries , query , playbookId )
rtn . OrderEntries ( )
2023-03-10 03:44:01 +01:00
return rtn , nil
2023-03-02 09:31:19 +01:00
} )
}
2023-03-03 07:26:15 +01:00
func getLineIdsFromHistoryItems ( historyItems [ ] * HistoryItemType ) [ ] string {
var rtn [ ] string
for _ , hitem := range historyItems {
if hitem . LineId != "" {
rtn = append ( rtn , hitem . LineId )
}
}
return rtn
}
func getCmdIdsFromHistoryItems ( historyItems [ ] * HistoryItemType ) [ ] string {
var rtn [ ] string
for _ , hitem := range historyItems {
if hitem . CmdId != "" {
rtn = append ( rtn , hitem . CmdId )
}
}
return rtn
}
func GetLineCmdsFromHistoryItems ( ctx context . Context , historyItems [ ] * HistoryItemType ) ( [ ] * LineType , [ ] * CmdType , error ) {
if len ( historyItems ) == 0 {
return nil , nil , nil
}
2023-03-10 03:44:01 +01:00
return WithTxRtn3 ( ctx , func ( tx * TxWrap ) ( [ ] * LineType , [ ] * CmdType , error ) {
var lineArr [ ] * LineType
2023-03-03 07:26:15 +01:00
query := ` SELECT * FROM line WHERE lineid IN (SELECT value FROM json_each(?)) `
tx . Select ( & lineArr , query , quickJsonArr ( getLineIdsFromHistoryItems ( historyItems ) ) )
query = ` SELECT * FROM cmd WHERE cmdid IN (SELECT value FROM json_each(?)) `
2023-03-27 23:11:02 +02:00
cmdArr := dbutil . SelectMapsGen [ * CmdType ] ( tx , query , quickJsonArr ( getCmdIdsFromHistoryItems ( historyItems ) ) )
2023-03-10 03:44:01 +01:00
return lineArr , cmdArr , nil
2023-03-03 07:26:15 +01:00
} )
}
2023-03-03 22:31:16 +01:00
func PurgeHistoryByIds ( ctx context . Context , historyIds [ ] string ) ( [ ] * HistoryItemType , error ) {
2023-03-10 03:44:01 +01:00
return WithTxRtn ( ctx , func ( tx * TxWrap ) ( [ ] * HistoryItemType , error ) {
2023-03-03 22:31:16 +01:00
query := ` SELECT * FROM history WHERE historyid IN (SELECT value FROM json_each(?)) `
2023-03-27 23:11:02 +02:00
rtn := dbutil . SelectMapsGen [ * HistoryItemType ] ( tx , query , quickJsonArr ( historyIds ) )
2023-03-03 22:31:16 +01:00
query = ` DELETE FROM history WHERE historyid IN (SELECT value FROM json_each(?)) `
tx . Exec ( query , quickJsonArr ( historyIds ) )
for _ , hitem := range rtn {
if hitem . LineId != "" {
2023-03-21 03:20:57 +01:00
err := PurgeLinesByIds ( tx . Context ( ) , hitem . ScreenId , [ ] string { hitem . LineId } )
2023-03-03 22:31:16 +01:00
if err != nil {
2023-03-10 03:44:01 +01:00
return nil , err
2023-03-03 22:31:16 +01:00
}
}
}
2023-03-10 03:44:01 +01:00
return rtn , nil
2023-03-03 22:31:16 +01:00
} )
}
2023-03-24 18:34:07 +01:00
func ScreenWebShareStart ( ctx context . Context , screenId string , shareOpts ScreenWebShareOpts ) error {
return WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT screenid FROM screen WHERE screenid = ? `
if ! tx . Exists ( query , screenId ) {
return fmt . Errorf ( "screen does not exist" )
}
shareMode := tx . GetString ( ` SELECT sharemode FROM screen WHERE screenid = ? ` , screenId )
if shareMode == ShareModeWeb {
return fmt . Errorf ( "screen is already shared to web" )
}
if shareMode != ShareModeLocal {
return fmt . Errorf ( "screen cannot be shared, invalid current share mode %q (must be local)" , shareMode )
}
2023-03-28 09:24:37 +02:00
nowTs := time . Now ( ) . UnixMilli ( )
2023-03-24 18:34:07 +01:00
query = ` UPDATE screen SET sharemode = ?, webshareopts = ? WHERE screenid = ? `
tx . Exec ( query , ShareModeWeb , quickJson ( shareOpts ) , screenId )
2023-03-26 22:21:58 +02:00
insertScreenUpdate ( tx , screenId , UpdateType_ScreenNew )
2023-03-25 20:54:56 +01:00
query = ` INSERT INTO screenupdate ( screenid , lineid , updatetype , updatets )
2023-03-24 18:34:07 +01:00
SELECT screenid , lineid , ? , ? FROM line WHERE screenid = ? ORDER BY linenum `
2023-03-28 09:24:37 +02:00
tx . Exec ( query , UpdateType_LineNew , nowTs , screenId )
query = ` INSERT INTO screenupdate ( screenid , lineid , updatetype , updatets )
SELECT c . screenid , l . lineid , ? , ? FROM cmd c , line l WHERE c . screenid = ? AND l . cmdid = c . cmdid `
tx . Exec ( query , UpdateType_PtyPos , nowTs , screenId )
2023-03-27 08:07:30 +02:00
NotifyUpdateWriter ( )
2023-03-24 18:34:07 +01:00
return nil
} )
}
func ScreenWebShareStop ( ctx context . Context , screenId string ) error {
return WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT screenid FROM screen WHERE screenid = ? `
if ! tx . Exists ( query , screenId ) {
return fmt . Errorf ( "screen does not exist" )
}
shareMode := tx . GetString ( ` SELECT sharemode FROM screen WHERE screenid = ? ` , screenId )
if shareMode != ShareModeWeb {
return fmt . Errorf ( "screen is not currently shared to the web" )
}
query = ` UPDATE screen SET sharemode = ?, webshareopts = ? WHERE screenid = ? `
tx . Exec ( query , ShareModeLocal , "null" , screenId )
2023-03-25 20:54:56 +01:00
query = ` DELETE FROM screenupdate WHERE screenid = ? `
2023-03-24 18:34:07 +01:00
tx . Exec ( query , screenId )
2023-03-28 09:24:37 +02:00
query = ` DELETE FROM webptypos WHERE screenid = ? `
tx . Exec ( query , screenId )
2023-03-26 22:21:58 +02:00
insertScreenUpdate ( tx , screenId , UpdateType_ScreenDel )
2023-03-24 18:34:07 +01:00
return nil
} )
}
func isWebShare ( tx * TxWrap , screenId string ) bool {
return tx . Exists ( ` SELECT screenid FROM screen WHERE screenid = ? AND sharemode = ? ` , screenId , ShareModeWeb )
}
2023-03-25 20:54:56 +01:00
2023-03-26 22:21:58 +02:00
func insertScreenUpdate ( tx * TxWrap , screenId string , updateType string ) {
if screenId == "" {
tx . SetErr ( errors . New ( "invalid screen-update, screenid is empty" ) )
return
}
query := ` INSERT INTO screenupdate (screenid, lineid, updatetype, updatets) VALUES (?, ?, ?, ?) `
tx . Exec ( query , screenId , "" , updateType , time . Now ( ) . UnixMilli ( ) )
2023-03-27 08:07:30 +02:00
NotifyUpdateWriter ( )
2023-03-26 22:21:58 +02:00
}
func insertScreenLineUpdate ( tx * TxWrap , screenId string , lineId string , updateType string ) {
2023-03-25 20:54:56 +01:00
if screenId == "" {
tx . SetErr ( errors . New ( "invalid screen-update, screenid is empty" ) )
return
}
if lineId == "" {
tx . SetErr ( errors . New ( "invalid screen-update, lineid is empty" ) )
return
}
2023-03-27 08:07:30 +02:00
query := ` SELECT updateid FROM screenupdate WHERE screenid = ? AND lineid = ? AND (updatetype = ? OR updatetype = ?) `
if ! tx . Exists ( query , screenId , lineId , updateType , UpdateType_LineNew ) {
query = ` INSERT INTO screenupdate (screenid, lineid, updatetype, updatets) VALUES (?, ?, ?, ?) `
tx . Exec ( query , screenId , lineId , updateType , time . Now ( ) . UnixMilli ( ) )
NotifyUpdateWriter ( )
}
2023-03-25 20:54:56 +01:00
}
2023-03-26 22:21:58 +02:00
func insertScreenCmdUpdate ( tx * TxWrap , screenId string , cmdId string , updateType string ) {
2023-03-25 20:54:56 +01:00
if screenId == "" {
tx . SetErr ( errors . New ( "invalid screen-update, screenid is empty" ) )
return
}
if cmdId == "" {
tx . SetErr ( errors . New ( "invalid screen-update, cmdid is empty" ) )
return
}
query := ` SELECT lineid FROM line WHERE screenid = ? AND cmdid = ? `
lineId := tx . GetString ( query , screenId , cmdId )
if lineId != "" {
2023-03-26 22:21:58 +02:00
insertScreenLineUpdate ( tx , screenId , lineId , updateType )
2023-03-25 20:54:56 +01:00
}
}
2023-03-26 22:21:58 +02:00
2023-03-27 03:48:43 +02:00
func GetScreenUpdates ( ctx context . Context , maxNum int ) ( [ ] * ScreenUpdateType , error ) {
return WithTxRtn ( ctx , func ( tx * TxWrap ) ( [ ] * ScreenUpdateType , error ) {
var updates [ ] * ScreenUpdateType
query := ` SELECT * FROM screenupdate ORDER BY updateid LIMIT ? `
tx . Select ( & updates , query , maxNum )
return updates , nil
} )
}
func RemoveScreenUpdate ( ctx context . Context , updateId int64 ) error {
2023-03-26 22:21:58 +02:00
return WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` DELETE FROM screenupdate WHERE updateid = ? `
tx . Exec ( query , updateId )
return nil
} )
}
2023-03-28 09:24:37 +02:00
func SetWebScreenPtyPosDelIntent ( screenId string , lineId string ) {
WebScreenPtyPosLock . Lock ( )
defer WebScreenPtyPosLock . Unlock ( )
WebScreenPtyPosDelIntent [ screenId + ":" + lineId ] = true
}
func ClearWebScreenPtyPosDelIntent ( screenId string , lineId string ) bool {
WebScreenPtyPosLock . Lock ( )
defer WebScreenPtyPosLock . Unlock ( )
rtn := WebScreenPtyPosDelIntent [ screenId + ":" + lineId ]
delete ( WebScreenPtyPosDelIntent , screenId + ":" + lineId )
return rtn
}
func MaybeInsertPtyPosUpdate ( ctx context . Context , screenId string , cmdId string ) error {
2023-03-26 22:21:58 +02:00
return WithTx ( ctx , func ( tx * TxWrap ) error {
2023-03-28 09:24:37 +02:00
if ! isWebShare ( tx , screenId ) {
return nil
}
2023-03-26 22:21:58 +02:00
query := ` SELECT lineid FROM line WHERE screenid = ? AND cmdid = ? `
lineId := tx . GetString ( query , screenId , cmdId )
if lineId == "" {
return fmt . Errorf ( "invalid ptypos update, no lineid found for %s/%s" , screenId , cmdId )
}
2023-03-28 09:24:37 +02:00
ClearWebScreenPtyPosDelIntent ( screenId , lineId ) // clear delete intention because we have a new update
2023-03-26 22:21:58 +02:00
query = ` SELECT updateid FROM screenupdate WHERE screenid = ? AND lineid = ? AND updatetype = ? `
if ! tx . Exists ( query , screenId , lineId , UpdateType_PtyPos ) {
2023-03-27 08:07:30 +02:00
query := ` INSERT INTO screenupdate (screenid, lineid, updatetype, updatets) VALUES (?, ?, ?, ?) `
tx . Exec ( query , screenId , lineId , UpdateType_PtyPos , time . Now ( ) . UnixMilli ( ) )
NotifyUpdateWriter ( )
2023-03-26 22:21:58 +02:00
}
return nil
} )
}
2023-03-28 09:24:37 +02:00
func MaybeRemovePtyPosUpdate ( ctx context . Context , screenId string , lineId string , updateId int64 ) error {
return WithTx ( ctx , func ( tx * TxWrap ) error {
intent := ClearWebScreenPtyPosDelIntent ( screenId , lineId ) // check for intention before deleting
if ! intent {
return nil
}
query := ` DELETE FROM screenupdate WHERE updateid = ? `
tx . Exec ( query , updateId )
return nil
} )
}
2023-03-26 22:21:58 +02:00
func GetWebPtyPos ( ctx context . Context , screenId string , lineId string ) ( int64 , error ) {
return WithTxRtn ( ctx , func ( tx * TxWrap ) ( int64 , error ) {
query := ` SELECT ptypos FROM webptypos WHERE screenid = ? AND lineid = ? `
ptyPos := tx . GetInt ( query , screenId , lineId )
return int64 ( ptyPos ) , nil
} )
}
func SetWebPtyPos ( ctx context . Context , screenId string , lineId string , ptyPos int64 ) error {
return WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT screenid FROM webptypos WHERE screenid = ? AND lineid = ? `
if tx . Exists ( query , screenId , lineId ) {
query = ` UPDATE webptypos SET ptypos = ? WHERE screenid = ? AND lineid = ? `
tx . Exec ( query , ptyPos , screenId , lineId )
} else {
query = ` INSERT INTO webptypos (screenid, lineid, ptypos) VALUES (?, ?, ?) `
tx . Exec ( query , screenId , lineId , ptyPos )
}
return nil
} )
}