1package server
 2
 3import (
 4	"context"
 5	"fmt"
 6	"log"
 7	"net"
 8
 9	appCfg "github.com/charmbracelet/soft-serve/config"
10	"github.com/charmbracelet/soft-serve/server/config"
11	"github.com/charmbracelet/wish"
12	bm "github.com/charmbracelet/wish/bubbletea"
13	gm "github.com/charmbracelet/wish/git"
14	lm "github.com/charmbracelet/wish/logging"
15	rm "github.com/charmbracelet/wish/recover"
16	"github.com/gliderlabs/ssh"
17	"github.com/muesli/termenv"
18)
19
20// Server is the Soft Serve server.
21type Server struct {
22	SSHServer *ssh.Server
23	Config    *config.Config
24	config    *appCfg.Config
25}
26
27// NewServer returns a new *ssh.Server configured to serve Soft Serve. The SSH
28// server key-pair will be created if none exists. An initial admin SSH public
29// key can be provided with authKey. If authKey is provided, access will be
30// restricted to that key. If authKey is not provided, the server will be
31// publicly writable until configured otherwise by cloning the `config` repo.
32func NewServer(cfg *config.Config) *Server {
33	ac, err := appCfg.NewConfig(cfg)
34	if err != nil {
35		log.Fatal(err)
36	}
37	mw := []wish.Middleware{
38		rm.MiddlewareWithLogger(
39			cfg.ErrorLog,
40			softMiddleware(ac),
41			bm.MiddlewareWithProgramHandler(SessionHandler(ac), termenv.ANSI256),
42			gm.Middleware(cfg.RepoPath, ac),
43			lm.Middleware(),
44		),
45	}
46	s, err := wish.NewServer(
47		ssh.PublicKeyAuth(ac.PublicKeyHandler),
48		ssh.PasswordAuth(ac.PasswordHandler),
49		wish.WithAddress(fmt.Sprintf("%s:%d", cfg.BindAddr, cfg.Port)),
50		wish.WithHostKeyPath(cfg.KeyPath),
51		wish.WithMiddleware(mw...),
52	)
53	if err != nil {
54		log.Fatalln(err)
55	}
56	return &Server{
57		SSHServer: s,
58		Config:    cfg,
59		config:    ac,
60	}
61}
62
63// Reload reloads the server configuration.
64func (srv *Server) Reload() error {
65	return srv.config.Reload()
66}
67
68// Start starts the SSH server.
69func (srv *Server) Start() error {
70	if err := srv.SSHServer.ListenAndServe(); err != ssh.ErrServerClosed {
71		return err
72	}
73	return nil
74}
75
76// Serve serves the SSH server using the provided listener.
77func (srv *Server) Serve(l net.Listener) error {
78	if err := srv.SSHServer.Serve(l); err != ssh.ErrServerClosed {
79		return err
80	}
81	return nil
82}
83
84// Shutdown lets the server gracefully shutdown.
85func (srv *Server) Shutdown(ctx context.Context) error {
86	return srv.SSHServer.Shutdown(ctx)
87}
88
89// Close closes the SSH server.
90func (srv *Server) Close() error {
91	return srv.SSHServer.Close()
92}