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 lm.Middleware(),
41 softMiddleware(ac),
42 bm.MiddlewareWithProgramHandler(SessionHandler(ac), termenv.ANSI256),
43 gm.Middleware(cfg.RepoPath, ac),
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 return srv.SSHServer.ListenAndServe()
71}
72
73// Serve serves the SSH server using the provided listener.
74func (srv *Server) Serve(l net.Listener) error {
75 return srv.SSHServer.Serve(l)
76}
77
78// Shutdown lets the server gracefully shutdown.
79func (srv *Server) Shutdown(ctx context.Context) error {
80 return srv.SSHServer.Shutdown(ctx)
81}
82
83// Close closes the SSH server.
84func (srv *Server) Close() error {
85 return srv.SSHServer.Close()
86}