@ -1,2 +1,3 @@ | |||
.idea/ | |||
data/ | |||
config.json |
@ -1,68 +1,87 @@ | |||
package main | |||
import ( | |||
"encoding/json" | |||
"flag" | |||
"io/ioutil" | |||
"os" | |||
"strconv" | |||
) | |||
type config struct { | |||
port string | |||
dnt bool | |||
dbPath string | |||
statsAuth bool | |||
statsUsername string | |||
statsPassword string | |||
Port string `json:"port"` | |||
Dnt bool `json:"dnt"` | |||
DbPath string `json:"dbPath"` | |||
StatsUsername string `json:"statsUsername"` | |||
StatsPassword string `json:"statsPassword"` | |||
Reports []report `json:"reports"` | |||
} | |||
type report struct { | |||
Name string `json:"name"` | |||
Time string `json:"time"` | |||
To string `json:"to"` | |||
SmtpUser string `json:"smtpUser"` | |||
SmtpPassword string `json:"smtpPassword"` | |||
SmtpHost string `json:"smtpHost"` | |||
From string `json:"from"` | |||
Query string `json:"query"` | |||
} | |||
var ( | |||
appConfig = &config{} | |||
appConfig = &config{ | |||
Port: "8080", | |||
Dnt: true, | |||
DbPath: "data/kis3.db", | |||
StatsUsername: "", | |||
StatsPassword: "", | |||
} | |||
) | |||
func init() { | |||
appConfig.port = port() | |||
appConfig.dnt = dnt() | |||
appConfig.dbPath = dbPath() | |||
appConfig.statsUsername = statsUsername() | |||
appConfig.statsPassword = statsPassword() | |||
appConfig.statsAuth = statsAuth(appConfig) | |||
parseConfigFile(appConfig) | |||
// Replace values that are set via environment vars (to make it compatible with old method) | |||
overwriteEnvVarValues(appConfig) | |||
} | |||
func port() string { | |||
port := os.Getenv("PORT") | |||
if len(port) != 0 { | |||
return port | |||
} else { | |||
return "8080" | |||
} | |||
} | |||
func dnt() bool { | |||
dnt := os.Getenv("DNT") | |||
dntBool, e := strconv.ParseBool(dnt) | |||
func parseConfigFile(appConfig *config) { | |||
configFile := flag.String("c", "config.json", "Config file") | |||
flag.Parse() | |||
configJson, e := ioutil.ReadFile(*configFile) | |||
if e != nil { | |||
dntBool = true | |||
return | |||
} | |||
return dntBool | |||
} | |||
func dbPath() (dbPath string) { | |||
dbPath = os.Getenv("DB_PATH") | |||
if len(dbPath) == 0 { | |||
dbPath = "data/kis3.db" | |||
e = json.Unmarshal([]byte(configJson), appConfig) | |||
if e != nil { | |||
return | |||
} | |||
return | |||
} | |||
func statsUsername() (username string) { | |||
username = os.Getenv("STATS_USERNAME") | |||
return | |||
} | |||
func statsPassword() (password string) { | |||
password = os.Getenv("STATS_PASSWORD") | |||
return | |||
func overwriteEnvVarValues(appConfig *config) { | |||
port, set := os.LookupEnv("PORT") | |||
if set { | |||
appConfig.Port = port | |||
} | |||
dntString, set := os.LookupEnv("DNT") | |||
dntBool, e := strconv.ParseBool(dntString) | |||
if set && e == nil { | |||
appConfig.Dnt = dntBool | |||
} | |||
dbPath, set := os.LookupEnv("DB_PATH") | |||
if set { | |||
appConfig.DbPath = dbPath | |||
} | |||
username, set := os.LookupEnv("STATS_USERNAME") | |||
if set { | |||
appConfig.StatsUsername = username | |||
} | |||
password, set := os.LookupEnv("STATS_PASSWORD") | |||
if set { | |||
appConfig.StatsPassword = password | |||
} | |||
} | |||
func statsAuth(ac *config) bool { | |||
return len(ac.statsUsername) > 0 && len(ac.statsPassword) > 0 | |||
func (ac *config) statsAuth() bool { | |||
return len(ac.StatsUsername) > 0 && len(ac.StatsPassword) > 0 | |||
} |
@ -1,131 +1,32 @@ | |||
package main | |||
import ( | |||
"os" | |||
"testing" | |||
) | |||
func Test_port(t *testing.T) { | |||
tests := []struct { | |||
name string | |||
envVar string | |||
want string | |||
}{ | |||
{name: "default", envVar: "", want: "8080"}, | |||
{name: "custom", envVar: "1234", want: "1234"}, | |||
func Test_config_statsAuth(t *testing.T) { | |||
type fields struct { | |||
StatsUsername string | |||
StatsPassword string | |||
} | |||
for _, tt := range tests { | |||
t.Run(tt.name, func(t *testing.T) { | |||
_ = os.Setenv("PORT", tt.envVar) | |||
if got := port(); got != tt.want { | |||
t.Errorf("port() = %v, want %v", got, tt.want) | |||
} | |||
}) | |||
} | |||
} | |||
func Test_dnt(t *testing.T) { | |||
tests := []struct { | |||
name string | |||
envVar string | |||
fields fields | |||
want bool | |||
}{ | |||
{name: "default", envVar: "", want: true}, | |||
{envVar: "true", want: true}, | |||
{envVar: "t", want: true}, | |||
{envVar: "TRUE", want: true}, | |||
{envVar: "1", want: true}, | |||
{envVar: "false", want: false}, | |||
{envVar: "f", want: false}, | |||
{envVar: "0", want: false}, | |||
{envVar: "abc", want: true}, | |||
} | |||
for _, tt := range tests { | |||
t.Run(tt.name, func(t *testing.T) { | |||
_ = os.Setenv("DNT", tt.envVar) | |||
if got := dnt(); got != tt.want { | |||
t.Errorf("dnt() = %v, want %v", got, tt.want) | |||
} | |||
}) | |||
} | |||
} | |||
func Test_dbPath(t *testing.T) { | |||
tests := []struct { | |||
name string | |||
envVar string | |||
wantDbPath string | |||
}{ | |||
{name: "default", envVar: "", wantDbPath: "data/kis3.db"}, | |||
{envVar: "kis3.db", wantDbPath: "kis3.db"}, | |||
{envVar: "data.db", wantDbPath: "data.db"}, | |||
} | |||
for _, tt := range tests { | |||
t.Run(tt.name, func(t *testing.T) { | |||
_ = os.Setenv("DB_PATH", tt.envVar) | |||
if gotDbPath := dbPath(); gotDbPath != tt.wantDbPath { | |||
t.Errorf("dbPath() = %v, want %v", gotDbPath, tt.wantDbPath) | |||
} | |||
}) | |||
} | |||
} | |||
func Test_statsUsername(t *testing.T) { | |||
tests := []struct { | |||
name string | |||
envVar string | |||
wantUsername string | |||
}{ | |||
{name: "default", envVar: "", wantUsername: ""}, | |||
{envVar: "abc", wantUsername: "abc"}, | |||
{"No username nor password", fields{"", ""}, false}, | |||
{"Only username", fields{"abc", ""}, false}, | |||
{"Only password", fields{"", "abc"}, false}, | |||
{"Username and password", fields{"abc", "abc"}, true}, | |||
} | |||
for _, tt := range tests { | |||
t.Run(tt.name, func(t *testing.T) { | |||
_ = os.Setenv("STATS_USERNAME", tt.envVar) | |||
if gotUsername := statsUsername(); gotUsername != tt.wantUsername { | |||
t.Errorf("statsUsername() = %v, want %v", gotUsername, tt.wantUsername) | |||
ac := &config{ | |||
StatsUsername: tt.fields.StatsUsername, | |||
StatsPassword: tt.fields.StatsPassword, | |||
} | |||
}) | |||
} | |||
} | |||
func Test_statsPassword(t *testing.T) { | |||
tests := []struct { | |||
name string | |||
envVar string | |||
wantPassword string | |||
}{ | |||
{name: "default", envVar: "", wantPassword: ""}, | |||
{envVar: "def", wantPassword: "def"}, | |||
} | |||
for _, tt := range tests { | |||
t.Run(tt.name, func(t *testing.T) { | |||
_ = os.Setenv("STATS_PASSWORD", tt.envVar) | |||
if gotPassword := statsPassword(); gotPassword != tt.wantPassword { | |||
t.Errorf("statsPassword() = %v, want %v", gotPassword, tt.wantPassword) | |||
} | |||
}) | |||
} | |||
} | |||
func Test_statsAuth(t *testing.T) { | |||
type args struct { | |||
ac *config | |||
} | |||
tests := []struct { | |||
name string | |||
args args | |||
want bool | |||
}{ | |||
{name: "default", args: struct{ ac *config }{ac: &config{}}, want: false}, | |||
{name: "only username set", args: struct{ ac *config }{ac: &config{statsUsername: "abc"}}, want: false}, | |||
{name: "only password set", args: struct{ ac *config }{ac: &config{statsPassword: "def"}}, want: false}, | |||
{name: "username and password set", args: struct{ ac *config }{ac: &config{statsUsername: "abc", statsPassword: "def"}}, want: true}, | |||
} | |||
for _, tt := range tests { | |||
t.Run(tt.name, func(t *testing.T) { | |||
if got := statsAuth(tt.args.ac); got != tt.want { | |||
t.Errorf("statsAuth() = %v, want %v", got, tt.want) | |||
if got := ac.statsAuth(); got != tt.want { | |||
t.Errorf("config.statsAuth() = %v, want %v", got, tt.want) | |||
} | |||
}) | |||
} | |||
@ -0,0 +1,58 @@ | |||
package main | |||
import ( | |||
"fmt" | |||
"github.com/jordan-wright/email" | |||
"github.com/whiteshtef/clockwork" | |||
"io/ioutil" | |||
"net" | |||
"net/http" | |||
"net/smtp" | |||
) | |||
func setupReports() { | |||
scheduler := clockwork.NewScheduler() | |||
for _, r := range appConfig.Reports { | |||
scheduler.Schedule().Every().Day().At(r.Time).Do(func() { | |||
executeReport(&r) | |||
}) | |||
} | |||
go scheduler.Run() | |||
} | |||
func executeReport(r *report) { | |||
fmt.Println("Execute report:", r.Name) | |||
req, e := http.NewRequest("GET", "http://localhost:"+appConfig.Port+"/stats?"+r.Query, nil) | |||
if e != nil { | |||
fmt.Println("Executing report failed:", e) | |||
return | |||
} | |||
req.SetBasicAuth(appConfig.StatsUsername, appConfig.StatsPassword) | |||
res, e := http.DefaultClient.Do(req) | |||
if e != nil { | |||
fmt.Println("Executing report failed:", e) | |||
return | |||
} | |||
body, e := ioutil.ReadAll(res.Body) | |||
if e != nil { | |||
fmt.Println("Executing report failed:", e) | |||
return | |||
} | |||
sendMail(r, body) | |||
} | |||
func sendMail(r *report, content []byte) { | |||
smtpHostNoPort, _, _ := net.SplitHostPort(r.SmtpHost) | |||
mail := email.NewEmail() | |||
mail.From = r.From | |||
mail.To = []string{r.To} | |||
mail.Subject = "KISSS report: " + r.Name | |||
mail.Text = content | |||
e := mail.Send(r.SmtpHost, smtp.PlainAuth("", r.SmtpUser, r.SmtpPassword, smtpHostNoPort)) | |||
if e != nil { | |||
fmt.Println("Sending report failed:", e) | |||
return | |||
} else { | |||
fmt.Println("Report sent") | |||
} | |||
} |