diff --git a/config.go b/config.go index 8a3425f..3e27bc1 100644 --- a/config.go +++ b/config.go @@ -3,18 +3,29 @@ package main import ( "encoding/json" "flag" - "fmt" "io/ioutil" "os" "strconv" ) type config struct { - Port string `json:"port"` - Dnt bool `json:"dnt"` - DbPath string `json:"dbPath"` - StatsUsername string `json:"statsUsername"` - StatsPassword string `json:"statsPassword"` + 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 ( @@ -31,7 +42,6 @@ func init() { parseConfigFile(appConfig) // Replace values that are set via environment vars (to make it compatible with old method) overwriteEnvVarValues(appConfig) - fmt.Println(appConfig) } func parseConfigFile(appConfig *config) { diff --git a/go.mod b/go.mod index bdd2d32..4e4a8a4 100644 --- a/go.mod +++ b/go.mod @@ -6,12 +6,15 @@ require ( github.com/go-sql-driver/mysql v1.4.1 // indirect github.com/gobuffalo/packr v1.25.0 // indirect github.com/gobuffalo/packr/v2 v2.2.0 + github.com/google/uuid v1.1.1 // indirect github.com/gorilla/handlers v1.4.0 github.com/gorilla/mux v1.7.1 + github.com/jordan-wright/email v0.0.0-20190218024454-3ea4d25e7cf8 github.com/lib/pq v1.1.0 // indirect github.com/mattn/go-sqlite3 v0.0.0-20190424093727-5994cc52dfa8 github.com/mssola/user_agent v0.5.0 github.com/rubenv/sql-migrate v0.0.0-20190327083759-54bad0a9b051 + github.com/whiteshtef/clockwork v0.0.0-20190417075149-ecf7d9abe8ec github.com/ziutek/mymysql v1.5.4 // indirect google.golang.org/appengine v1.5.0 // indirect gopkg.in/gorp.v1 v1.7.2 // indirect diff --git a/go.sum b/go.sum index 5441111..d3e23f6 100644 --- a/go.sum +++ b/go.sum @@ -24,10 +24,14 @@ github.com/gobuffalo/packr/v2 v2.2.0 h1:Ir9W9XIm9j7bhhkKE9cokvtTl1vBm62A/fene/ZC github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/handlers v1.4.0/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/jordan-wright/email v0.0.0-20190218024454-3ea4d25e7cf8 h1:XMe1IsRiRx3E3M50BhP7327VYF4A9RpCFfhHUFW+IeE= +github.com/jordan-wright/email v0.0.0-20190218024454-3ea4d25e7cf8/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A= github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -55,6 +59,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/whiteshtef/clockwork v0.0.0-20190417075149-ecf7d9abe8ec h1:4mCJZnO75zjolpdsj/ToKe7X1oLWm+JJHwS1ez8BkXY= +github.com/whiteshtef/clockwork v0.0.0-20190417075149-ecf7d9abe8ec/go.mod h1:6o8H8sci2q3QxZ4p/U88ggqZuhY3mg34+WE5BuazLsU= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= diff --git a/main.go b/main.go index 11afc25..6cc34fb 100644 --- a/main.go +++ b/main.go @@ -21,7 +21,9 @@ type kis3 struct { } var ( - app = &kis3{} + app = &kis3{ + staticBox: packr.New("staticFiles", "./static"), + } ) func init() { @@ -30,7 +32,7 @@ func init() { log.Fatal("Database setup failed:", e) } setupRouter() - app.staticBox = packr.New("staticFiles", "./static") + setupReports() } func main() { @@ -49,13 +51,13 @@ func setupRouter() { viewRouter := app.router.PathPrefix("/view").Subrouter() viewRouter.Use(corsHandler) - viewRouter.Path("").HandlerFunc(trackView) + viewRouter.Path("").HandlerFunc(TrackingHandler) - app.router.HandleFunc("/stats", requestStats) + app.router.HandleFunc("/stats", StatsHandler) staticRouter := app.router.PathPrefix("").Subrouter() staticRouter.Use(corsHandler) - staticRouter.HandleFunc("/kis3.js", serveTrackingScript) + staticRouter.HandleFunc("/kis3.js", TrackingScriptHandler) staticRouter.PathPrefix("").Handler(http.HandlerFunc(HelloResponseHandler)) } @@ -66,7 +68,7 @@ func startListening() { log.Fatal(http.ListenAndServe(addr, app.router)) } -func trackView(w http.ResponseWriter, r *http.Request) { +func TrackingHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate, max-age=0") url := r.URL.Query().Get("url") ref := r.URL.Query().Get("ref") @@ -81,7 +83,7 @@ func HelloResponseHandler(w http.ResponseWriter, _ *http.Request) { _, _ = fmt.Fprint(w, "Hello from KISSS") } -func serveTrackingScript(w http.ResponseWriter, r *http.Request) { +func TrackingScriptHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/javascript") w.Header().Set("Cache-Control", "public, max-age=432000") // 5 days filename := "kis3.js" @@ -97,7 +99,7 @@ func serveTrackingScript(w http.ResponseWriter, r *http.Request) { http.ServeContent(w, r, filename, stat.ModTime(), file) } -func requestStats(w http.ResponseWriter, r *http.Request) { +func StatsHandler(w http.ResponseWriter, r *http.Request) { // Require authentication if appConfig.statsAuth() { if !helpers.CheckAuth(w, r, appConfig.StatsUsername, appConfig.StatsPassword) { diff --git a/reports.go b/reports.go new file mode 100644 index 0000000..7eebaa5 --- /dev/null +++ b/reports.go @@ -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") + } +}