1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
package xdsconfig
import (
"encoding/json"
"os"
"os/user"
"path"
"path/filepath"
"regexp"
"strings"
)
type SyncThingConf struct {
Home string `json:"home"`
GuiAddress string `json:"gui-address"`
GuiAPIKey string `json:"gui-apikey"`
}
type FileConfig struct {
WebAppDir string `json:"webAppDir"`
ShareRootDir string `json:"shareRootDir"`
HTTPPort string `json:"httpPort"`
SThgConf SyncThingConf `json:"syncthing"`
}
// getConfigFromFile reads configuration from a config file.
// Order to determine which config file is used:
// 1/ from command line option: "--config myConfig.json"
// 2/ $HOME/.xds/config.json file
// 3/ <xds-server executable dir>/config.json file
func updateConfigFromFile(c *Config, confFile string) error {
searchIn := make([]string, 0, 3)
if confFile != "" {
searchIn = append(searchIn, confFile)
}
if usr, err := user.Current(); err == nil {
searchIn = append(searchIn, path.Join(usr.HomeDir, ".xds", "config.json"))
}
cwd, err := os.Getwd()
if err == nil {
searchIn = append(searchIn, path.Join(cwd, "config.json"))
}
exePath, err := filepath.Abs(filepath.Dir(os.Args[0]))
if err == nil {
searchIn = append(searchIn, path.Join(exePath, "config.json"))
}
var cFile *string
for _, p := range searchIn {
if _, err := os.Stat(p); err == nil {
cFile = &p
break
}
}
if cFile == nil {
// No config file found
return nil
}
// TODO move on viper package to support comments in JSON and also
// bind with flags (command line options)
// see https://github.com/spf13/viper#working-with-flags
fd, _ := os.Open(*cFile)
defer fd.Close()
fCfg := FileConfig{}
if err := json.NewDecoder(fd).Decode(&fCfg); err != nil {
return err
}
c.fileConf = fCfg
// Support environment variables (IOW ${MY_ENV_VAR} syntax) in config.json
// TODO: better to use reflect package to iterate on fields and be more generic
fCfg.WebAppDir = path.Clean(resolveEnvVar(fCfg.WebAppDir))
fCfg.ShareRootDir = path.Clean(resolveEnvVar(fCfg.ShareRootDir))
fCfg.SThgConf.Home = path.Clean(resolveEnvVar(fCfg.SThgConf.Home))
// Config file settings overwrite default config
if fCfg.WebAppDir != "" {
c.WebAppDir = strings.Trim(fCfg.WebAppDir, " ")
}
// Is it a full path ?
if !strings.HasPrefix(c.WebAppDir, "/") && exePath != "" {
// Check first from current directory
for _, rootD := range []string{cwd, exePath} {
ff := path.Join(rootD, c.WebAppDir, "index.html")
if exists(ff) {
c.WebAppDir = path.Join(rootD, c.WebAppDir)
break
}
}
}
if fCfg.ShareRootDir != "" {
c.ShareRootDir = fCfg.ShareRootDir
}
if fCfg.HTTPPort != "" {
c.HTTPPort = fCfg.HTTPPort
}
return nil
}
// resolveEnvVar Resolved environment variable regarding the syntax ${MYVAR}
func resolveEnvVar(s string) string {
re := regexp.MustCompile("\\${(.*)}")
vars := re.FindAllStringSubmatch(s, -1)
res := s
for _, v := range vars {
val := os.Getenv(v[1])
if val != "" {
rer := regexp.MustCompile("\\${" + v[1] + "}")
res = rer.ReplaceAllString(res, val)
}
}
return res
}
// exists returns whether the given file or directory exists or not
func exists(path string) bool {
_, err := os.Stat(path)
if err == nil {
return true
}
if os.IsNotExist(err) {
return false
}
return true
}
|