1package common
2
3import (
4 "errors"
5 "fmt"
6 "io/fs"
7 "os"
8 "path/filepath"
9 "runtime"
10 "strings"
11
12 "github.com/bnema/gordon/pkg/docker"
13 "github.com/bnema/gordon/pkg/parser"
14)
15
16type Config struct {
17 General GeneralConfig `yaml:"General"`
18 Http HttpConfig `yaml:"Http"`
19 Admin AdminConfig `yaml:"Admin"`
20 ContainerEngine ContainerEngineConfig `yaml:"ContainerEngine"`
21 Traefik TraefikConfig `yaml:"Traefik"`
22 Build BuildConfig `yaml:"-"`
23}
24
25type BuildConfig struct {
26 RunEnv string `yaml:"-"` // come from env
27 BuildVersion string `yaml:"-"` // come from build ldflags
28 BuildCommit string `yaml:"-"` // come from build ldflags
29 BuildDate string `yaml:"-"` // come from build ldflags
30 ProxyURL string `yaml:"-"`
31}
32
33type AdminConfig struct {
34 Path string `yaml:"path"`
35}
36
37type GeneralConfig struct {
38 StorageDir string `yaml:"storageDir"`
39 Token string `yaml:"token"`
40}
41type HttpConfig struct {
42 Port string `yaml:"port"`
43 Domain string `yaml:"domain"`
44 SubDomain string `yaml:"subDomain"`
45 BackendURL string `yaml:"backendURL"`
46 Https bool `yaml:"https"`
47}
48
49type ContainerEngineConfig struct {
50 Sock string `yaml:"dockersock"`
51 PodmanSock string `yaml:"podmansock"`
52 Podman bool `yaml:"podman"`
53 Network string `yaml:"network"`
54}
55
56type TraefikConfig struct {
57 EntryPoint string `yaml:"entryPoint"`
58 SecureEntryPoint string `yaml:"secureEntryPoint"`
59 Resolver string `yaml:"resolver"`
60}
61
62// Default values
63var (
64 sock = "/var/run/docker.sock"
65 podmansock = "/run/user/1000/podman/podman.sock"
66 entryPoint = "web"
67 entryPointSecure = "websecure"
68 resolver = "myresolver"
69)
70
71func getConfigDir() (string, error) {
72 var configDir string
73
74 if isWSL() {
75 // Use XDG_CONFIG_HOME for WSL
76 if xdgConfigHome := os.Getenv("XDG_CONFIG_HOME"); xdgConfigHome != "" {
77 configDir = filepath.Join(xdgConfigHome, "Gordon")
78 } else {
79 // If XDG_CONFIG_HOME is not set, fall back to default locations
80 homeDir, err := os.UserHomeDir()
81 if err != nil {
82 // If we can't get the home directory, use a fallback
83 homeDir = os.TempDir() // Use the system's temp directory as a fallback
84 fmt.Printf("Warning: Unable to determine home directory. Using temp directory: %s\n", homeDir)
85 }
86
87 configDir = filepath.Join(homeDir, ".config", "Gordon")
88
89 }
90
91 return configDir, nil
92 }
93
94 if docker.IsRunningInContainer() {
95 return "/.", nil
96 }
97
98 // Check for XDG_CONFIG_HOME first
99 if xdgConfigHome := os.Getenv("XDG_CONFIG_HOME"); xdgConfigHome != "" {
100 configDir = filepath.Join(xdgConfigHome, "Gordon")
101 } else {
102 // If XDG_CONFIG_HOME is not set, fall back to default locations
103 homeDir, err := os.UserHomeDir()
104 if err != nil {
105 // If we can't get the home directory, use a fallback
106 homeDir = os.TempDir() // Use the system's temp directory as a fallback
107 fmt.Printf("Warning: Unable to determine home directory. Using temp directory: %s\n", homeDir)
108 }
109
110 // Select the configuration directory based on the OS
111 if runtime.GOOS == "windows" {
112 configDir = filepath.Join(homeDir, "AppData", "Roaming", "Gordon")
113 } else {
114 configDir = filepath.Join(homeDir, ".config", "Gordon")
115 }
116 }
117
118 return configDir, nil
119}
120
121func readAndUnmarshalConfig(fs fs.FS, filePath string, config *Config) error {
122 err := parser.ParseYAMLFile(fs, filePath, config)
123 if err != nil {
124 return fmt.Errorf("error reading and unmarshaling configuration file: %w", err)
125 }
126 return nil
127}
128
129func (config *Config) LoadConfig() (*Config, error) {
130 configDir, err := getConfigDir()
131 if err != nil {
132 return nil, fmt.Errorf("error getting configuration directory: %w", err)
133 }
134
135 configFilePath := filepath.Join(configDir, "config.yml")
136
137 _, err = os.Stat(configFilePath)
138 if errors.Is(err, fs.ErrNotExist) {
139 fmt.Printf("Config file not found, creating it at %s\n", configFilePath)
140
141 // Create config dir if it doesn't exist
142 err = os.MkdirAll(configDir, 0755)
143 if err != nil {
144 return nil, fmt.Errorf("error creating configuration directory: %w", err)
145 }
146
147 // Create config file with the default values of ContainerEngineConfig
148 config = &Config{
149 ContainerEngine: ContainerEngineConfig{
150 Sock: sock,
151 PodmanSock: podmansock,
152 Podman: ReadUserInput("Are you using podman ? (y/n)") == "y",
153 },
154 Traefik: TraefikConfig{
155 EntryPoint: entryPoint,
156 SecureEntryPoint: entryPointSecure,
157 Resolver: resolver,
158 },
159 }
160
161 err = config.SaveConfig()
162 if err != nil {
163 return nil, fmt.Errorf("error saving new configuration: %w", err)
164 }
165 return config, nil
166 }
167
168 if err != nil {
169 return nil, fmt.Errorf("error checking configuration file: %w", err)
170 }
171
172 err = readAndUnmarshalConfig(os.DirFS(configDir), "config.yml", config)
173 if err != nil {
174 return nil, err
175 }
176
177 // Load run env
178 config.Build.RunEnv = os.Getenv("RUN_ENV")
179
180 if config.Build.RunEnv == "" {
181 config.Build.RunEnv = "prod"
182 }
183
184 return config, nil
185}
186
187func (config *Config) SaveConfig() error {
188 configDir, err := getConfigDir()
189 if err != nil {
190 return fmt.Errorf("error getting configuration directory: %w", err)
191 }
192 configFilePath := filepath.Join(configDir, "config.yml")
193
194 err = parser.WriteYAMLFile(configFilePath, config)
195 if err != nil {
196 return fmt.Errorf("error writing configuration file: %w", err)
197 }
198
199 return nil
200}
201
202func (config *Config) GetToken() (string, error) {
203 token := config.General.Token
204 if token == "" {
205 return "", fmt.Errorf("no token found in config.yml")
206 }
207
208 return token, nil
209
210}
211
212// GetRunEnv returns the run environment
213func (config *BuildConfig) GetRunEnv() string {
214 return config.RunEnv
215}
216
217// GetVersion returns the version
218func (config *Config) GetVersion() string {
219 return config.Build.BuildVersion
220}
221
222// get storage dir
223func (config *Config) GetStorageDir() string {
224 return config.General.StorageDir
225}
226
227func (c *Config) GetBackendURL() string {
228 return c.Http.BackendURL
229}
230
231func (c *Config) SetToken(token string) {
232 c.General.Token = token
233}
234
235func isWSL() bool {
236 // Check for /proc/version file
237 if _, err := os.Stat("/proc/version"); err == nil {
238 content, err := os.ReadFile("/proc/version")
239 if err == nil && strings.Contains(strings.ToLower(string(content)), "microsoft") {
240 return true
241 }
242 }
243
244 // Check for WSL-specific environment variable
245 if os.Getenv("WSL_DISTRO_NAME") != "" {
246 return true
247 }
248
249 return false
250}
251
252func (c *HttpConfig) Protocol() string {
253 if c.Https {
254 return "https"
255 }
256 return "http"
257}
258
259func (c *HttpConfig) FullDomain() string {
260 if c.SubDomain != "" {
261 return fmt.Sprintf("%s.%s", c.SubDomain, c.Domain)
262 }
263 return c.Domain
264}