Merge remote-tracking branch 'origin/wip'
[src/xds/xds-server.git] / lib / xdsconfig / fileconfig.go
1 package xdsconfig
2
3 import (
4         "encoding/json"
5         "os"
6         "os/user"
7         "path"
8         "path/filepath"
9         "strings"
10
11         common "github.com/iotbzh/xds-common/golib"
12 )
13
14 const (
15         // ConfigDir Directory in user HOME directory where xds config will be saved
16         ConfigDir = ".xds"
17         // GlobalConfigFilename Global config filename
18         GlobalConfigFilename = "config.json"
19         // FoldersConfigFilename Folders config filename
20         FoldersConfigFilename = "server-config_folders.xml"
21 )
22
23 // SyncThingConf definition
24 type SyncThingConf struct {
25         BinDir          string `json:"binDir"`
26         Home            string `json:"home"`
27         GuiAddress      string `json:"gui-address"`
28         GuiAPIKey       string `json:"gui-apikey"`
29         RescanIntervalS int    `json:"rescanIntervalS"`
30 }
31
32 // FileConfig is the JSON structure of xds-server config file (config.json)
33 type FileConfig struct {
34         WebAppDir    string         `json:"webAppDir"`
35         ShareRootDir string         `json:"shareRootDir"`
36         SdkRootDir   string         `json:"sdkRootDir"`
37         HTTPPort     string         `json:"httpPort"`
38         SThgConf     *SyncThingConf `json:"syncthing"`
39         LogsDir      string         `json:"logsDir"`
40 }
41
42 // readGlobalConfig reads configuration from a config file.
43 // Order to determine which config file is used:
44 //  1/ from command line option: "--config myConfig.json"
45 //  2/ $HOME/.xds/config.json file
46 //  3/ <current_dir>/config.json file
47 //  4/ <xds-server executable dir>/config.json file
48 func readGlobalConfig(c *Config, confFile string) error {
49
50         searchIn := make([]string, 0, 3)
51         if confFile != "" {
52                 searchIn = append(searchIn, confFile)
53         }
54         if usr, err := user.Current(); err == nil {
55                 searchIn = append(searchIn, path.Join(usr.HomeDir, ConfigDir,
56                         GlobalConfigFilename))
57         }
58         cwd, err := os.Getwd()
59         if err == nil {
60                 searchIn = append(searchIn, path.Join(cwd, "config.json"))
61         }
62         exePath, err := filepath.Abs(filepath.Dir(os.Args[0]))
63         if err == nil {
64                 searchIn = append(searchIn, path.Join(exePath, "config.json"))
65         }
66
67         var cFile *string
68         for _, p := range searchIn {
69                 if _, err := os.Stat(p); err == nil {
70                         cFile = &p
71                         break
72                 }
73         }
74         if cFile == nil {
75                 // No config file found
76                 return nil
77         }
78
79         c.Log.Infof("Use config file: %s", *cFile)
80
81         // TODO move on viper package to support comments in JSON and also
82         // bind with flags (command line options)
83         // see https://github.com/spf13/viper#working-with-flags
84         fd, _ := os.Open(*cFile)
85         defer fd.Close()
86         fCfg := FileConfig{}
87         if err := json.NewDecoder(fd).Decode(&fCfg); err != nil {
88                 return err
89         }
90
91         // Support environment variables (IOW ${MY_ENV_VAR} syntax) in config.json
92         vars := []*string{
93                 &fCfg.WebAppDir,
94                 &fCfg.ShareRootDir,
95                 &fCfg.SdkRootDir,
96                 &fCfg.LogsDir}
97         if fCfg.SThgConf != nil {
98                 vars = append(vars, &fCfg.SThgConf.Home, &fCfg.SThgConf.BinDir)
99         }
100         for _, field := range vars {
101                 var err error
102                 if *field, err = common.ResolveEnvVar(*field); err != nil {
103                         return err
104                 }
105         }
106
107         // Use config file settings else use default config
108         if fCfg.WebAppDir == "" {
109                 fCfg.WebAppDir = c.FileConf.WebAppDir
110         }
111         if fCfg.ShareRootDir == "" {
112                 fCfg.ShareRootDir = c.FileConf.ShareRootDir
113         }
114         if fCfg.SdkRootDir == "" {
115                 fCfg.SdkRootDir = c.FileConf.SdkRootDir
116         }
117         if fCfg.HTTPPort == "" {
118                 fCfg.HTTPPort = c.FileConf.HTTPPort
119         }
120
121         // Resolve webapp dir (support relative or full path)
122         fCfg.WebAppDir = strings.Trim(fCfg.WebAppDir, " ")
123         if !strings.HasPrefix(fCfg.WebAppDir, "/") && exePath != "" {
124                 // Check first from current directory
125                 for _, rootD := range []string{cwd, exePath} {
126                         ff := path.Join(rootD, fCfg.WebAppDir, "index.html")
127                         if common.Exists(ff) {
128                                 fCfg.WebAppDir = path.Join(rootD, fCfg.WebAppDir)
129                                 break
130                         }
131                 }
132         }
133
134         c.FileConf = fCfg
135         return nil
136 }
137
138 // FoldersConfigFilenameGet
139 func FoldersConfigFilenameGet() (string, error) {
140         usr, err := user.Current()
141         if err != nil {
142                 return "", err
143         }
144         return path.Join(usr.HomeDir, ConfigDir, FoldersConfigFilename), nil
145 }