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/internal/git"
 13	"github.com/charmbracelet/soft/stats"
 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	Stats        stats.Stats
 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 (cfg *Config) WithStats(s stats.Stats) *Config {
 45	cfg.Stats = s
 46	return cfg
 47}
 48
 49func NewConfig(host string, port int, pk string, rs *git.RepoSource) (*Config, error) {
 50	var anonAccess string
 51	var yamlUsers string
 52	var displayHost string
 53	cfg := &Config{}
 54	cfg.Host = host
 55	cfg.Port = port
 56	cfg.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 := cfg.createDefaultConfigRepo(yaml)
 79	if err != nil {
 80		return nil, err
 81	}
 82	return cfg, 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, false)
128		if err != nil {
129			return err
130		}
131
132		rp := filepath.Join(rs.Path, cn, "README.md")
133		err = createFile(rp, defaultReadme)
134		if err != nil {
135			return err
136		}
137		cp := filepath.Join(rs.Path, cn, "config.yaml")
138		err = createFile(cp, yaml)
139		if err != nil {
140			return err
141		}
142		wt, err := cr.Repository.Worktree()
143		if err != nil {
144			return err
145		}
146		_, err = wt.Add("README.md")
147		if err != nil {
148			return err
149		}
150		_, err = wt.Add("config.yaml")
151		if err != nil {
152			return err
153		}
154		_, err = wt.Commit("Default init", &gg.CommitOptions{
155			All: true,
156			Author: &object.Signature{
157				Name:  "Soft Serve Server",
158				Email: "vt100@charm.sh",
159			},
160		})
161		if err != nil {
162			return err
163		}
164		if err != nil {
165			return err
166		}
167	} else if err != nil {
168		return err
169	}
170	return cfg.reload()
171}
172
173func (cfg *Config) isPrivate(repo string) bool {
174	for _, r := range cfg.Repos {
175		if r.Repo == repo {
176			return r.Private
177		}
178	}
179	return false
180}