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 := git.NewRepoSource(cfg.RepoPath)
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}