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}