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