session.go

 1package ssh
 2
 3import (
 4	"time"
 5
 6	tea "github.com/charmbracelet/bubbletea/v2"
 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	wish "github.com/charmbracelet/wish/v2"
14	bm "github.com/charmbracelet/wish/v2/bubbletea"
15	"github.com/prometheus/client_golang/prometheus"
16	"github.com/prometheus/client_golang/prometheus/promauto"
17)
18
19var tuiSessionCounter = promauto.NewCounterVec(prometheus.CounterOpts{
20	Namespace: "soft_serve",
21	Subsystem: "ssh",
22	Name:      "tui_session_total",
23	Help:      "The total number of TUI sessions",
24}, []string{"repo", "term"})
25
26var tuiSessionDuration = promauto.NewCounterVec(prometheus.CounterOpts{
27	Namespace: "soft_serve",
28	Subsystem: "ssh",
29	Name:      "tui_session_seconds_total",
30	Help:      "The total time spent in TUI sessions",
31}, []string{"repo", "term"})
32
33// SessionHandler is the soft-serve bubbletea ssh session handler.
34// This middleware must be run after the ContextMiddleware.
35func SessionHandler(s ssh.Session) *tea.Program {
36	pty, _, active := s.Pty()
37	if !active {
38		return nil
39	}
40
41	ctx := s.Context()
42	be := backend.FromContext(ctx)
43	cfg := config.FromContext(ctx)
44	cmd := s.Command()
45	initialRepo := ""
46	if len(cmd) == 1 {
47		initialRepo = cmd[0]
48		auth := be.AccessLevelByPublicKey(ctx, initialRepo, s.PublicKey())
49		if auth < access.ReadOnlyAccess {
50			wish.Fatalln(s, proto.ErrUnauthorized)
51			return nil
52		}
53	}
54
55	opts := bm.MakeOptions(s)
56	opts = append(opts,
57		tea.WithAltScreen(),
58		tea.WithoutCatchPanics(),
59		tea.WithMouseCellMotion(),
60		tea.WithContext(ctx),
61		tea.WithColorProfile(common.DefaultColorProfile),
62	)
63
64	c := common.NewCommon(ctx, pty.Window.Width, pty.Window.Height)
65	c.SetValue(common.ConfigKey, cfg)
66	m := NewUI(c, initialRepo)
67	p := tea.NewProgram(m, opts...)
68
69	tuiSessionCounter.WithLabelValues(initialRepo, pty.Term).Inc()
70
71	start := time.Now()
72	go func() {
73		<-ctx.Done()
74		tuiSessionDuration.WithLabelValues(initialRepo, pty.Term).Add(time.Since(start).Seconds())
75	}()
76
77	return p
78}