feat: add supervisor prototype with embedded frontend
This commit is contained in:
158
internal/session/manager.go
Normal file
158
internal/session/manager.go
Normal file
@ -0,0 +1,158 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"supervisor/internal/domain"
|
||||
"supervisor/internal/store"
|
||||
"supervisor/internal/util"
|
||||
)
|
||||
|
||||
var ErrSessionNotFound = errors.New("session not found")
|
||||
var ErrSessionRunning = errors.New("session is running")
|
||||
|
||||
type Manager struct {
|
||||
mu sync.RWMutex
|
||||
store store.SessionStore
|
||||
factory PTYFactory
|
||||
runtimes map[string]*Session
|
||||
scrollbackLimit int
|
||||
}
|
||||
|
||||
func NewManager(store store.SessionStore, factory PTYFactory) *Manager {
|
||||
if factory == nil {
|
||||
factory = DefaultPTYFactory{}
|
||||
}
|
||||
return &Manager{
|
||||
store: store,
|
||||
factory: factory,
|
||||
runtimes: make(map[string]*Session),
|
||||
scrollbackLimit: 512 * 1024,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager) CreateSession(ctx context.Context, params CreateSessionParams) (domain.Session, error) {
|
||||
command := params.Command
|
||||
if command == "" {
|
||||
command = "bash"
|
||||
}
|
||||
s := domain.Session{
|
||||
ID: util.NewID("sess"),
|
||||
Name: params.Name,
|
||||
AgentID: params.AgentID,
|
||||
Command: command,
|
||||
Status: domain.SessionStatusCreated,
|
||||
CreatedAt: time.Now().UTC(),
|
||||
}
|
||||
if s.Name == "" {
|
||||
s.Name = s.ID
|
||||
}
|
||||
|
||||
runtime := NewSession(s, m.scrollbackLimit, func(session domain.Session) {
|
||||
_ = m.store.Upsert(context.Background(), session)
|
||||
})
|
||||
|
||||
m.mu.Lock()
|
||||
m.runtimes[s.ID] = runtime
|
||||
m.mu.Unlock()
|
||||
|
||||
if err := m.store.Upsert(ctx, s); err != nil {
|
||||
return domain.Session{}, err
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (m *Manager) StartSession(_ context.Context, id string) error {
|
||||
runtime, err := m.runtimeByID(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return runtime.Start(m.factory)
|
||||
}
|
||||
|
||||
func (m *Manager) StopSession(_ context.Context, id string) error {
|
||||
runtime, err := m.runtimeByID(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return runtime.Stop()
|
||||
}
|
||||
|
||||
func (m *Manager) ListSessions(ctx context.Context) ([]domain.Session, error) {
|
||||
return m.store.List(ctx)
|
||||
}
|
||||
|
||||
func (m *Manager) GetSession(ctx context.Context, id string) (domain.Session, error) {
|
||||
s, ok, err := m.store.Get(ctx, id)
|
||||
if err != nil {
|
||||
return domain.Session{}, err
|
||||
}
|
||||
if !ok {
|
||||
return domain.Session{}, ErrSessionNotFound
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (m *Manager) WriteInput(_ context.Context, id string, input string) error {
|
||||
runtime, err := m.runtimeByID(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return runtime.WriteInput(input)
|
||||
}
|
||||
|
||||
func (m *Manager) Subscribe(id string) (<-chan domain.Event, func(), error) {
|
||||
runtime, err := m.runtimeByID(id)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
_, ch, cancel := runtime.Subscribe()
|
||||
return ch, cancel, nil
|
||||
}
|
||||
|
||||
func (m *Manager) Resize(_ context.Context, id string, cols, rows int) error {
|
||||
runtime, err := m.runtimeByID(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return runtime.Resize(cols, rows)
|
||||
}
|
||||
|
||||
func (m *Manager) Scrollback(id string) ([]byte, error) {
|
||||
runtime, err := m.runtimeByID(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return runtime.Scrollback(), nil
|
||||
}
|
||||
|
||||
func (m *Manager) DeleteSession(ctx context.Context, id string) error {
|
||||
runtime, err := m.runtimeByID(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
snapshot := runtime.Snapshot()
|
||||
if snapshot.Status == domain.SessionStatusRunning {
|
||||
return ErrSessionRunning
|
||||
}
|
||||
|
||||
m.mu.Lock()
|
||||
delete(m.runtimes, id)
|
||||
m.mu.Unlock()
|
||||
|
||||
return m.store.Delete(ctx, id)
|
||||
}
|
||||
|
||||
func (m *Manager) runtimeByID(id string) (*Session, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
runtime, ok := m.runtimes[id]
|
||||
if !ok {
|
||||
return nil, ErrSessionNotFound
|
||||
}
|
||||
return runtime, nil
|
||||
}
|
||||
Reference in New Issue
Block a user