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}