fix session with split screens
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
package internals
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// credsMu guards concurrent reads/writes of appCreds.Workspaces from HTTP
|
||||
// handlers. Credential fields (Username/Hash/etc.) are only written at
|
||||
// startup via CLI flags that call os.Exit, so they don't need the lock.
|
||||
var credsMu sync.Mutex
|
||||
|
||||
// WorkspacePaneLayout is a recursive pane tree.
|
||||
// Leaf nodes own a PTY session; split nodes contain two children.
|
||||
type WorkspacePaneLayout struct {
|
||||
Type string `json:"type"` // "leaf" | "split"
|
||||
ID string `json:"id,omitempty"` // PTY session hex ID (leaf only)
|
||||
Dir string `json:"dir,omitempty"` // "h" | "v" (split only)
|
||||
Ratio float64 `json:"ratio,omitempty"` // 0.1–0.9 (split only)
|
||||
A *WorkspacePaneLayout `json:"a,omitempty"`
|
||||
B *WorkspacePaneLayout `json:"b,omitempty"`
|
||||
}
|
||||
|
||||
// WorkspaceTabLayout stores one tab's label and pane tree.
|
||||
type WorkspaceTabLayout struct {
|
||||
TabID string `json:"tab_id"`
|
||||
Label string `json:"label"`
|
||||
Root WorkspacePaneLayout `json:"root"`
|
||||
}
|
||||
|
||||
// WorkspaceLayout is the full layout stored per workspace ID in gws-creds.json.
|
||||
type WorkspaceLayout struct {
|
||||
ID string `json:"id"`
|
||||
Tabs []WorkspaceTabLayout `json:"tabs"`
|
||||
}
|
||||
|
||||
// handleWorkspaceAPI dispatches GET / PUT / DELETE for /api/workspace/<id>.
|
||||
// All methods require auth.
|
||||
func handleWorkspaceAPI(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if !isAuthed(r) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.Write([]byte(`{"error":"unauthorized"}`))
|
||||
return
|
||||
}
|
||||
id := strings.TrimPrefix(r.URL.Path, "/api/workspace/")
|
||||
if !validID(id) {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(`{"error":"invalid id"}`))
|
||||
return
|
||||
}
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
workspaceGet(w, id)
|
||||
case http.MethodPut:
|
||||
workspacePut(w, r, id)
|
||||
case http.MethodDelete:
|
||||
workspaceDelete(w, id)
|
||||
default:
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
w.Write([]byte(`{"error":"method not allowed"}`))
|
||||
}
|
||||
}
|
||||
|
||||
func workspaceGet(w http.ResponseWriter, id string) {
|
||||
credsMu.Lock()
|
||||
ws := appCreds.Workspaces[id]
|
||||
credsMu.Unlock()
|
||||
if ws == nil {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Write([]byte(`{"error":"not found"}`))
|
||||
return
|
||||
}
|
||||
json.NewEncoder(w).Encode(ws) //nolint:errcheck
|
||||
}
|
||||
|
||||
func workspacePut(w http.ResponseWriter, r *http.Request, id string) {
|
||||
var ws WorkspaceLayout
|
||||
if err := json.NewDecoder(r.Body).Decode(&ws); err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(`{"error":"bad json"}`))
|
||||
return
|
||||
}
|
||||
ws.ID = id
|
||||
|
||||
credsMu.Lock()
|
||||
if appCreds.Workspaces == nil {
|
||||
appCreds.Workspaces = make(map[string]*WorkspaceLayout)
|
||||
}
|
||||
appCreds.Workspaces[id] = &ws
|
||||
err := saveCreds(appCreds)
|
||||
credsMu.Unlock()
|
||||
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(`{"error":"save failed"}`))
|
||||
return
|
||||
}
|
||||
w.Write([]byte(`{"ok":true}`))
|
||||
}
|
||||
|
||||
func workspaceDelete(w http.ResponseWriter, id string) {
|
||||
credsMu.Lock()
|
||||
if appCreds.Workspaces != nil {
|
||||
delete(appCreds.Workspaces, id)
|
||||
}
|
||||
err := saveCreds(appCreds)
|
||||
credsMu.Unlock()
|
||||
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(`{"error":"save failed"}`))
|
||||
return
|
||||
}
|
||||
w.Write([]byte(`{"ok":true}`))
|
||||
}
|
||||
Reference in New Issue
Block a user