Monorepo for Tangled tangled.org
5

Configure Feed

Select the types of activity you want to include in your feed.

1package ssh 2 3import ( 4 "context" 5 "fmt" 6 "log/slog" 7 8 "github.com/charmbracelet/ssh" 9 "github.com/charmbracelet/wish" 10 tea "github.com/charmbracelet/wish/bubbletea" 11 "tangled.org/core/appview/config" 12 "tangled.org/core/appview/db" 13 "tangled.org/core/appview/pipelines" 14) 15 16type Server struct { 17 db *db.DB 18 config *config.Config 19 pipelineNotifier *pipelines.StatusNotifier 20 logger *slog.Logger 21} 22 23func New(db *db.DB, cfg *config.Config, pn *pipelines.StatusNotifier, logger *slog.Logger) *Server { 24 return &Server{db: db, config: cfg, pipelineNotifier: pn, logger: logger} 25} 26 27func (s *Server) ListenAndServe(ctx context.Context) error { 28 opts := []ssh.Option{ 29 wish.WithAddress(s.config.SSH.ListenAddr), 30 wish.WithMiddleware( 31 tea.Middleware(s.teaHandler), 32 requirePty, 33 ), 34 } 35 36 if s.config.SSH.HostKeyPath != "" { 37 opts = append(opts, wish.WithHostKeyPath(s.config.SSH.HostKeyPath)) 38 } 39 40 srv, err := wish.NewServer(opts...) 41 if err != nil { 42 return err 43 } 44 45 go func() { 46 <-ctx.Done() 47 s.logger.Info("shutting down SSH log server") 48 srv.Close() 49 }() 50 51 s.logger.Info("SSH log server listening", "address", s.config.SSH.ListenAddr) 52 if err := srv.ListenAndServe(); err != ssh.ErrServerClosed { 53 return err 54 } 55 s.logger.Info("SSH log server stopped") 56 return nil 57} 58 59// requirePty is a middleware that rejects connections without a PTY and tells the user to pass -t. 60func requirePty(next ssh.Handler) ssh.Handler { 61 return func(sess ssh.Session) { 62 _, _, ok := sess.Pty() 63 if !ok { 64 fmt.Fprintf(sess.Stderr(), "error: no terminal allocated\nhint: use `ssh -t` to force PTY allocation\n") 65 sess.Exit(1) 66 return 67 } 68 next(sess) 69 } 70}