session.go

 1package shell
 2
 3import (
 4	"strings"
 5
 6	tea "github.com/charmbracelet/bubbletea"
 7	"github.com/charmbracelet/soft-serve/pkg/access"
 8	"github.com/charmbracelet/soft-serve/pkg/backend"
 9	"github.com/charmbracelet/soft-serve/pkg/config"
10	"github.com/charmbracelet/soft-serve/pkg/proto"
11	"github.com/charmbracelet/soft-serve/pkg/ui/common"
12	"github.com/charmbracelet/ssh"
13	"github.com/charmbracelet/wish"
14	"github.com/muesli/termenv"
15)
16
17// var tuiSessionCounter = promauto.NewCounterVec(prometheus.CounterOpts{
18// 	Namespace: "soft_serve",
19// 	Subsystem: "ssh",
20// 	Name:      "tui_session_total",
21// 	Help:      "The total number of TUI sessions",
22// }, []string{"repo", "term"})
23//
24// var tuiSessionDuration = promauto.NewCounterVec(prometheus.CounterOpts{
25// 	Namespace: "soft_serve",
26// 	Subsystem: "ssh",
27// 	Name:      "tui_session_seconds_total",
28// 	Help:      "The total number of TUI sessions",
29// }, []string{"repo", "term"})
30
31// SessionHandler is the soft-serve bubbletea ssh session handler.
32// This middleware must be run after the ContextMiddleware.
33func SessionHandler(s ssh.Session) *tea.Program {
34	pty, _, active := s.Pty()
35	if !active {
36		return nil
37	}
38
39	ctx := s.Context()
40	be := backend.FromContext(ctx)
41	cfg := config.FromContext(ctx)
42	cmd := s.Command()
43	initialRepo := ""
44	if len(cmd) == 1 {
45		initialRepo = cmd[0]
46		auth := be.AccessLevelByPublicKey(ctx, initialRepo, s.PublicKey())
47		if auth < access.ReadOnlyAccess {
48			wish.Fatalln(s, proto.ErrUnauthorized)
49			return nil
50		}
51	}
52
53	envs := &sessionEnv{s}
54	output := termenv.NewOutput(s, termenv.WithColorCache(true), termenv.WithEnvironment(envs))
55	c := common.NewCommon(ctx, output, pty.Window.Width, pty.Window.Height)
56	c.SetValue(common.ConfigKey, cfg)
57	m := NewUI(c, initialRepo)
58	p := tea.NewProgram(m,
59		tea.WithInput(s),
60		tea.WithOutput(s),
61		tea.WithAltScreen(),
62		tea.WithoutCatchPanics(),
63		tea.WithMouseCellMotion(),
64		tea.WithContext(ctx),
65	)
66
67	// tuiSessionCounter.WithLabelValues(initialRepo, pty.Term).Inc()
68	//
69	// start := time.Now()
70	// go func() {
71	// 	<-ctx.Done()
72	// 	tuiSessionDuration.WithLabelValues(initialRepo, pty.Term).Add(time.Since(start).Seconds())
73	// }()
74
75	return p
76}
77
78var _ termenv.Environ = &sessionEnv{}
79
80type sessionEnv struct {
81	ssh.Session
82}
83
84func (s *sessionEnv) Environ() []string {
85	pty, _, _ := s.Pty()
86	return append(s.Session.Environ(), "TERM="+pty.Term)
87}
88
89func (s *sessionEnv) Getenv(key string) string {
90	for _, env := range s.Environ() {
91		if strings.HasPrefix(env, key+"=") {
92			return strings.TrimPrefix(env, key+"=")
93		}
94	}
95	return ""
96}