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}