main
cfg.go
Eric Bower
·
2026-02-25
1package patchbin
2
3import (
4 "fmt"
5 "log/slog"
6 "path/filepath"
7 "strings"
8 "time"
9
10 "github.com/knadh/koanf/parsers/toml"
11 "github.com/knadh/koanf/providers/env"
12 "github.com/knadh/koanf/providers/file"
13 "github.com/knadh/koanf/v2"
14 "golang.org/x/crypto/ssh"
15)
16
17var k = koanf.New(".")
18
19type GitCfg struct {
20 DataDir string `koanf:"data_dir"`
21 Url string `koanf:"url"`
22 Host string `koanf:"host"`
23 SshPort string `koanf:"ssh_port"`
24 WebPort string `koanf:"web_port"`
25 PromPort string `koanf:"prom_port"`
26 AdminsStr []string `koanf:"admins"`
27 Admins []ssh.PublicKey `koanf:"admins_pk"`
28 Theme string `koanf:"theme"`
29 TimeFormat string `koanf:"time_format"`
30 Desc string `koanf:"desc"`
31 RateLimitCount int `koanf:"rate_limit_count"`
32 RateLimitInterval string `koanf:"rate_limit_interval"`
33 MaxStdinBytes int64 `koanf:"max_stdin_bytes"`
34 Logger *slog.Logger
35}
36
37func LoadConfigFile(fpath string, logger *slog.Logger) {
38 fpp, err := filepath.Abs(fpath)
39 if err != nil {
40 panic(err)
41 }
42 logger.Info("loading configuration file", "fpath", fpp)
43
44 if err := k.Load(file.Provider(fpp), toml.Parser()); err != nil {
45 panic(fmt.Sprintf("error loading config: %v", err))
46 }
47}
48
49func NewGitCfg(logger *slog.Logger) *GitCfg {
50 err := k.Load(env.Provider("GITPR_", ".", func(s string) string {
51 keyword := strings.ToLower(strings.TrimPrefix(s, "GITPR_"))
52 return keyword
53 }), nil)
54 if err != nil {
55 panic(fmt.Sprintf("could not load environment variables: %v", err))
56 }
57
58 var out GitCfg
59 err = k.UnmarshalWithConf("", &out, koanf.UnmarshalConf{Tag: "koanf"})
60 if err != nil {
61 panic(fmt.Sprintf("could not unmarshal config: %v", err))
62 }
63
64 if len(out.AdminsStr) > 0 {
65 keys, err := GetAuthorizedKeys(out.AdminsStr)
66 if err == nil {
67 out.Admins = keys
68 } else {
69 panic(fmt.Sprintf("could not parse authorized keys file: %v", err))
70 }
71 } else {
72 logger.Info("no admin specified in config")
73 }
74
75 // make datadir absolute
76 tmpdir := out.DataDir
77 if out.DataDir == "" {
78 tmpdir = "./data"
79 }
80 datadir, err := filepath.Abs(tmpdir)
81 out.DataDir = datadir
82 if err != nil {
83 panic(err)
84 }
85
86 if out.Host == "" {
87 out.Host = "0.0.0.0"
88 }
89
90 if out.SshPort == "" {
91 out.SshPort = "2222"
92 }
93
94 if out.WebPort == "" {
95 out.WebPort = "3000"
96 }
97
98 if out.Theme == "" {
99 out.Theme = "dracula"
100 }
101
102 if out.TimeFormat == "" {
103 out.TimeFormat = time.RFC3339
104 }
105
106 if out.RateLimitCount == 0 {
107 out.RateLimitCount = 10
108 }
109
110 if out.RateLimitInterval == "" {
111 out.RateLimitInterval = "1m"
112 }
113
114 if out.MaxStdinBytes == 0 {
115 out.MaxStdinBytes = 5 << 20 // 5MB
116 }
117
118 logger.Info(
119 "config",
120 "url", out.Url,
121 "data_dir", out.DataDir,
122 "host", out.Host,
123 "ssh_port", out.SshPort,
124 "web_port", out.WebPort,
125 "theme", out.Theme,
126 "time_format", out.TimeFormat,
127 "desc", out.Desc,
128 "rate_limit_count", out.RateLimitCount,
129 "rate_limit_interval", out.RateLimitInterval,
130 "max_stdin_bytes", out.MaxStdinBytes,
131 )
132
133 for _, pubkey := range out.AdminsStr {
134 logger.Info("admin", "pubkey", pubkey)
135 }
136
137 out.Logger = logger
138 return &out
139}