1package config
  2
  3import (
  4	"os/exec"
  5	"path/filepath"
  6	"strings"
  7
  8	"gopkg.in/yaml.v2"
  9
 10	"fmt"
 11	"os"
 12
 13	"github.com/charmbracelet/soft/config"
 14	"github.com/charmbracelet/soft/internal/git"
 15	gg "github.com/go-git/go-git/v5"
 16	"github.com/go-git/go-git/v5/plumbing/object"
 17)
 18
 19type Config struct {
 20	Name         string `yaml:"name"`
 21	Host         string `yaml:"host"`
 22	Port         int    `yaml:"port"`
 23	AnonAccess   string `yaml:"anon-access"`
 24	AllowKeyless bool   `yaml:"allow-keyless"`
 25	Users        []User `yaml:"users"`
 26	Repos        []Repo `yaml:"repos"`
 27	Source       *git.RepoSource
 28	Cfg          *config.Config
 29}
 30
 31type User struct {
 32	Name        string   `yaml:"name"`
 33	Admin       bool     `yaml:"admin"`
 34	PublicKeys  []string `yaml:"public-keys"`
 35	CollabRepos []string `yaml:"collab-repos"`
 36}
 37
 38type Repo struct {
 39	Name    string `yaml:"name"`
 40	Repo    string `yaml:"repo"`
 41	Note    string `yaml:"note"`
 42	Private bool   `yaml:"private"`
 43}
 44
 45func NewConfig(cfg *config.Config) (*Config, error) {
 46	var anonAccess string
 47	var yamlUsers string
 48	var displayHost string
 49	host := cfg.Host
 50	port := cfg.Port
 51	pk := cfg.InitialAdminKey
 52	rs := git.NewRepoSource(cfg.RepoPath)
 53	c := &Config{
 54		Cfg: cfg,
 55	}
 56	c.Host = cfg.Host
 57	c.Port = port
 58	c.Source = rs
 59	if pk == "" {
 60		anonAccess = "read-write"
 61	} else {
 62		anonAccess = "no-access"
 63	}
 64	if host == "" {
 65		displayHost = "localhost"
 66	} else {
 67		displayHost = host
 68	}
 69	yamlConfig := fmt.Sprintf(defaultConfig, displayHost, port, anonAccess)
 70	if pk != "" {
 71		pks := ""
 72		for _, key := range strings.Split(strings.TrimSpace(pk), "\n") {
 73			pks += fmt.Sprintf("      - %s\n", key)
 74		}
 75		yamlUsers = fmt.Sprintf(hasKeyUserConfig, pks)
 76	} else {
 77		yamlUsers = defaultUserConfig
 78	}
 79	yaml := fmt.Sprintf("%s%s%s", yamlConfig, yamlUsers, exampleUserConfig)
 80	err := c.createDefaultConfigRepo(yaml)
 81	if err != nil {
 82		return nil, err
 83	}
 84	return c, nil
 85}
 86
 87func (cfg *Config) Reload() error {
 88	err := cfg.Source.LoadRepos()
 89	if err != nil {
 90		return err
 91	}
 92	cr, err := cfg.Source.GetRepo("config")
 93	if err != nil {
 94		return err
 95	}
 96	cs, err := cr.LatestFile("config.yaml")
 97	if err != nil {
 98		return err
 99	}
100	err = yaml.Unmarshal([]byte(cs), cfg)
101	if err != nil {
102		return fmt.Errorf("bad yaml in config.yaml: %s", err)
103	}
104	return nil
105}
106
107func createFile(path string, content string) error {
108	f, err := os.Create(path)
109	if err != nil {
110		return err
111	}
112	defer f.Close()
113	_, err = f.WriteString(content)
114	if err != nil {
115		return err
116	}
117	return f.Sync()
118}
119
120func (cfg *Config) createDefaultConfigRepo(yaml string) error {
121	cn := "config"
122	rs := cfg.Source
123	err := rs.LoadRepos()
124	if err != nil {
125		return err
126	}
127	_, err = rs.GetRepo(cn)
128	if err == git.ErrMissingRepo {
129		cr, err := rs.InitRepo(cn, true)
130		if err != nil {
131			return err
132		}
133		wt, err := cr.Repository.Worktree()
134		if err != nil {
135			return err
136		}
137		rm, err := wt.Filesystem.Create("README.md")
138		if err != nil {
139			return err
140		}
141		_, err = rm.Write([]byte(defaultReadme))
142		if err != nil {
143			return err
144		}
145		cf, err := wt.Filesystem.Create("config.yaml")
146		if err != nil {
147			return err
148		}
149		_, err = cf.Write([]byte(yaml))
150		if err != nil {
151			return err
152		}
153		_, err = wt.Add("README.md")
154		if err != nil {
155			return err
156		}
157		_, err = wt.Add("config.yaml")
158		if err != nil {
159			return err
160		}
161		_, err = wt.Commit("Default init", &gg.CommitOptions{
162			All: true,
163			Author: &object.Signature{
164				Name:  "Soft Serve Server",
165				Email: "vt100@charm.sh",
166			},
167		})
168		if err != nil {
169			return err
170		}
171		err = cr.Repository.Push(&gg.PushOptions{})
172		if err != nil {
173			return err
174		}
175		cmd := exec.Command("git", "update-server-info")
176		cmd.Dir = filepath.Join(rs.Path, cn)
177		err = cmd.Run()
178		if err != nil {
179			return err
180		}
181	} else if err != nil {
182		return err
183	}
184	return cfg.Reload()
185}
186
187func (cfg *Config) isPrivate(repo string) bool {
188	for _, r := range cfg.Repos {
189		if r.Repo == repo {
190			return r.Private
191		}
192	}
193	return false
194}