checkpoint, adding 'screen' concept to contain windows

This commit is contained in:
sawka 2022-07-12 21:51:17 -07:00
parent 11087c10be
commit db841f2951
5 changed files with 156 additions and 118 deletions

View File

@ -340,17 +340,7 @@ func HandleCreateWindow(w http.ResponseWriter, r *http.Request) {
return
}
name := qvals.Get("name")
windowId, err := sstore.InsertWindow(r.Context(), sessionId, name)
if err != nil {
WriteJsonError(w, fmt.Errorf("inserting new window: %w", err))
return
}
window, err := sstore.GetWindowById(r.Context(), sessionId, windowId)
if err != nil {
WriteJsonError(w, fmt.Errorf("getting new window: %w", err))
return
}
WriteJsonSuccess(w, window)
fmt.Printf("insert-window %s\n", name)
return
}

View File

@ -2,25 +2,25 @@ CREATE TABLE session (
sessionid varchar(36) PRIMARY KEY,
name varchar(50) NOT NULL,
sessionidx int NOT NULL,
activescreenid varchar(36) NOT NULL,
notifynum int NOT NULL
);
CREATE UNIQUE INDEX session_name_unique ON session(name);
CREATE TABLE window (
sessionid varchar(36) NOT NULL,
windowid varchar(36) NOT NULL,
name varchar(50) NOT NULL,
curremote varchar(50) NOT NULL,
winopts json NOT NULL,
PRIMARY KEY (sessionid, windowid)
);
CREATE UNIQUE INDEX window_name_unique ON window(sessionid, name);
CREATE TABLE screen (
sessionid varchar(36) NOT NULL,
screenid varchar(36) NOT NULL,
name varchar(50) NOT NULL,
activewindowid varchar(36) NOT NULL,
screenidx int NOT NULL,
screenopts json NOT NULL,
PRIMARY KEY (sessionid, screenid)
);
@ -28,6 +28,7 @@ CREATE TABLE screen_window (
sessionid varchar(36) NOT NULL,
screenid varchar(36) NOT NULL,
windowid varchar(36) NOT NULL,
name varchar(50) NOT NULL,
layout json NOT NULL,
PRIMARY KEY (sessionid, screenid, windowid)
);

View File

