mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-08 19:38:51 +01:00
checkpoint, adding 'screen' concept to contain windows
This commit is contained in:
parent
11087c10be
commit
db841f2951
@ -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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
);
|
||||
|
@ -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 {
|
||||
|
@ -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"`
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user