1package server
2
3import (
4 "context"
5 "net/http"
6
7 "github.com/charmbracelet/log"
8
9 "github.com/charmbracelet/soft-serve/server/backend"
10 "github.com/charmbracelet/soft-serve/server/config"
11 "github.com/charmbracelet/ssh"
12 "golang.org/x/sync/errgroup"
13)
14
15var (
16 logger = log.WithPrefix("server")
17)
18
19// Server is the Soft Serve server.
20type Server struct {
21 SSHServer *SSHServer
22 GitDaemon *GitDaemon
23 HTTPServer *HTTPServer
24 Config *config.Config
25 Backend backend.Backend
26 Access backend.AccessMethod
27}
28
29// NewServer returns a new *ssh.Server configured to serve Soft Serve. The SSH
30// server key-pair will be created if none exists. An initial admin SSH public
31// key can be provided with authKey. If authKey is provided, access will be
32// restricted to that key. If authKey is not provided, the server will be
33// publicly writable until configured otherwise by cloning the `config` repo.
34func NewServer(cfg *config.Config) (*Server, error) {
35 var err error
36 srv := &Server{
37 Config: cfg,
38 Backend: cfg.Backend,
39 Access: cfg.Access,
40 }
41 srv.SSHServer, err = NewSSHServer(cfg)
42 if err != nil {
43 return nil, err
44 }
45
46 srv.GitDaemon, err = NewGitDaemon(cfg)
47 if err != nil {
48 return nil, err
49 }
50
51 srv.HTTPServer, err = NewHTTPServer(cfg)
52 if err != nil {
53 return nil, err
54 }
55
56 return srv, nil
57}
58
59// Start starts the SSH server.
60func (s *Server) Start() error {
61 var errg errgroup.Group
62 errg.Go(func() error {
63 log.Print("Starting Git daemon", "addr", s.Config.Git.ListenAddr)
64 if err := s.GitDaemon.Start(); err != ErrServerClosed {
65 return err
66 }
67 return nil
68 })
69 errg.Go(func() error {
70 log.Print("Starting HTTP server", "addr", s.Config.HTTP.ListenAddr)
71 if err := s.HTTPServer.ListenAndServe(); err != http.ErrServerClosed {
72 return err
73 }
74 return nil
75 })
76 errg.Go(func() error {
77 log.Print("Starting SSH server", "addr", s.Config.SSH.ListenAddr)
78 if err := s.SSHServer.ListenAndServe(); err != ssh.ErrServerClosed {
79 return err
80 }
81 return nil
82 })
83 return errg.Wait()
84}
85
86// Shutdown lets the server gracefully shutdown.
87func (s *Server) Shutdown(ctx context.Context) error {
88 var errg errgroup.Group
89 errg.Go(func() error {
90 return s.GitDaemon.Shutdown(ctx)
91 })
92 errg.Go(func() error {
93 return s.HTTPServer.Shutdown(ctx)
94 })
95 errg.Go(func() error {
96 return s.SSHServer.Shutdown(ctx)
97 })
98 return errg.Wait()
99}
100
101// Close closes the SSH server.
102func (s *Server) Close() error {
103 var errg errgroup.Group
104 errg.Go(s.GitDaemon.Close)
105 errg.Go(s.HTTPServer.Close)
106 errg.Go(s.SSHServer.Close)
107 return errg.Wait()
108}