checkpoint

This commit is contained in:
sawka 2022-07-14 18:39:40 -07:00
parent c1ace6f5d6
commit 275e177218
5 changed files with 179 additions and 52 deletions

View File

@ -9,6 +9,7 @@ import (
"io/fs"
"net/http"
"os"
"strconv"
"strings"
"sync"
"time"
@ -190,6 +191,30 @@ func HandleCreateWindow(w http.ResponseWriter, r *http.Request) {
return
}
// params: sessionid, name, activate
func HandleCreateScreen(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin"))
w.Header().Set("Access-Control-Allow-Credentials", "true")
w.Header().Set("Vary", "Origin")
w.Header().Set("Cache-Control", "no-cache")
qvals := r.URL.Query()
sessionId := qvals.Get("sessionid")
if _, err := uuid.Parse(sessionId); err != nil {
WriteJsonError(w, fmt.Errorf("invalid sessionid: %w", err))
return
}
name := qvals.Get("name")
activateStr := qvals.Get("activate")
activate := activateStr != ""
screenId, err := sstore.InsertScreen(r.Context(), sessionId, name, activate)
if err != nil {
WriteJsonError(w, fmt.Errorf("inserting screen: %w", err))
return
}
WriteJsonSuccess(w, screenId)
return
}
// params: [none]
func HandleGetRemotes(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin"))
@ -339,20 +364,45 @@ func HandleRunCommand(w http.ResponseWriter, r *http.Request) {
}
func ProcessFeCommandPacket(ctx context.Context, pk *scpacket.FeCommandPacketType) (*runCommandResponse, error) {
// parse metacmd
commandStr := strings.TrimSpace(pk.CmdStr)
if commandStr == "" {
return nil, fmt.Errorf("invalid emtpty command")
}
if strings.HasPrefix(commandStr, "/comment ") {
text := strings.TrimSpace(commandStr[9:])
metaCmd := ""
metaSubCmd := ""
if commandStr == "cd" || strings.HasPrefix(commandStr, "cd ") {
metaCmd = "cd"
commandStr = strings.TrimSpace(commandStr[2:])
} else if commandStr[0] == '/' {
spaceIdx := strings.Index(commandStr, " ")
if spaceIdx == -1 {
metaCmd = commandStr[1:]
commandStr = ""
} else {
metaCmd = commandStr[1:spaceIdx]
commandStr = strings.TrimSpace(commandStr[spaceIdx+1:])
}
colonIdx := strings.Index(metaCmd, ":")
if colonIdx != -1 {
metaCmd = metaCmd[0:colonIdx]
metaSubCmd = metaCmd[colonIdx+1:]
}
if metaCmd == "" {
return nil, fmt.Errorf("invalid command, got bare '/', with no command")
}
}
// execute metacmd
if metaCmd == "comment" {
text := commandStr
rtnLine, err := sstore.AddCommentLine(ctx, pk.SessionId, pk.WindowId, pk.UserId, text)
if err != nil {
return nil, err
}
return &runCommandResponse{Line: rtnLine}, nil
}
if strings.HasPrefix(commandStr, "cd ") {
newDir := strings.TrimSpace(commandStr[3:])
} else if metaCmd == "cd" {
newDir := commandStr
cdPacket := packet.MakeCdPacket()
cdPacket.ReqId = uuid.New().String()
cdPacket.Dir = newDir
@ -366,17 +416,53 @@ func ProcessFeCommandPacket(ctx context.Context, pk *scpacket.FeCommandPacketTyp
}
fmt.Printf("GOT cd RESP: %v\n", resp)
return nil, nil
} else if metaCmd == "s" || metaCmd == "screen" {
err := RunScreenCmd(ctx, pk.SessionId, pk.ScreenId, metaSubCmd, commandStr)
if err != nil {
return nil, err
}
return nil, nil
} else if metaCmd == "" {
cmdId := uuid.New().String()
cmd, err := remote.RunCommand(ctx, pk, cmdId)
if err != nil {
return nil, err
}
rtnLine, err := sstore.AddCmdLine(ctx, pk.SessionId, pk.WindowId, pk.UserId, cmd)
if err != nil {
return nil, err
}
return &runCommandResponse{Line: rtnLine, Cmd: cmd}, nil
} else {
fmt.Printf("INVALID metacmd '%s'\n", metaCmd)
return nil, fmt.Errorf("Invalid command type /%s", metaCmd)
}
cmdId := uuid.New().String()
cmd, err := remote.RunCommand(ctx, pk, cmdId)
}
func RunScreenCmd(ctx context.Context, sessionId string, screenId string, subCmd string, commandStr string) error {
if subCmd != "" {
return fmt.Errorf("invalid /screen subcommand '%s'", subCmd)
}
if commandStr == "" {
return fmt.Errorf("usage /screen [screen-name|screen-index|screen-id], no param specified")
}
screens, err := sstore.GetSessionScreens(ctx, sessionId)
if err != nil {
return nil, err
return fmt.Errorf("could not retreive screens for session=%s", sessionId)
}
rtnLine, err := sstore.AddCmdLine(ctx, pk.SessionId, pk.WindowId, pk.UserId, cmd)
if err != nil {
return nil, err
screenNum, err := strconv.Atoi(commandStr)
if err == nil {
if screenNum < 1 || screenNum > len(screens) {
return fmt.Errorf("could not switch to screen #%d (out of range), valid screens 1-%d", screenNum, len(screens))
}
return sstore.SwitchScreenById(ctx, sessionId, screens[screenNum-1].ScreenId)
}
return &runCommandResponse{Line: rtnLine, Cmd: cmd}, nil
for _, screen := range screens {
if screen.Name == commandStr {
return sstore.SwitchScreenById(ctx, sessionId, screen.ScreenId)
}
}
return fmt.Errorf("could not switch to screen '%s' (name not found)", commandStr)
}
// /api/start-session
@ -384,12 +470,6 @@ func ProcessFeCommandPacket(ctx context.Context, pk *scpacket.FeCommandPacketTyp
// * userid
// * sessionid
//
// /api/get-session
// params:
// * name
// returns:
// * session
//
// /api/ptyout (pos=[position]) - returns contents of ptyout file
// params:
// * sessionid
@ -525,10 +605,10 @@ func main() {
gr.HandleFunc("/api/ptyout", HandleGetPtyOut)
gr.HandleFunc("/api/get-all-sessions", HandleGetAllSessions)
gr.HandleFunc("/api/create-session", HandleCreateSession)
gr.HandleFunc("/api/get-session", HandleGetSession)
gr.HandleFunc("/api/get-window", HandleGetWindow)
gr.HandleFunc("/api/get-remotes", HandleGetRemotes)
gr.HandleFunc("/api/create-window", HandleCreateWindow)
gr.HandleFunc("/api/create-screen", HandleCreateScreen)
gr.HandleFunc("/api/run-command", HandleRunCommand).Methods("GET", "POST", "OPTIONS")
server := &http.Server{
Addr: MainServerAddr,

View File

@ -18,6 +18,7 @@ type RemoteState struct {
type FeCommandPacketType struct {
Type string `json:"type"`
SessionId string `json:"sessionid"`
ScreenId string `json:"screenid"`
WindowId string `json:"windowid"`
UserId string `json:"userid"`
CmdStr string `json:"cmdstr"`

View File

@ -90,18 +90,6 @@ func GetAllSessions(ctx context.Context) ([]*SessionType, error) {
err := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT * FROM session`
tx.SelectWrap(&rtn, query)
var windows []*WindowType
query = `SELECT * FROM window`
tx.SelectWrap(&windows, query)
winMap := make(map[string][]*WindowType)
for _, win := range windows {
winArr := winMap[win.SessionId]
winArr = append(winArr, win)
winMap[win.SessionId] = winArr
}
for _, session := range rtn {
session.Windows = winMap[session.SessionId]
}
var screens []*ScreenType
query = `SELECT * FROM screen ORDER BY screenidx`
tx.SelectWrap(&screens, query)
@ -155,6 +143,16 @@ func GetWindowById(ctx context.Context, sessionId string, windowId string) (*Win
return rtnWindow, err
}
func GetSessionScreens(ctx context.Context, sessionId string) ([]*ScreenType, error) {
var rtn []*ScreenType
txErr := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT * FROM screen WHERE sessionid = ? ORDER BY screenidx`
tx.SelectWrap(&rtn, query, sessionId)
return nil
})
return rtn, txErr
}
func GetSessionById(ctx context.Context, id string) (*SessionType, error) {
var rtnSession *SessionType
err := WithTx(ctx, func(tx *TxWrap) error {
@ -165,15 +163,10 @@ func GetSessionById(ctx context.Context, id string) (*SessionType, error) {
return nil
}
rtnSession = &session
query = `SELECT * FROM window WHERE sessionid = ?`
tx.SelectWrap(&session.Windows, query, session.SessionId)
query = `SELECT * FROM screen WHERE sessionid = ? ORDER BY screenidx`
tx.SelectWrap(&session.Screens, query, session.SessionId)
query = `SELECT * FROM remote_instance WHERE sessionid = ?`
tx.SelectWrap(&session.Remotes, query, session.SessionId)
query = `SELECT * FROM cmd WHERE sessionid = ?`
marr := tx.SelectMaps(query, session.SessionId)
for _, m := range marr {
session.Cmds = append(session.Cmds, CmdFromMap(m))
}
return nil
})
if err != nil {
@ -209,15 +202,16 @@ func InsertSessionWithName(ctx context.Context, sessionName string) (string, err
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, "")
_, err := InsertScreen(tx.Context(), newSessionId, "", true)
if err != nil {
return err
}
query = `UPDATE session SET activescreenid = ? WHERE sessionid = ?`
tx.ExecWrap(query, screenId, newSessionId)
return nil
})
return newSessionId, txErr
if txErr != nil {
return "", txErr
}
return newSessionId, nil
}
func containsStr(strs []string, testStr string) bool {
@ -253,7 +247,7 @@ func fmtUniqueName(name string, defaultFmtStr string, startIdx int, strs []strin
}
}
func InsertScreen(ctx context.Context, sessionId string, screenName string) (string, error) {
func InsertScreen(ctx context.Context, sessionId string, screenName string, activate bool) (string, error) {
var newScreenId string
txErr := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT sessionid FROM session WHERE sessionid = ?`
@ -270,11 +264,35 @@ func InsertScreen(ctx context.Context, sessionId string, screenName string) (str
layout := LayoutType{Type: LayoutFull}
query = `INSERT INTO screen_window (sessionid, screenid, windowid, name, layout) VALUES (?, ?, ?, ?, ?)`
tx.ExecWrap(query, sessionId, newScreenId, newWindowId, DefaultScreenWindowName, layout)
if activate {
query = `UPDATE session SET activescreenid = ? WHERE sessionid = ?`
tx.ExecWrap(query, newScreenId, sessionId)
}
return nil
})
return newScreenId, txErr
}
func GetScreenById(ctx context.Context, sessionId string, screenId string) (*ScreenType, error) {
var rtnScreen *ScreenType
txErr := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT * FROM screen WHERE sessionid = ? AND screenid = ?`
var screen ScreenType
found := tx.GetWrap(&screen, query, sessionId, screenId)
if !found {
return nil
}
rtnScreen = &screen
query = `SELECT * FROM screen_window WHERE sessionid = ? AND screenid = ?`
tx.SelectWrap(&screen.Windows, query, sessionId, screenId)
return nil
})
if txErr != nil {
return nil, txErr
}
return rtnScreen, nil
}
func txCreateWindow(tx *TxWrap, sessionId string) string {
windowId := uuid.New().String()
query := `INSERT INTO window (sessionid, windowid, curremote, winopts) VALUES (?, ?, ?, ?)`
@ -366,3 +384,25 @@ func HangupRunningCmdsByRemoteId(ctx context.Context, remoteId string) error {
return nil
})
}
func SwitchScreenById(ctx context.Context, sessionId string, screenId 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("cannot switch to screen, screen=%s does not exist in session=%s", screenId, sessionId)
}
query = `UPDATE session SET activescreenid = ? WHERE sessionid = ?`
tx.ExecWrap(query, screenId, sessionId)
return nil
})
sessionUpdate := SessionType{
SessionId: sessionId,
ActiveScreenId: screenId,
NotifyNum: -1,
}
update := &SessionUpdate{
Sessions: []SessionType{sessionUpdate},
}
MainBus.SendUpdate("", update)
return txErr
}

View File

@ -63,9 +63,11 @@ type SessionType struct {
ActiveScreenId string `json:"activescreenid"`
NotifyNum int64 `json:"notifynum"`
Screens []*ScreenType `json:"screens"`
Windows []*WindowType `json:"windows"`
Cmds []*CmdType `json:"cmds"`
Remotes []*RemoteInstance `json:"remotes"`
// only for updates
Remove bool `json:"remove,omitempty"`
Full bool `json:"full,omitempty"`
}
type WindowOptsType struct {
@ -88,6 +90,9 @@ type WindowType struct {
Cmds []*CmdType `json:"cmds"`
History []*HistoryItemType `json:"history"`
Remotes []*RemoteInstance `json:"remotes"`
// only for updates
Remove bool `json:"remove,omitempty"`
}
type ScreenOptsType struct {
@ -110,6 +115,10 @@ type ScreenType struct {
ActiveWindowId string `json:"activewindowid"`
ScreenOpts ScreenOptsType `json:"screenopts"`
Windows []*ScreenWindowType `json:"windows"`
// only for updates
Remove bool `json:"remove,omitempty"`
Full bool `json:"full,omitempty"`
}
const (
@ -143,6 +152,9 @@ type ScreenWindowType struct {
WindowId string `json:"windowid"`
Name string `json:"name"`
Layout LayoutType `json:"layout"`
// only for updates
Remove bool `json:"remove,omitempty"`
}
type HistoryItemType struct {

View File

@ -23,8 +23,7 @@ type WindowUpdate struct {
}
type SessionUpdate struct {
Session SessionType `json:"session"`
Remove bool `json:"remove,omitempty"`
Sessions []SessionType `json:"sessions"`
}
type CmdUpdate struct {
@ -32,11 +31,6 @@ type CmdUpdate struct {
Remove bool `json:"remove,omitempty"`
}
type ScreenUpdate struct {
Screen CmdType `json:"screen"`
Remove bool `json:"remove,omitempty"`
}
type UpdateChannel struct {
SessionId string
ClientId string