-
Notifications
You must be signed in to change notification settings - Fork 0
/
config.go
176 lines (143 loc) · 4.19 KB
/
config.go
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
package config
import (
"github.com/rs/zerolog"
"os"
"os/exec"
"path"
"path/filepath"
"runtime"
"strings"
"time"
_ "github.com/kardianos/minwinsvc" // import minwinsvc for windows services
"github.com/pkg/errors"
"github.com/robfig/cron/v3"
"gopkg.in/ini.v1"
)
var (
// Path of config file
Path string
// General config
General = &general{}
// Mail config
Mail = &mail{}
// DB config
DB = &db{}
// Log config
Log = &log{}
// AppWorkPath of binary
AppWorkPath string
isWindows bool
)
// general defines the general configuration.
type general struct {
Root string `ini:"ROOT"`
CronExpression string `ini:"SCHEDULE"`
Schedule cron.Schedule `ini:"-"`
Interval time.Duration `ini:"-"`
}
// mail defines the mailer configuration.
type mail struct {
Server string `ini:"SERVER"`
Port int `ini:"PORT"`
User string `ini:"USER"`
Password string `ini:"PASSWORD"`
From string `ini:"FROM"`
To string `ini:"TO"`
Subject string `ini:"SUBJECT"`
}
// db defines the database configuration.
type db struct {
Server string `ini:"SERVER"`
Port int `ini:"PORT"`
User string `ini:"USER"`
Password string `ini:"PASSWORD"`
Database string `ini:"DATABASE"`
}
// log defines the logging configuration.
type log struct {
Level string `ini:"LEVEL"`
Colored bool `ini:"COLORED"`
Pretty bool `ini:"PRETTY"`
}
// Load loads the configuration from `Path`
func Load() error {
isWindows = runtime.GOOS == "windows"
var appPath string
var err error
if appPath, err = getAppPath(); err != nil {
return errors.Wrap(err, "could not get application path")
}
AppWorkPath = getWorkPath(appPath)
if !filepath.IsAbs(Path) {
Path = path.Join(AppWorkPath, Path)
}
config, err := ini.Load(Path)
if err != nil {
return errors.Wrap(err, "could not load ini config")
}
if err = config.Section("general").MapTo(General); err != nil {
return errors.Wrap(err, "could not map general section")
}
if !filepath.IsAbs(General.Root) {
General.Root = path.Join(AppWorkPath, General.Root)
}
if err := os.MkdirAll(General.Root, os.ModePerm); err != nil {
return errors.Wrap(err, "could not create folders of root path")
}
parser := cron.NewParser(cron.Second | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor)
General.Schedule, err = parser.Parse(General.CronExpression)
if err != nil {
return errors.Wrap(err, "could not parse cron expression")
}
// calculate interval
nextExecutionTime := General.Schedule.Next(time.Now())
General.Interval = General.Schedule.Next(nextExecutionTime).Sub(nextExecutionTime)
// check if interval is longer than 15 minutes
if General.Interval.Minutes() < 15 {
return errors.New("schedule interval shorter than 15 minutes")
}
if err = config.Section("mail").MapTo(Mail); err != nil {
return errors.Wrap(err, "could not map mail section")
}
if err = config.Section("db").MapTo(DB); err != nil {
return errors.Wrap(err, "could not map db section")
}
if err = config.Section("log").MapTo(Log); err != nil {
return errors.Wrap(err, "could not map log section")
}
if _, err := zerolog.ParseLevel(Log.Level); err != nil {
return errors.Wrap(err, "could not parse log level")
}
return nil
}
func getAppPath() (string, error) {
var appPath string
if isWindows && filepath.IsAbs(os.Args[0]) {
appPath = filepath.Clean(os.Args[0])
} else {
var err error
appPath, err = exec.LookPath(os.Args[0])
if err != nil {
return "", errors.Wrapf(err, "could not find %s", os.Args[0])
}
}
appPath, err := filepath.Abs(appPath)
if err != nil {
return "", errors.Wrapf(err, "could not create the absolute filepath of %s", appPath)
}
// Note: we don't use path.Dir here because it does not handle case
// which path starts with two "/" in Windows: "//psf/Home/..."
return strings.Replace(appPath, "\\", "/", -1), nil
}
func getWorkPath(appPath string) string {
workPath := ""
i := strings.LastIndex(appPath, "/")
if i == -1 {
workPath = appPath
} else {
workPath = appPath[:i]
}
// Note: we don't use path.Dir here because it does not handle case
// which path starts with two "/" in Windows: "//psf/Home/..."
return strings.Replace(workPath, "\\", "/", -1)
}