1package ssh
2
3import (
4 "time"
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 bm "github.com/charmbracelet/wish/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 number of 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 output := bm.MakeRenderer(s)
56 opts := append(
57 bm.MakeOptions(s),
58 tea.WithAltScreen(),
59 tea.WithoutCatchPanics(),
60 tea.WithMouseCellMotion(),
61 tea.WithContext(ctx),
62 )
63 c := common.NewCommon(ctx, output, pty.Window.Width, pty.Window.Height)
64 c.SetValue(common.ConfigKey, cfg)
65 m := NewUI(c, initialRepo)
66 p := tea.NewProgram(m, opts...)
67
68 tuiSessionCounter.WithLabelValues(initialRepo, pty.Term).Inc()
69
70 start := time.Now()
71 go func() {
72 <-ctx.Done()
73 tuiSessionDuration.WithLabelValues(initialRepo, pty.Term).Add(time.Since(start).Seconds())
74 }()
75
76 return p
77}