server.go

 1package soft
 2
 3import (
 4	"fmt"
 5	"log"
 6
 7	"github.com/charmbracelet/soft/internal/config"
 8	"github.com/charmbracelet/soft/internal/git"
 9	"github.com/charmbracelet/soft/internal/tui"
10	"github.com/charmbracelet/soft/stats"
11	"github.com/meowgorithm/babyenv"
12
13	"github.com/charmbracelet/wish"
14	bm "github.com/charmbracelet/wish/bubbletea"
15	gm "github.com/charmbracelet/wish/git"
16	lm "github.com/charmbracelet/wish/logging"
17	"github.com/gliderlabs/ssh"
18)
19
20// Config is the configuration for the soft-serve.
21type Config struct {
22	Host            string `env:"SOFT_SERVE_HOST" default:""`
23	Port            int    `env:"SOFT_SERVE_PORT" default:"23231"`
24	KeyPath         string `env:"SOFT_SERVE_KEY_PATH" default:".ssh/soft_serve_server_ed25519"`
25	RepoPath        string `env:"SOFT_SERVE_REPO_PATH" default:".repos"`
26	InitialAdminKey string `env:"SOFT_SERVE_INITIAL_ADMIN_KEY" default:""`
27	Stats           stats.Stats
28	cfg             *config.Config
29}
30
31// DefaultConfig returns a Config with the values populated with the defaults
32// or specified environment variables.
33func DefaultConfig() *Config {
34	var scfg Config
35	err := babyenv.Parse(&scfg)
36	if err != nil {
37		log.Fatalln(err)
38	}
39	return &scfg
40}
41
42// NewServer returns a new *ssh.Server configured to serve Soft Serve. The SSH
43// server key-pair will be created if none exists. An initial admin SSH public
44// key can be provided with authKey. If authKey is provided, access will be
45// restricted to that key. If authKey is not provided, the server will be
46// publicly writable until configured otherwise by cloning the `config` repo.
47func NewServer(scfg *Config) *ssh.Server {
48	rs := git.NewRepoSource(scfg.RepoPath)
49	cfg, err := config.NewConfig(scfg.Host, scfg.Port, scfg.InitialAdminKey, rs)
50	if err != nil {
51		log.Fatalln(err)
52	}
53	if scfg.Stats != nil {
54		cfg = cfg.WithStats(scfg.Stats)
55	}
56	scfg.cfg = cfg
57	mw := []wish.Middleware{
58		bm.Middleware(tui.SessionHandler(cfg)),
59		gm.Middleware(scfg.RepoPath, cfg),
60		lm.Middleware(),
61	}
62	s, err := wish.NewServer(
63		ssh.PublicKeyAuth(cfg.PublicKeyHandler),
64		ssh.PasswordAuth(cfg.PasswordHandler),
65		wish.WithAddress(fmt.Sprintf("%s:%d", scfg.Host, scfg.Port)),
66		wish.WithHostKeyPath(scfg.KeyPath),
67		wish.WithMiddleware(mw...),
68	)
69	if err != nil {
70		log.Fatalln(err)
71	}
72	return s
73}