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