@ -4,6 +4,7 @@ import (
"context"
"database/sql"
"fmt"
"strings"
"github.com/google/uuid"
"github.com/scripthaus-dev/mshell/pkg/packet"
@ -101,6 +102,32 @@ func GetAllSessions(ctx context.Context) ([]*SessionType, error) {
for _, session := range rtn {
session.Windows = winMap[session.SessionId]
}
var screens []*ScreenType
query = `SELECT * FROM screen ORDER BY screenidx`
tx.SelectWrap(&screens, query)
screenMap := make(map[string][]*ScreenType)
for _, screen := range screens {
screenArr := screenMap[screen.SessionId]
screenArr = append(screenArr, screen)
screenMap[screen.SessionId] = screenArr
}
for _, session := range rtn {
session.Screens = screenMap[session.SessionId]
}
var sws []*ScreenWindowType
query = `SELECT * FROM screen_window`
tx.SelectWrap(&sws, query)
screenIdMap := make(map[string]*ScreenType)
for _, screen := range screens {
screenIdMap[screen.SessionId+screen.ScreenId] = screen
}
for _, sw := range sws {
screen := screenIdMap[sw.SessionId+sw.ScreenId]
if screen == nil {
continue
}
screen.Windows = append(screen.Windows, sw)
}
return nil
})
return rtn, err
@ -177,43 +204,17 @@ func GetSessionByName(ctx context.Context, name string) (*SessionType, error) {
func InsertSessionWithName(ctx context.Context, sessionName string) (string, error) {
newSessionId := uuid.New().String()
txErr := WithTx(ctx, func(tx *TxWrap) error {
if sessionName == "" {
var names []string
query := `SELECT name FROM session`
tx.GetWrap(&names, query)
snum := len(names) + 1
for {
sessionName = fmt.Sprintf("session-%d", snum)
if !containsStr(names, sessionName) {
break
}
snum++
}
} else {
var dupSessionId string
query := `SELECT sessionid FROM session WHERE name = ?`
tx.GetWrap(&dupSessionId, query, sessionName)
if dupSessionId != "" {
return fmt.Errorf("cannot create session with duplicate name")
}
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`)
query := `INSERT INTO session (sessionid, name, activescreenid, sessionidx, notifynum) VALUES (?, ?, '', ?, ?)`
tx.ExecWrap(query, newSessionId, sessionName, maxSessionIdx+1, 0)
screenId, err := InsertScreen(tx.Context(), newSessionId, "")
if err != nil {
return err
}
var maxSessionIdx int64
query := `SELECT COALESCE(max(sessionidx), 0) FROM session`
tx.GetWrap(&maxSessionIdx, query)
newSession := &SessionType{
SessionId: newSessionId,
Name: sessionName,
SessionIdx: maxSessionIdx + 1,
}
query = `INSERT INTO session (sessionid, name, sessionidx, notifynum) VALUES (:sessionid, :name, :sessionidx, :notifynum)`
tx.NamedExecWrap(query, newSession)
window := &WindowType{
SessionId: newSessionId,
WindowId: uuid.New().String(),
Name: DefaultWindowName,
CurRemote: LocalRemoteName,
}
txInsertWindow(tx, window)
query = `UPDATE session SET activescreenid = ? WHERE sessionid = ?`
tx.ExecWrap(query, screenId, newSessionId)
return nil
})
return newSessionId, txErr
@ -228,61 +229,57 @@ func containsStr(strs []string, testStr string) bool {
return false
}
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
}
}
func InsertScreen(ctx context.Context, sessionId string, screenName string) (string, error) {
var newScreenId string
txErr := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT sessionid FROM session WHERE sessionid = ?`
if !tx.Exists(query, sessionId) {
return fmt.Errorf("cannot create screen, no session found")
}
newWindowId := txCreateWindow(tx, sessionId)
maxScreenIdx := tx.GetInt(`SELECT COALESCE(max(screenidx), 0) FROM screen WHERE sessionid = ?`, sessionId)
screenNames := tx.SelectStrings(`SELECT name FROM screen WHERE sessionid = ?`, sessionId)
screenName = fmtUniqueName(screenName, "s%d", maxScreenIdx+1, screenNames)
newScreenId = uuid.New().String()
query = `INSERT INTO screen (sessionid, screenid, name, activewindowid, screenidx, screenopts) VALUES (?, ?, ?, ?, ?, ?)`
tx.ExecWrap(query, sessionId, newScreenId, screenName, newWindowId, maxScreenIdx+1, ScreenOptsType{})
layout := LayoutType{Type: LayoutFull}
query = `INSERT INTO screen_window (sessionid, screenid, windowid, name, layout) VALUES (?, ?, ?, ?, ?)`
tx.ExecWrap(query, sessionId, newScreenId, newWindowId, DefaultScreenWindowName, layout)
return nil
})
return newScreenId, txErr
}
// if windowName == "", it will be generated
// returns (windowid, err)
func InsertWindow(ctx context.Context, sessionId string, windowName string) (string, error) {
var newWindowId string
txErr := WithTx(ctx, func(tx *TxWrap) error {
var testSessionId string
query := `SELECT sesssionid FROM session WHERE sessionid = ?`
sessionExists := tx.GetWrap(&testSessionId, query, sessionId)
if !sessionExists {
return fmt.Errorf("cannot insert window, session does not exist")
}
if windowName == "" {
var names []string
query = `SELECT name FROM window WHERE sessionid = ?`
tx.GetWrap(&names, query, sessionId)
wnum := len(names) + 1
for {
windowName = fmt.Sprintf("w%d", wnum)
if !containsStr(names, windowName) {
break
}
wnum++
}
} else {
var testWindowId string
query = `SELECT windowid FROM window WHERE sessionid = ? AND name = ?`
windowExists := tx.GetWrap(&testWindowId, query, sessionId, windowName)
if windowExists {
return fmt.Errorf("cannot insert window, name already exists in session")
}
}
newWindowId = uuid.New().String()
window := &WindowType{
SessionId: sessionId,
WindowId: newWindowId,
Name: windowName,
CurRemote: LocalRemoteName,
}
txInsertWindow(tx, window)
return nil
})
return newWindowId, txErr
}
func txInsertWindow(tx *TxWrap, window *WindowType) {
query := `INSERT INTO window (sessionid, windowid, name, curremote, winopts) VALUES (:sessionid, :windowid, :name, :curremote, :winopts)`
tx.NamedExecWrap(query, window)
func txCreateWindow(tx *TxWrap, sessionId string) string {
windowId := uuid.New().String()
query := `INSERT INTO window (sessionid, windowid, curremote, winopts) VALUES (?, ?, ?, ?)`
tx.ExecWrap(query, sessionId, windowId, LocalRemoteName, WindowOptsType{})
return windowId
}
func InsertLine(ctx context.Context, line *LineType, cmd *CmdType) error {

View File

@ -25,6 +25,7 @@ const DBFileName = "sh2.db"
const DefaultSessionName = "default"
const DefaultWindowName = "default"
const LocalRemoteName = "local"
const DefaultScreenWindowName = "w1"
const DefaultCwd = "~"
@ -56,13 +57,15 @@ func GetDB(ctx context.Context) (*sqlx.DB, error) {
}
type SessionType struct {
SessionId string `json:"sessionid"`
Name string `json:"name"`
SessionIdx int64 `json:"sessionidx"`
NotifyNum int64 `json:"notifynum"`
Windows []*WindowType `json:"windows"`
Cmds []*CmdType `json:"cmds"`
Remotes []*RemoteInstance `json:"remotes"`
SessionId string `json:"sessionid"`
Name string `json:"name"`
SessionIdx int64 `json:"sessionidx"`
ActiveScreenId string `json:"activescreenid"`
NotifyNum int64 `json:"notifynum"`
Screens []*ScreenType `json:"screens"`
Windows []*WindowType `json:"windows"`
Cmds []*CmdType `json:"cmds"`
Remotes []*RemoteInstance `json:"remotes"`
}
type WindowOptsType struct {
@ -79,31 +82,51 @@ func (opts WindowOptsType) Value() (driver.Value, error) {
type WindowType struct {
SessionId string `json:"sessionid"`
WindowId string `json:"windowid"`
Name string `json:"name"`
CurRemote string `json:"curremote"`
WinOpts WindowOptsType `json:"winopts"`
Lines []*LineType `json:"lines"`
Cmds []*CmdType `json:"cmds"`
History []*HistoryItemType `json:"history"`
Remotes []*RemoteInstance `json:"remotes"`
WinOpts WindowOptsType `json:"winopts"`
}
type ScreenOptsType struct {
TabColor string `json:"tabcolor"`
}
func (opts *ScreenOptsType) Scan(val interface{}) error {
return quickScanJson(opts, val)
}
func (opts ScreenOptsType) Value() (driver.Value, error) {
return quickValueJson(opts)
}
type ScreenType struct {
SessionId string `json:"sessionid"`
ScreenId string `json:"screenid"`
ScreenIdx int64 `json:"screenidx"`
Name string `json:"name"`
SessionId string `json:"sessionid"`
ScreenId string `json:"screenid"`
ScreenIdx int64 `json:"screenidx"`
Name string `json:"name"`
ActiveWindowId string `json:"activewindowid"`
ScreenOpts ScreenOptsType `json:"screenopts"`
Windows []*ScreenWindowType `json:"windows"`
}
const (
LayoutFull = "full"
)
type LayoutType struct {
ZIndex int64 `json:"zindex"`
Float bool `json:"float"`
Top string `json:"top"`
Bottom string `json:"bottom"`
Left string `json:"left"`
Right string `json:"right"`
Width string `json:"width"`
Height string `json:"height"`
Type string `json:"type"`
Parent string `json:"parent,omitempty"`
ZIndex int64 `json:"zindex,omitempty"`
Float bool `json:"float,omitempty"`
Top string `json:"top,omitempty"`
Bottom string `json:"bottom,omitempty"`
Left string `json:"left,omitempty"`
Right string `json:"right,omitempty"`
Width string `json:"width,omitempty"`
Height string `json:"height,omitempty"`
}
func (l *LayoutType) Scan(val interface{}) error {
@ -118,6 +141,7 @@ type ScreenWindowType struct {
SessionId string `json:"sessionid"`
ScreenId string `json:"screenid"`
WindowId string `json:"windowid"`
Name string `json:"name"`
Layout LayoutType `json:"layout"`
}

View File

@ -25,6 +25,9 @@ func WithTx(ctx context.Context, fn func(tx *TxWrap) error) (rtnErr error) {
ctxVal := ctx.Value(txWrapKey{})
if ctxVal != nil {
txWrap = ctxVal.(*TxWrap)
if txWrap.Err != nil {
return txWrap.Err
}
}
if txWrap == nil {
db, err := GetDB(ctx)
@ -84,6 +87,29 @@ func (tx *TxWrap) ExecWrap(query string, args ...interface{}) sql.Result {
return result
}
func (tx *TxWrap) Exists(query string, args ...interface{}) bool {
var dest interface{}
return tx.GetWrap(&dest, query, args...)
}
func (tx *TxWrap) GetString(query string, args ...interface{}) string {
var rtnStr string
tx.GetWrap(&rtnStr, query, args...)
return rtnStr
}
func (tx *TxWrap) SelectStrings(query string, args ...interface{}) []string {
var rtnArr []string
tx.SelectWrap(&rtnArr, query, args...)
return rtnArr
}
func (tx *TxWrap) GetInt(query string, args ...interface{}) int {
var rtnInt int
tx.GetWrap(&rtnInt, query, args...)
return rtnInt
}
func (tx *TxWrap) GetWrap(dest interface{}, query string, args ...interface{}) bool {
if tx.Err != nil {
return false