config.go

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