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}