diff --git a/.gitignore b/.gitignore index da7e1961..68fa2d42 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ .idea -rice-box.go +./rice-box.go config.yml statup.db plugins/*.so diff --git a/.travis.yml b/.travis.yml index fcc62ea1..2bba6816 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ services: env: global: - - VERSION=0.29.9 + - VERSION=0.30 - DB_HOST=localhost - DB_USER=travis - DB_PASS= diff --git a/cli.go b/cli.go index cef480c8..38918577 100644 --- a/cli.go +++ b/cli.go @@ -4,7 +4,7 @@ import ( "encoding/json" "fmt" "github.com/hunterlong/statup/core" - "github.com/hunterlong/statup/plugin" + "github.com/hunterlong/statup/types" "github.com/hunterlong/statup/utils" "github.com/joho/godotenv" "io/ioutil" @@ -106,7 +106,7 @@ func RunOnce() { utils.Log(4, err) } for _, s := range core.CoreApp.Services { - out := s.Check() + out := core.ServiceCheck(s.ToService()) fmt.Printf(" Service %v | URL: %v | Latency: %0.0fms | Online: %v\n", out.Name, out.Domain, (out.Latency * 1000), out.Online) } } @@ -127,7 +127,7 @@ func HelpEcho() { fmt.Println("Give Statup a Star at https://github.com/hunterlong/statup") } -func TestPlugin(plug plugin.PluginActions) { +func TestPlugin(plug types.PluginActions) { defer utils.DeleteFile("./.plugin_test.db") RenderBoxes() @@ -148,41 +148,41 @@ func TestPlugin(plug plugin.PluginActions) { core.OnLoad(core.DbSession) fmt.Println("\n" + BRAKER) fmt.Println(POINT + "Sending 'OnSuccess(Service)'") - core.OnSuccess(core.SelectService(1)) + core.OnSuccess(core.SelectService(1).ToService()) fmt.Println("\n" + BRAKER) fmt.Println(POINT + "Sending 'OnFailure(Service, FailureData)'") fakeFailD := core.FailureData{ Issue: "No issue, just testing this plugin. This would include HTTP failure information though", } - core.OnFailure(core.SelectService(1), fakeFailD) + core.OnFailure(core.SelectService(1).ToService(), fakeFailD) fmt.Println("\n" + BRAKER) fmt.Println(POINT + "Sending 'OnSettingsSaved(Core)'") fmt.Println(BRAKER) - core.OnSettingsSaved(core.CoreApp) + core.OnSettingsSaved(core.CoreApp.ToCore()) fmt.Println("\n" + BRAKER) fmt.Println(POINT + "Sending 'OnNewService(Service)'") - core.OnNewService(core.SelectService(2)) + core.OnNewService(core.SelectService(2).ToService()) fmt.Println("\n" + BRAKER) fmt.Println(POINT + "Sending 'OnNewUser(User)'") user, _ := core.SelectUser(1) core.OnNewUser(user) fmt.Println("\n" + BRAKER) fmt.Println(POINT + "Sending 'OnUpdateService(Service)'") - srv := core.SelectService(2) + srv := core.SelectService(2).ToService() srv.Type = "http" srv.Domain = "https://yahoo.com" core.OnUpdateService(srv) fmt.Println("\n" + BRAKER) fmt.Println(POINT + "Sending 'OnDeletedService(Service)'") - core.OnDeletedService(core.SelectService(1)) + core.OnDeletedService(core.SelectService(1).ToService()) fmt.Println("\n" + BRAKER) } -func FakeSeed(plug plugin.PluginActions) { +func FakeSeed(plug types.PluginActions) { var err error core.CoreApp = core.NewCore() - core.CoreApp.AllPlugins = []plugin.PluginActions{plug} + core.CoreApp.AllPlugins = []types.PluginActions{plug} fmt.Printf("\n" + BRAKER) @@ -212,21 +212,21 @@ func FakeSeed(plug plugin.PluginActions) { core.CoreApp.ApiSecret = "0x0x0x0x0" core.CoreApp.ApiKey = "abcdefg12345" - fakeSrv := &core.Service{ + fakeSrv := &types.Service{ Name: "Test Plugin Service", Domain: "https://google.com", Method: "GET", } - fakeSrv.Create() + core.CreateService(fakeSrv) - fakeSrv2 := &core.Service{ + fakeSrv2 := &types.Service{ Name: "Awesome Plugin Service", Domain: "https://netflix.com", Method: "GET", } - fakeSrv2.Create() + core.CreateService(fakeSrv2) - fakeUser := &core.User{ + fakeUser := &types.User{ Id: 6334, Username: "Bulbasaur", Password: "$2a$14$NzT/fLdE3f9iB1Eux2C84O6ZoPhI4NfY0Ke32qllCFo8pMTkUPZzy", @@ -234,35 +234,37 @@ func FakeSeed(plug plugin.PluginActions) { Admin: true, CreatedAt: time.Now(), } - fakeUser.Create() + core.CreateUser(fakeUser) - fakeUser = &core.User{ + fakeUser = &types.User{ Id: 6335, Username: "Billy", Password: "$2a$14$NzT/fLdE3f9iB1Eux2C84O6ZoPhI4NfY0Ke32qllCFo8pMTkUPZzy", Email: "info@awesome.com", CreatedAt: time.Now(), } - fakeUser.Create() + core.CreateUser(fakeUser) for i := 0; i <= 50; i++ { dd := core.HitData{ Latency: rand.Float64(), } - fakeSrv.CreateHit(dd) + core.CreateServiceHit(fakeSrv, dd) + dd = core.HitData{ Latency: rand.Float64(), } - fakeSrv2.CreateHit(dd) + core.CreateServiceHit(fakeSrv2, dd) + fail := core.FailureData{ Issue: "This is not an issue, but it would container HTTP response errors.", } - fakeSrv.CreateFailure(fail) + core.CreateServiceFailure(fakeSrv, fail) fail = core.FailureData{ Issue: "HTTP Status Code 521 did not match 200", } - fakeSrv2.CreateFailure(fail) + core.CreateServiceFailure(fakeSrv, fail) } fmt.Println("Seeding example data is complete, running Plugin Tests") diff --git a/core/assets.go b/core/assets.go index fa7d1c99..fbaa6597 100644 --- a/core/assets.go +++ b/core/assets.go @@ -95,8 +95,8 @@ func CreateAllAssets() { utils.Log(1, "Inserting scss, css, emails, and javascript files into assets..") CopyToPublic(ScssBox, "scss", "base.scss") CopyToPublic(ScssBox, "scss", "variables.scss") - CopyToPublic(EmailBox, "emails", "message.html") - CopyToPublic(EmailBox, "emails", "failure.html") + //CopyToPublic(EmailBox, "emails", "message.html") + //CopyToPublic(EmailBox, "emails", "failure.html") CopyToPublic(CssBox, "css", "bootstrap.min.css") CopyToPublic(JsBox, "js", "bootstrap.min.js") CopyToPublic(JsBox, "js", "Chart.bundle.min.js") diff --git a/core/checker.go b/core/checker.go index 976516e7..68ad41c6 100644 --- a/core/checker.go +++ b/core/checker.go @@ -18,21 +18,21 @@ type FailureData types.FailureData func CheckServices() { CoreApp.Services, _ = SelectAllServices() utils.Log(1, fmt.Sprintf("Starting monitoring process for %v Services", len(CoreApp.Services))) - for _, v := range CoreApp.Services { - obj := v + for _, ser := range CoreApp.Services { + s := ser.ToService() //go obj.StartCheckins() - obj.stopRoutine = make(chan struct{}) - go obj.CheckQueue() + s.StopRoutine = make(chan struct{}) + go CheckQueue(s) } } -func (s *Service) CheckQueue() { +func CheckQueue(s *types.Service) { for { select { - case <-s.stopRoutine: + case <-s.StopRoutine: return default: - s.Check() + ServiceCheck(s) if s.Interval < 1 { s.Interval = 1 } @@ -43,7 +43,7 @@ func (s *Service) CheckQueue() { } } -func (s *Service) DNSCheck() (float64, error) { +func DNSCheck(s *types.Service) (float64, error) { t1 := time.Now() url, err := url.Parse(s.Domain) if err != nil { @@ -58,13 +58,13 @@ func (s *Service) DNSCheck() (float64, error) { return subTime, err } -func (s *Service) Check() *Service { - dnsLookup, err := s.DNSCheck() +func ServiceCheck(s *types.Service) *types.Service { + dnsLookup, err := DNSCheck(s) if err != nil { - s.Failure(fmt.Sprintf("Could not get IP address for domain %v, %v", s.Domain, err)) + RecordFailure(s, fmt.Sprintf("Could not get IP address for domain %v, %v", s.Domain, err)) return s } - s.dnsLookup = dnsLookup + s.DnsLookup = dnsLookup t1 := time.Now() client := http.Client{ Timeout: 30 * time.Second, @@ -77,14 +77,14 @@ func (s *Service) Check() *Service { response, err = client.Get(s.Domain) } if err != nil { - s.Failure(fmt.Sprintf("HTTP Error %v", err)) + RecordFailure(s, fmt.Sprintf("HTTP Error %v", err)) return s } response.Header.Set("User-Agent", "StatupMonitor") t2 := time.Now() s.Latency = t2.Sub(t1).Seconds() if err != nil { - s.Failure(fmt.Sprintf("HTTP Error %v", err)) + RecordFailure(s, fmt.Sprintf("HTTP Error %v", err)) return s } defer response.Body.Close() @@ -100,20 +100,20 @@ func (s *Service) Check() *Service { if !match { s.LastResponse = string(contents) s.LastStatusCode = response.StatusCode - s.Failure(fmt.Sprintf("HTTP Response Body did not match '%v'", s.Expected)) + RecordFailure(s, fmt.Sprintf("HTTP Response Body did not match '%v'", s.Expected)) return s } } if s.ExpectedStatus != response.StatusCode { s.LastResponse = string(contents) s.LastStatusCode = response.StatusCode - s.Failure(fmt.Sprintf("HTTP Status Code %v did not match %v", response.StatusCode, s.ExpectedStatus)) + RecordFailure(s, fmt.Sprintf("HTTP Status Code %v did not match %v", response.StatusCode, s.ExpectedStatus)) return s } s.LastResponse = string(contents) s.LastStatusCode = response.StatusCode s.Online = true - s.Record(response) + RecordSuccess(s, response) return s } @@ -121,23 +121,23 @@ type HitData struct { Latency float64 } -func (s *Service) Record(response *http.Response) { +func RecordSuccess(s *types.Service, response *http.Response) { s.Online = true s.LastOnline = time.Now() data := HitData{ Latency: s.Latency, } - s.CreateHit(data) + CreateServiceHit(s, data) OnSuccess(s) } -func (s *Service) Failure(issue string) { +func RecordFailure(s *types.Service, issue string) { s.Online = false data := FailureData{ Issue: issue, } utils.Log(2, fmt.Sprintf("Service %v Failing: %v", s.Name, issue)) - s.CreateFailure(data) + CreateServiceFailure(s, data) //SendFailureEmail(s) OnFailure(s, data) } diff --git a/core/checkin.go b/core/checkin.go index 0aa6ef70..8b0721ec 100644 --- a/core/checkin.go +++ b/core/checkin.go @@ -14,9 +14,9 @@ func (c *Checkin) String() string { return c.Api } -func FindCheckin(api string) *Checkin { - for _, s := range CoreApp.Services { - for _, c := range s.Checkins { +func FindCheckin(api string) *types.Checkin { + for _, ser := range CoreApp.Services { + for _, c := range ser.ToService().Checkins { if c.Api == api { return c } @@ -25,8 +25,8 @@ func FindCheckin(api string) *Checkin { return nil } -func (s *Service) SelectAllCheckins() []*Checkin { - var checkins []*Checkin +func SelectAllCheckins(s *types.Service) []*types.Checkin { + var checkins []*types.Checkin col := DbSession.Collection("checkins").Find("service", s.Id).OrderBy("-id") col.All(&checkins) s.Checkins = checkins @@ -78,33 +78,33 @@ func (f *Checkin) Ago() string { return got } -func (c *Checkin) Run() { - if c.Interval == 0 { - return - } - fmt.Println("checking: ", c.Api) - between := time.Now().Sub(c.Last).Seconds() - if between > float64(c.Interval) { - guard := make(chan struct{}) - c.RecheckCheckinFailure(guard) - <-guard - } - time.Sleep(1 * time.Second) - c.Run() -} - -func (s *Service) StartCheckins() { - for _, c := range s.Checkins { - checkin := c - go checkin.Run() - } -} - -func CheckinProcess() { - for _, s := range CoreApp.Services { - for _, c := range s.Checkins { - checkin := c - go checkin.Run() - } - } -} +//func (c *Checkin) Run() { +// if c.Interval == 0 { +// return +// } +// fmt.Println("checking: ", c.Api) +// between := time.Now().Sub(c.Last).Seconds() +// if between > float64(c.Interval) { +// guard := make(chan struct{}) +// c.RecheckCheckinFailure(guard) +// <-guard +// } +// time.Sleep(1 * time.Second) +// c.Run() +//} +// +//func (s *Service) StartCheckins() { +// for _, c := range s.Checkins { +// checkin := c.(*Checkin) +// go checkin.Run() +// } +//} +// +//func CheckinProcess() { +// for _, s := range CoreApp.Services { +// for _, c := range s.Checkins { +// checkin := c +// go checkin.Run() +// } +// } +//} diff --git a/core/configs.go b/core/configs.go index 5229bac8..e80d81de 100644 --- a/core/configs.go +++ b/core/configs.go @@ -14,7 +14,7 @@ import ( func LoadConfig() (*types.Config, error) { if os.Getenv("DB_CONN") != "" { utils.Log(1, "DB_CONN environment variable was found, sleeping for 30 seconds") - time.Sleep(30 * time.Second) + //time.Sleep(30 * time.Second) return LoadUsingEnv() } Configs = new(types.Config) @@ -85,7 +85,7 @@ func LoadUsingEnv() (*types.Config, error) { DropDatabase() CreateDatabase() - CoreApp = &Core{ + CoreApp = &Core{Core: &types.Core{ Name: dbConfig.Project, Description: dbConfig.Description, Config: "config.yml", @@ -93,22 +93,22 @@ func LoadUsingEnv() (*types.Config, error) { ApiSecret: utils.NewSHA1Hash(16), Domain: dbConfig.Domain, MigrationId: time.Now().Unix(), - } + }} CoreApp.DbConnection = dbConfig.DbConn - err := CoreApp.Insert() + err := InsertCore(CoreApp) if err != nil { utils.Log(3, err) } - admin := &User{ + admin := &types.User{ Username: "admin", Password: "admin", Email: "info@admin.com", Admin: true, } - admin.Create() + CreateUser(admin) LoadSampleData() diff --git a/core/core.go b/core/core.go index 84bec322..947cb9e7 100644 --- a/core/core.go +++ b/core/core.go @@ -3,7 +3,6 @@ package core import ( "github.com/GeertJohan/go.rice" "github.com/hunterlong/statup/notifiers" - "github.com/hunterlong/statup/plugin" "github.com/hunterlong/statup/types" "github.com/pkg/errors" "os" @@ -14,24 +13,8 @@ type PluginJSON types.PluginJSON type PluginRepos types.PluginRepos type Core struct { - Name string `db:"name" json:"name"` - Description string `db:"description" json:"name"` - Config string `db:"config" json:"-"` - ApiKey string `db:"api_key" json:"-"` - ApiSecret string `db:"api_secret" json:"-"` - Style string `db:"style" json:"-"` - Footer string `db:"footer" json:"-"` - Domain string `db:"domain" json:"domain,omitempty"` - Version string `db:"version" json:"version,omitempty"` - MigrationId int64 `db:"migration_id" json:"-"` - UseCdn bool `db:"use_cdn" json:"-"` - Services []*Service `json:"services,omitempty"` - Plugins []plugin.Info - Repos []PluginJSON - AllPlugins []plugin.PluginActions - Communications []notifiers.AllNotifiers - DbConnection string - started time.Time + *types.Core + Services []*Service } var ( @@ -42,7 +25,6 @@ var ( ScssBox *rice.Box JsBox *rice.Box TmplBox *rice.Box - EmailBox *rice.Box SetupMode bool UsingAssets bool VERSION string @@ -54,16 +36,21 @@ func init() { func NewCore() *Core { CoreApp = new(Core) - CoreApp.started = time.Now() + CoreApp.Core = new(types.Core) + CoreApp.Started = time.Now() return CoreApp } -func (c *Core) Insert() error { +func InsertCore(c *Core) error { col := DbSession.Collection("core") - _, err := col.Insert(c) + _, err := col.Insert(c.Core) return err } +func (c *Core) ToCore() *types.Core { + return c.Core +} + func InitApp() { SelectCore() notifiers.Collections = DbSession.Collection("communication") @@ -73,9 +60,9 @@ func InitApp() { go DatabaseMaintence() } -func (c *Core) Update() (*Core, error) { +func UpdateCore(c *Core) (*Core, error) { res := DbSession.Collection("core").Find().Limit(1) - err := res.Update(c) + err := res.Update(c.Core) return c, err } @@ -105,7 +92,8 @@ func (c Core) MobileSASS() string { } func (c Core) AllOnline() bool { - for _, s := range CoreApp.Services { + for _, ser := range CoreApp.Services { + s := ser.ToService() if !s.Online { return false } @@ -114,7 +102,7 @@ func (c Core) AllOnline() bool { } func SelectLastMigration() (int64, error) { - var c *Core + var c *types.Core if DbSession == nil { return 0, errors.New("Database connection has not been created yet") } @@ -126,17 +114,16 @@ func SelectLastMigration() (int64, error) { } func SelectCore() (*Core, error) { - var c *Core + var c *types.Core exists := DbSession.Collection("core").Exists() if !exists { return nil, errors.New("core database has not been setup yet.") } - err := DbSession.Collection("core").Find().One(&c) if err != nil { return nil, err } - CoreApp = c + CoreApp.Core = c CoreApp.DbConnection = Configs.Connection CoreApp.Version = VERSION CoreApp.Services, _ = SelectAllServices() diff --git a/core/database.go b/core/database.go index 1aecfc01..4adf195b 100644 --- a/core/database.go +++ b/core/database.go @@ -119,7 +119,7 @@ func (c *DbConfig) Save() error { DropDatabase() CreateDatabase() - newCore := &Core{ + newCore := &types.Core{ Name: c.Project, Description: c.Description, Config: "config.yml", @@ -131,7 +131,7 @@ func (c *DbConfig) Save() error { col := DbSession.Collection("core") _, err = col.Insert(newCore) if err == nil { - CoreApp = newCore + CoreApp = &Core{Core: newCore} } CoreApp, err = SelectCore() @@ -204,7 +204,7 @@ func RunDatabaseUpgrades() error { panic(err) } CoreApp.MigrationId = currentMigration - CoreApp.Update() + UpdateCore(CoreApp) } return err } diff --git a/core/events.go b/core/events.go index df27fcc0..2cfe3216 100644 --- a/core/events.go +++ b/core/events.go @@ -3,7 +3,7 @@ package core import ( "github.com/fatih/structs" "github.com/hunterlong/statup/notifiers" - "github.com/hunterlong/statup/plugin" + "github.com/hunterlong/statup/types" "upper.io/db.v3/lib/sqlbuilder" ) @@ -13,55 +13,55 @@ func OnLoad(db sqlbuilder.Database) { } } -func OnSuccess(s *Service) { +func OnSuccess(s *types.Service) { for _, p := range CoreApp.AllPlugins { p.OnSuccess(structs.Map(s)) } - notifiers.OnSuccess() + notifiers.OnSuccess(s) } -func OnFailure(s *Service, f FailureData) { +func OnFailure(s *types.Service, f FailureData) { for _, p := range CoreApp.AllPlugins { p.OnFailure(structs.Map(s)) } - notifiers.OnFailure() + notifiers.OnFailure(s) } -func OnSettingsSaved(c *Core) { +func OnSettingsSaved(c *types.Core) { for _, p := range CoreApp.AllPlugins { p.OnSettingsSaved(structs.Map(c)) } } -func OnNewUser(u *User) { +func OnNewUser(u *types.User) { for _, p := range CoreApp.AllPlugins { p.OnNewUser(structs.Map(u)) } } -func OnNewService(s *Service) { +func OnNewService(s *types.Service) { for _, p := range CoreApp.AllPlugins { p.OnNewService(structs.Map(s)) } } -func OnDeletedService(s *Service) { +func OnDeletedService(s *types.Service) { for _, p := range CoreApp.AllPlugins { p.OnDeletedService(structs.Map(s)) } } -func OnUpdateService(s *Service) { +func OnUpdateService(s *types.Service) { for _, p := range CoreApp.AllPlugins { p.OnUpdatedService(structs.Map(s)) } } -func SelectPlugin(name string) plugin.PluginActions { +func SelectPlugin(name string) types.PluginActions { for _, p := range CoreApp.AllPlugins { if p.GetInfo().Name == name { return p } } - return plugin.PluginInfo{} + return types.PluginInfo{} } diff --git a/core/failures.go b/core/failures.go index bb3fdb48..b25cbbe2 100644 --- a/core/failures.go +++ b/core/failures.go @@ -3,13 +3,14 @@ package core import ( "fmt" "github.com/ararog/timeago" + "github.com/hunterlong/statup/types" "github.com/hunterlong/statup/utils" "strings" "time" ) -func (s *Service) CreateFailure(data FailureData) (int64, error) { - fail := &Failure{ +func CreateServiceFailure(s *types.Service, data FailureData) (int64, error) { + fail := &types.Failure{ Issue: data.Issue, Service: s.Id, CreatedAt: time.Now(), @@ -26,8 +27,8 @@ func (s *Service) CreateFailure(data FailureData) (int64, error) { return uuid.(int64), err } -func (s *Service) SelectAllFailures() []*Failure { - var fails []*Failure +func SelectAllFailures(s *types.Service) []*types.Failure { + var fails []*types.Failure col := DbSession.Collection("failures").Find("service", s.Id).OrderBy("-id") err := col.All(&fails) if err != nil { @@ -36,7 +37,7 @@ func (s *Service) SelectAllFailures() []*Failure { return fails } -func (u *Service) DeleteFailures() { +func DeleteFailures(u *types.Service) { var fails []*Failure col := DbSession.Collection("failures") col.Find("service", u.Id).All(&fails) @@ -45,26 +46,33 @@ func (u *Service) DeleteFailures() { } } -func (s *Service) LimitedFailures() []*Failure { - var fails []*Failure +func (ser *Service) LimitedFailures() []*Failure { + s := ser.ToService() + var fails []*types.Failure + var failArr []*Failure col := DbSession.Collection("failures").Find("service", s.Id).OrderBy("-id").Limit(10) col.All(&fails) - return fails + for _, f := range fails { + failArr = append(failArr, MakeFailure(f)) + } + return failArr } -func reverseFailures(input []*Failure) []*Failure { +func reverseFailures(input []*types.Failure) []*types.Failure { if len(input) == 0 { return input } return append(reverseFailures(input[1:]), input[0]) } -func (f *Failure) Ago() string { +func (fail *Failure) Ago() string { + f := fail.ToFailure() got, _ := timeago.TimeAgoWithTime(time.Now(), f.CreatedAt) return got } -func (f *Failure) Delete() error { +func (fail *Failure) Delete() error { + f := fail.ToFailure() col := DbSession.Collection("failures").Find("id", f.Id) return col.Delete() } @@ -79,19 +87,31 @@ func CountFailures() uint64 { return amount } -func (s *Service) TotalFailures() (uint64, error) { +func (ser *Service) TotalFailures() (uint64, error) { + s := ser.ToService() col := DbSession.Collection("failures").Find("service", s.Id) amount, err := col.Count() return amount, err } -func (s *Service) TotalFailures24Hours() (uint64, error) { +func (ser *Service) TotalFailures24Hours() (uint64, error) { + s := ser.ToService() col := DbSession.Collection("failures").Find("service", s.Id) amount, err := col.Count() return amount, err } -func (f *Failure) ParseError() string { +func (f *Failure) ToFailure() *types.Failure { + return f.F.(*types.Failure) +} + +func MakeFailure(f *types.Failure) *Failure { + fail := &Failure{f} + return fail +} + +func (fail *Failure) ParseError() string { + f := fail.ToFailure() err := strings.Contains(f.Issue, "operation timed out") if err { return fmt.Sprintf("HTTP Request Timed Out") diff --git a/core/hits.go b/core/hits.go index c2d94cd1..38a9f092 100644 --- a/core/hits.go +++ b/core/hits.go @@ -13,7 +13,7 @@ func hitCol() db.Collection { return DbSession.Collection("hits") } -func (s *Service) CreateHit(d HitData) (int64, error) { +func CreateServiceHit(s *types.Service, d HitData) (int64, error) { h := Hit{ Service: s.Id, Latency: d.Latency, @@ -27,14 +27,16 @@ func (s *Service) CreateHit(d HitData) (int64, error) { return uuid.(int64), err } -func (s *Service) Hits() ([]Hit, error) { +func (ser *Service) Hits() ([]Hit, error) { + s := ser.ToService() var hits []Hit col := hitCol().Find("service", s.Id).OrderBy("-id") err := col.All(&hits) return hits, err } -func (s *Service) LimitedHits() ([]*Hit, error) { +func (ser *Service) LimitedHits() ([]*Hit, error) { + s := ser.ToService() var hits []*Hit col := hitCol().Find("service", s.Id).OrderBy("-id").Limit(1024) err := col.All(&hits) @@ -48,14 +50,16 @@ func reverseHits(input []*Hit) []*Hit { return append(reverseHits(input[1:]), input[0]) } -func (s *Service) SelectHitsGroupBy(group string) ([]Hit, error) { +func (ser *Service) SelectHitsGroupBy(group string) ([]Hit, error) { + s := ser.ToService() var hits []Hit col := hitCol().Find("service", s.Id) err := col.All(&hits) return hits, err } -func (s *Service) TotalHits() (uint64, error) { +func (ser *Service) TotalHits() (uint64, error) { + s := ser.ToService() col := hitCol().Find("service", s.Id) amount, err := col.Count() return amount, err diff --git a/core/services.go b/core/services.go index d763ea99..292b6449 100644 --- a/core/services.go +++ b/core/services.go @@ -10,33 +10,12 @@ import ( "upper.io/db.v3" ) -type Failure types.Failure - type Service struct { - Id int64 `db:"id,omitempty" json:"id"` - Name string `db:"name" json:"name"` - Domain string `db:"domain" json:"domain"` - Expected string `db:"expected" json:"expected"` - ExpectedStatus int `db:"expected_status" json:"expected_status"` - Interval int `db:"check_interval" json:"check_interval"` - Type string `db:"check_type" json:"type"` - Method string `db:"method" json:"method"` - PostData string `db:"post_data" json:"post_data"` - Port int `db:"port" json:"port"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - Online bool `json:"online"` - Latency float64 `json:"latency"` - Online24Hours float32 `json:"24_hours_online"` - AvgResponse string `json:"avg_response"` - TotalUptime string `json:"uptime"` - OrderId int64 `json:"order_id"` - Failures []*Failure `json:"failures"` - Checkins []*Checkin `json:"checkins"` - stopRoutine chan struct{} - LastResponse string - LastStatusCode int - LastOnline time.Time - dnsLookup float64 `json:"dns_lookup_time"` + s *types.Service +} + +type Failure struct { + F interface{} } func serviceCol() db.Collection { @@ -45,27 +24,31 @@ func serviceCol() db.Collection { func SelectService(id int64) *Service { for _, s := range CoreApp.Services { - if s.Id == id { - return s + ser := s.ToService() + if ser.Id == id { + return &Service{ser} } } return nil } func SelectAllServices() ([]*Service, error) { - var srvcs []*Service + var services []*types.Service + var sers []*Service col := serviceCol().Find() - err := col.All(&srvcs) + err := col.All(&services) if err != nil { utils.Log(3, err) return nil, err } - for _, s := range srvcs { - s.Checkins = s.SelectAllCheckins() - s.Failures = s.SelectAllFailures() + for _, s := range services { + ser := NewService(s) + sers = append(sers, ser) + s.Checkins = SelectAllCheckins(s) + s.Failures = SelectAllFailures(s) } - CoreApp.Services = srvcs - return srvcs, err + CoreApp.Services = sers + return sers, err } func (s *Service) AvgTime() float64 { @@ -80,9 +63,10 @@ func (s *Service) AvgTime() float64 { return val } -func (s *Service) Online24() float32 { - total, _ := s.TotalHits() - failed, _ := s.TotalFailures24Hours() +func (ser *Service) Online24() float32 { + s := ser.ToService() + total, _ := ser.TotalHits() + failed, _ := ser.TotalFailures24Hours() if failed == 0 { s.Online24Hours = 100.00 return s.Online24Hours @@ -106,12 +90,22 @@ type DateScan struct { Value int64 `json:"y"` } -func (s *Service) SmallText() string { - last := s.LimitedFailures() - hits, _ := s.LimitedHits() +func (s *Service) ToService() *types.Service { + return s.s +} + +func NewService(s *types.Service) *Service { + return &Service{s} +} + +func (ser *Service) SmallText() string { + s := ser.ToService() + last := ser.LimitedFailures() + hits, _ := ser.LimitedHits() if !s.Online { if len(last) > 0 { - return fmt.Sprintf("%v on %v", last[0].ParseError(), last[0].CreatedAt.Format("Monday 3:04PM, Jan _2 2006")) + lastFailure := MakeFailure(last[0].ToFailure()) + return fmt.Sprintf("%v on %v", lastFailure.ParseError(), last[0].ToFailure().CreatedAt.Format("Monday 3:04PM, Jan _2 2006")) } else { return fmt.Sprintf("%v is currently offline", s.Name) } @@ -138,7 +132,8 @@ func GroupDataBy(column string, id int64, tm time.Time, increment string) string return sql } -func (s *Service) GraphData() string { +func (ser *Service) GraphData() string { + s := ser.ToService() var d []*DateScan since := time.Now().Add(time.Hour*-24 + time.Minute*0 + time.Second*0) @@ -172,9 +167,10 @@ func (s *Service) GraphData() string { return string(data) } -func (s *Service) AvgUptime() string { - failed, _ := s.TotalFailures() - total, _ := s.TotalHits() +func (ser *Service) AvgUptime() string { + s := ser.ToService() + failed, _ := ser.TotalFailures() + total, _ := ser.TotalHits() if failed == 0 { s.TotalUptime = "100" return s.TotalUptime @@ -195,10 +191,11 @@ func (s *Service) AvgUptime() string { return s.TotalUptime } -func (u *Service) RemoveArray() []*Service { +func RemoveArray(u *types.Service) []*Service { var srvcs []*Service for _, s := range CoreApp.Services { - if s.Id != u.Id { + ser := s.ToService() + if ser.Id != u.Id { srvcs = append(srvcs, s) } } @@ -206,7 +203,7 @@ func (u *Service) RemoveArray() []*Service { return srvcs } -func (u *Service) Delete() error { +func DeleteService(u *types.Service) error { res := serviceCol().Find("id", u.Id) err := res.Delete() if err != nil { @@ -214,28 +211,27 @@ func (u *Service) Delete() error { return err } utils.Log(1, fmt.Sprintf("Stopping %v Monitoring...", u.Name)) - if u.stopRoutine != nil { - close(u.stopRoutine) + if u.StopRoutine != nil { + close(u.StopRoutine) } utils.Log(1, fmt.Sprintf("Stopped %v Monitoring Service", u.Name)) - u.RemoveArray() + RemoveArray(u) OnDeletedService(u) return err } -func (u *Service) Update(s *Service) *Service { - s.CreatedAt = time.Now() +func UpdateService(u *types.Service) *types.Service { + u.CreatedAt = time.Now() res := serviceCol().Find("id", u.Id) - err := res.Update(s) + err := res.Update(u) if err != nil { utils.Log(3, fmt.Sprintf("Failed to update service %v. %v", u.Name, err)) } - *u = *s OnUpdateService(u) return u } -func (u *Service) Create() (int64, error) { +func CreateService(u *types.Service) (int64, error) { u.CreatedAt = time.Now() uuid, err := serviceCol().Insert(u) if uuid == nil { @@ -243,15 +239,16 @@ func (u *Service) Create() (int64, error) { return 0, err } u.Id = uuid.(int64) - u.stopRoutine = make(chan struct{}) - CoreApp.Services = append(CoreApp.Services, u) + u.StopRoutine = make(chan struct{}) + CoreApp.Services = append(CoreApp.Services, &Service{u}) return uuid.(int64), err } func CountOnline() int { amount := 0 - for _, v := range CoreApp.Services { - if v.Online { + for _, s := range CoreApp.Services { + ser := s.ToService() + if ser.Online { amount++ } } diff --git a/core/setup.go b/core/setup.go index 841ca1f9..80df737a 100644 --- a/core/setup.go +++ b/core/setup.go @@ -2,6 +2,7 @@ package core import ( "fmt" + "github.com/hunterlong/statup/types" "github.com/hunterlong/statup/utils" "os" ) @@ -19,7 +20,7 @@ type ErrorResponse struct { func LoadSampleData() error { utils.Log(1, "Inserting Sample Data...") - s1 := &Service{ + s1 := &types.Service{ Name: "Google", Domain: "https://google.com", ExpectedStatus: 200, @@ -28,7 +29,7 @@ func LoadSampleData() error { Type: "http", Method: "GET", } - s2 := &Service{ + s2 := &types.Service{ Name: "Statup Github", Domain: "https://github.com/hunterlong/statup", ExpectedStatus: 200, @@ -37,7 +38,7 @@ func LoadSampleData() error { Type: "http", Method: "GET", } - s3 := &Service{ + s3 := &types.Service{ Name: "JSON Users Test", Domain: "https://jsonplaceholder.typicode.com/users", ExpectedStatus: 200, @@ -46,7 +47,7 @@ func LoadSampleData() error { Type: "http", Method: "GET", } - s4 := &Service{ + s4 := &types.Service{ Name: "JSON API Tester", Domain: "https://jsonplaceholder.typicode.com/posts", ExpectedStatus: 201, @@ -56,19 +57,19 @@ func LoadSampleData() error { Method: "POST", PostData: `{ "title": "statup", "body": "bar", "userId": 19999 }`, } - id, err := s1.Create() + id, err := CreateService(s1) if err != nil { utils.Log(3, fmt.Sprintf("Error creating Service %v: %v", id, err)) } - id, err = s2.Create() + id, err = CreateService(s2) if err != nil { utils.Log(3, fmt.Sprintf("Error creating Service %v: %v", id, err)) } - id, err = s3.Create() + id, err = CreateService(s3) if err != nil { utils.Log(3, fmt.Sprintf("Error creating Service %v: %v", id, err)) } - id, err = s4.Create() + id, err = CreateService(s4) if err != nil { utils.Log(3, fmt.Sprintf("Error creating Service %v: %v", id, err)) } diff --git a/core/users.go b/core/users.go index a341aa9b..e203b248 100644 --- a/core/users.go +++ b/core/users.go @@ -10,36 +10,36 @@ import ( type User types.User -func SelectUser(id int64) (*User, error) { - var user User +func SelectUser(id int64) (*types.User, error) { + var user *types.User col := DbSession.Collection("users") res := col.Find("id", id) err := res.One(&user) - return &user, err + return user, err } -func SelectUsername(username string) (*User, error) { - var user User +func SelectUsername(username string) (*types.User, error) { + var user *types.User col := DbSession.Collection("users") res := col.Find("username", username) err := res.One(&user) - return &user, err + return user, err } -func (u *User) Delete() error { +func DeleteUser(u *types.User) error { col := DbSession.Collection("users") user := col.Find("id", u.Id) return user.Delete() } -func (u *User) Update() error { +func UpdateUser(u *types.User) error { u.CreatedAt = time.Now() col := DbSession.Collection("users") user := col.Find("id", u.Id) return user.Update(u) } -func (u *User) Create() (int64, error) { +func CreateUser(u *types.User) (int64, error) { u.CreatedAt = time.Now() u.Password = utils.HashPassword(u.Password) u.ApiKey = utils.NewSHA1Hash(5) @@ -63,7 +63,7 @@ func SelectAllUsers() ([]User, error) { return users, err } -func AuthUser(username, password string) (*User, bool) { +func AuthUser(username, password string) (*types.User, bool) { var auth bool user, err := SelectUsername(username) if err != nil { diff --git a/handlers/api.go b/handlers/api.go index a96c86ce..bedc8eb4 100644 --- a/handlers/api.go +++ b/handlers/api.go @@ -4,6 +4,7 @@ import ( "encoding/json" "github.com/gorilla/mux" "github.com/hunterlong/statup/core" + "github.com/hunterlong/statup/types" "github.com/hunterlong/statup/utils" "net/http" ) @@ -16,6 +17,21 @@ func ApiIndexHandler(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(core.CoreApp) } +func ApiRenewHandler(w http.ResponseWriter, r *http.Request) { + if !isAPIAuthorized(r) { + http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) + return + } + var err error + core.CoreApp.ApiKey = utils.NewSHA1Hash(40) + core.CoreApp.ApiSecret = utils.NewSHA1Hash(40) + core.CoreApp, err = core.UpdateCore(core.CoreApp) + if err != nil { + utils.Log(3, err) + } + http.Redirect(w, r, "/settings", http.StatusSeeOther) +} + func ApiCheckinHandler(w http.ResponseWriter, r *http.Request) { if !isAPIAuthorized(r) { http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) @@ -23,7 +39,7 @@ func ApiCheckinHandler(w http.ResponseWriter, r *http.Request) { } vars := mux.Vars(r) checkin := core.FindCheckin(vars["api"]) - checkin.Receivehit() + //checkin.Receivehit() w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(checkin) } @@ -44,11 +60,12 @@ func ApiServiceUpdateHandler(w http.ResponseWriter, r *http.Request) { return } vars := mux.Vars(r) - service := core.SelectService(utils.StringInt(vars["id"])) - var s core.Service + serv := core.SelectService(utils.StringInt(vars["id"])) + var s *types.Service decoder := json.NewDecoder(r.Body) decoder.Decode(&s) - service.Update(&s) + service := serv.ToService() + core.UpdateService(service) json.NewEncoder(w).Encode(s) } diff --git a/handlers/index.go b/handlers/index.go index c9b8a393..f5cf56c3 100644 --- a/handlers/index.go +++ b/handlers/index.go @@ -6,8 +6,7 @@ import ( ) type index struct { - Core core.Core - Services []*core.Service + Core *core.Core } func IndexHandler(w http.ResponseWriter, r *http.Request) { @@ -15,6 +14,5 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/setup", http.StatusSeeOther) return } - out := index{*core.CoreApp, core.CoreApp.Services} - ExecuteResponse(w, r, "index.html", out) + ExecuteResponse(w, r, "index.html", core.CoreApp) } diff --git a/handlers/prometheus.go b/handlers/prometheus.go index 96576a50..efdace07 100644 --- a/handlers/prometheus.go +++ b/handlers/prometheus.go @@ -28,7 +28,8 @@ func PrometheusHandler(w http.ResponseWriter, r *http.Request) { system := fmt.Sprintf("statup_total_failures %v\n", core.CountFailures()) system += fmt.Sprintf("statup_total_services %v", len(core.CoreApp.Services)) metrics = append(metrics, system) - for _, v := range core.CoreApp.Services { + for _, ser := range core.CoreApp.Services { + v := ser.ToService() online := 1 if !v.Online { online = 0 diff --git a/handlers/routes.go b/handlers/routes.go index 873afe43..c71e38d6 100644 --- a/handlers/routes.go +++ b/handlers/routes.go @@ -33,7 +33,7 @@ func Router() *mux.Router { r.Handle("/user/{id}", http.HandlerFunc(UsersEditHandler)).Methods("GET") r.Handle("/user/{id}", http.HandlerFunc(UpdateUserHandler)).Methods("POST") r.Handle("/user/{id}/delete", http.HandlerFunc(UsersDeleteHandler)).Methods("GET") - r.Handle("/settings", http.HandlerFunc(PluginsHandler)).Methods("GET") + r.Handle("/settings", http.HandlerFunc(SettingsHandler)).Methods("GET") r.Handle("/settings", http.HandlerFunc(SaveSettingsHandler)).Methods("POST") r.Handle("/settings/css", http.HandlerFunc(SaveSASSHandler)).Methods("POST") r.Handle("/settings/build", http.HandlerFunc(SaveAssetsHandler)).Methods("GET") @@ -42,6 +42,7 @@ func Router() *mux.Router { r.Handle("/plugins/{name}/save", http.HandlerFunc(PluginSavedHandler)).Methods("POST") r.Handle("/help", http.HandlerFunc(HelpHandler)) r.Handle("/api", http.HandlerFunc(ApiIndexHandler)) + r.Handle("/api/renew", http.HandlerFunc(ApiRenewHandler)) r.Handle("/api/checkin/{api}", http.HandlerFunc(ApiCheckinHandler)) r.Handle("/api/services", http.HandlerFunc(ApiAllServicesHandler)) r.Handle("/api/services/{id}", http.HandlerFunc(ApiServiceHandler)).Methods("GET") diff --git a/handlers/services.go b/handlers/services.go index 23e8b0b8..0e7bea86 100644 --- a/handlers/services.go +++ b/handlers/services.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/gorilla/mux" "github.com/hunterlong/statup/core" + "github.com/hunterlong/statup/types" "github.com/hunterlong/statup/utils" "net/http" "strconv" @@ -40,7 +41,7 @@ func CreateServiceHandler(w http.ResponseWriter, r *http.Request) { checkType := r.PostForm.Get("check_type") postData := r.PostForm.Get("post_data") - service := &core.Service{ + service := &types.Service{ Name: name, Domain: domain, Method: method, @@ -51,12 +52,12 @@ func CreateServiceHandler(w http.ResponseWriter, r *http.Request) { Port: port, PostData: postData, } - _, err := service.Create() + _, err := core.CreateService(service) if err != nil { utils.Log(3, fmt.Sprintf("Error starting %v check routine. %v", service.Name, err)) } - go service.CheckQueue() + go core.CheckQueue(service) core.OnNewService(service) http.Redirect(w, r, "/services", http.StatusSeeOther) @@ -68,21 +69,25 @@ func ServicesDeleteHandler(w http.ResponseWriter, r *http.Request) { return } vars := mux.Vars(r) - service := core.SelectService(utils.StringInt(vars["id"])) - service.Delete() + serv := core.SelectService(utils.StringInt(vars["id"])) + service := serv.ToService() + core.DeleteService(service) http.Redirect(w, r, "/services", http.StatusSeeOther) } func ServicesViewHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) - service := core.SelectService(utils.StringInt(vars["id"])) - ExecuteResponse(w, r, "service.html", service) + serv := core.SelectService(utils.StringInt(vars["id"])) + + fmt.Println(serv.ToService()) + + ExecuteResponse(w, r, "service.html", serv) } func ServicesBadgeHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) - service := core.SelectService(utils.StringInt(vars["id"])) - + serv := core.SelectService(utils.StringInt(vars["id"])) + service := serv.ToService() var badge []byte if service.Online { badge = []byte(``) @@ -92,9 +97,7 @@ func ServicesBadgeHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "image/svg+xml") w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") - w.Write(badge) - } func ServicesUpdateHandler(w http.ResponseWriter, r *http.Request) { @@ -103,7 +106,8 @@ func ServicesUpdateHandler(w http.ResponseWriter, r *http.Request) { return } vars := mux.Vars(r) - service := core.SelectService(utils.StringInt(vars["id"])) + serv := core.SelectService(utils.StringInt(vars["id"])) + service := serv.ToService() r.ParseForm() name := r.PostForm.Get("name") domain := r.PostForm.Get("domain") @@ -114,7 +118,7 @@ func ServicesUpdateHandler(w http.ResponseWriter, r *http.Request) { port, _ := strconv.Atoi(r.PostForm.Get("port")) checkType := r.PostForm.Get("check_type") postData := r.PostForm.Get("post_data") - serviceUpdate := &core.Service{ + serviceUpdate := &types.Service{ Id: service.Id, Name: name, Domain: domain, @@ -126,7 +130,7 @@ func ServicesUpdateHandler(w http.ResponseWriter, r *http.Request) { Port: port, PostData: postData, } - service = service.Update(serviceUpdate) + service = core.UpdateService(serviceUpdate) ExecuteResponse(w, r, "service.html", service) } @@ -136,9 +140,9 @@ func ServicesDeleteFailuresHandler(w http.ResponseWriter, r *http.Request) { return } vars := mux.Vars(r) - service := core.SelectService(utils.StringInt(vars["id"])) - - service.DeleteFailures() + serv := core.SelectService(utils.StringInt(vars["id"])) + service := serv.ToService() + core.DeleteFailures(service) core.CoreApp.Services, _ = core.SelectAllServices() http.Redirect(w, r, "/services", http.StatusSeeOther) } @@ -150,7 +154,8 @@ func CheckinCreateUpdateHandler(w http.ResponseWriter, r *http.Request) { } vars := mux.Vars(r) interval := utils.StringInt(r.PostForm.Get("interval")) - service := core.SelectService(utils.StringInt(vars["id"])) + serv := core.SelectService(utils.StringInt(vars["id"])) + service := serv.ToService() checkin := &core.Checkin{ Service: service.Id, Interval: interval, diff --git a/handlers/settings.go b/handlers/settings.go index 59951b21..9b9a230a 100644 --- a/handlers/settings.go +++ b/handlers/settings.go @@ -9,23 +9,11 @@ import ( "net/http" ) -func PluginsHandler(w http.ResponseWriter, r *http.Request) { +func SettingsHandler(w http.ResponseWriter, r *http.Request) { if !IsAuthenticated(r) { http.Redirect(w, r, "/", http.StatusSeeOther) return } - //CoreApp.FetchPluginRepo() - - //var pluginFields []PluginSelect - // - //for _, p := range allPlugins { - // fields := structs.Map(p.GetInfo()) - // - // pluginFields = append(pluginFields, PluginSelect{p.GetInfo().Name, p.GetForm(), fields}) - //} - - //CoreApp.PluginFields = pluginFields - ExecuteResponse(w, r, "settings.html", core.CoreApp) } @@ -56,8 +44,8 @@ func SaveSettingsHandler(w http.ResponseWriter, r *http.Request) { core.CoreApp.Domain = domain } core.CoreApp.UseCdn = (r.PostForm.Get("enable_cdn") == "on") - core.CoreApp.Update() - core.OnSettingsSaved(core.CoreApp) + core.CoreApp, _ = core.UpdateCore(core.CoreApp) + core.OnSettingsSaved(core.CoreApp.ToCore()) http.Redirect(w, r, "/settings", http.StatusSeeOther) } @@ -105,8 +93,8 @@ func SaveNotificationHandler(w http.ResponseWriter, r *http.Request) { var2 := r.PostForm.Get("var2") apiKey := r.PostForm.Get("api_key") apiSecret := r.PostForm.Get("api_secret") - limits := int64(utils.StringInt(r.PostForm.Get("limits"))) - notifer := notifiers.Select(utils.StringInt(notifierId)) + limits := int(utils.StringInt(r.PostForm.Get("limits"))) + notifer := notifiers.SelectNotifier(utils.StringInt(notifierId)).Select() if host != "" { notifer.Host = host diff --git a/handlers/setup.go b/handlers/setup.go index 975b8482..d6d1d18c 100644 --- a/handlers/setup.go +++ b/handlers/setup.go @@ -104,13 +104,13 @@ func ProcessSetupHandler(w http.ResponseWriter, r *http.Request) { return } - admin := &core.User{ + admin := &types.User{ Username: config.Username, Password: config.Password, Email: config.Email, Admin: true, } - admin.Create() + core.CreateUser(admin) if sample == "on" { core.LoadSampleData() diff --git a/handlers/users.go b/handlers/users.go index 854d1bdf..61a8af26 100644 --- a/handlers/users.go +++ b/handlers/users.go @@ -64,7 +64,7 @@ func UpdateUserHandler(w http.ResponseWriter, r *http.Request) { if password != "##########" { user.Password = utils.HashPassword(password) } - user.Update() + core.UpdateUser(user) users, _ := core.SelectAllUsers() ExecuteResponse(w, r, "users.html", users) } @@ -80,13 +80,13 @@ func CreateUserHandler(w http.ResponseWriter, r *http.Request) { email := r.PostForm.Get("email") admin := r.PostForm.Get("admin") - user := &core.User{ + user := &types.User{ Username: username, Password: password, Email: email, Admin: (admin == "on"), } - _, err := user.Create() + _, err := core.CreateUser(user) if err != nil { utils.Log(2, err) } @@ -108,6 +108,6 @@ func UsersDeleteHandler(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/users", http.StatusSeeOther) return } - user.Delete() + core.DeleteUser(user) http.Redirect(w, r, "/users", http.StatusSeeOther) } diff --git a/main.go b/main.go index cdc76b76..9de838e7 100644 --- a/main.go +++ b/main.go @@ -6,7 +6,7 @@ import ( "github.com/fatih/structs" "github.com/hunterlong/statup/core" "github.com/hunterlong/statup/handlers" - "github.com/hunterlong/statup/plugin" + "github.com/hunterlong/statup/types" "github.com/hunterlong/statup/utils" "github.com/joho/godotenv" "io/ioutil" @@ -50,7 +50,6 @@ func RenderBoxes() { core.ScssBox = rice.MustFindBox("source/scss") core.JsBox = rice.MustFindBox("source/js") core.TmplBox = rice.MustFindBox("source/tmpl") - core.EmailBox = rice.MustFindBox("source/emails") } func LoadDotEnvs() { @@ -124,8 +123,8 @@ func LoadPlugins(debug bool) { utils.Log(1, structs.Map(symPlugin)) } - var plugActions plugin.PluginActions - plugActions, ok := symPlugin.(plugin.PluginActions) + var plugActions types.PluginActions + plugActions, ok := symPlugin.(types.PluginActions) if !ok { utils.Log(3, fmt.Sprintf("Plugin '%v' could not load correctly, error: %v", f.Name(), err)) if debug { diff --git a/main_test.go b/main_test.go index 40256ddc..7850b93b 100644 --- a/main_test.go +++ b/main_test.go @@ -6,6 +6,7 @@ import ( "github.com/hunterlong/statup/core" "github.com/hunterlong/statup/handlers" "github.com/hunterlong/statup/notifiers" + "github.com/hunterlong/statup/types" "github.com/rendon/testcli" "github.com/stretchr/testify/assert" "net/http" @@ -31,7 +32,7 @@ func RunInit(t *testing.T) { core.CoreApp = core.NewCore() } -var forceSequential chan bool = make(chan bool, 1) +var forceSequential = make(chan bool, 1) func TestRunAll(t *testing.T) { @@ -187,7 +188,6 @@ func TestAssetsCommand(t *testing.T) { assert.True(t, fileExists("assets/js/main.js")) assert.True(t, fileExists("assets/css/base.css")) assert.True(t, fileExists("assets/scss/base.scss")) - assert.True(t, fileExists("assets/emails/failure.html")) } func RunMySQLMakeConfig(t *testing.T, db string) { @@ -267,22 +267,22 @@ func RunUser_SelectAll(t *testing.T) { } func RunUser_Create(t *testing.T) { - user := &core.User{ + user := &types.User{ Username: "admin", Password: "admin", Email: "info@testuser.com", Admin: true, } - id, err := user.Create() + id, err := core.CreateUser(user) assert.Nil(t, err) assert.Equal(t, int64(1), id) - user2 := &core.User{ + user2 := &types.User{ Username: "superadmin", Password: "admin", Email: "info@adminer.com", Admin: true, } - id, err = user2.Create() + id, err = core.CreateUser(user2) assert.Nil(t, err) assert.Equal(t, int64(2), id) } @@ -291,7 +291,7 @@ func RunUser_Update(t *testing.T) { user, err := core.SelectUser(1) user.Email = "info@updatedemail.com" assert.Nil(t, err) - err = user.Update() + err = core.UpdateUser(user) assert.Nil(t, err) updatedUser, err := core.SelectUser(1) assert.Nil(t, err) @@ -299,12 +299,12 @@ func RunUser_Update(t *testing.T) { } func RunUser_NonUniqueCreate(t *testing.T) { - user := &core.User{ + user := &types.User{ Username: "admin", Password: "admin", Email: "info@testuser.com", } - _, err := user.Create() + _, err := core.CreateUser(user) assert.NotNil(t, err) } @@ -312,7 +312,7 @@ func RunUser_Delete(t *testing.T) { user, err := core.SelectUser(2) assert.Nil(t, err) assert.NotNil(t, user) - err = user.Delete() + err = core.DeleteUser(user) assert.Nil(t, err) } @@ -327,11 +327,11 @@ func RunOneService_Check(t *testing.T) { service := core.SelectService(1) assert.NotNil(t, service) t.Log(service) - assert.Equal(t, "Google", service.Name) + assert.Equal(t, "Google", service.ToService().Name) } func RunService_Create(t *testing.T) { - service := &core.Service{ + service := &types.Service{ Name: "test service", Domain: "https://google.com", ExpectedStatus: 200, @@ -340,7 +340,7 @@ func RunService_Create(t *testing.T) { Type: "http", Method: "GET", } - id, err := service.Create() + id, err := core.CreateService(service) assert.Nil(t, err) assert.Equal(t, int64(5), id) t.Log(service) @@ -371,7 +371,7 @@ func RunService_GraphData(t *testing.T) { } func RunBadService_Create(t *testing.T) { - service := &core.Service{ + service := &types.Service{ Name: "Bad Service", Domain: "https://9839f83h72gey2g29278hd2od2d.com", ExpectedStatus: 200, @@ -380,7 +380,7 @@ func RunBadService_Create(t *testing.T) { Type: "http", Method: "GET", } - id, err := service.Create() + id, err := core.CreateService(service) assert.Nil(t, err) assert.Equal(t, int64(6), id) } @@ -388,14 +388,14 @@ func RunBadService_Create(t *testing.T) { func RunBadService_Check(t *testing.T) { service := core.SelectService(4) assert.NotNil(t, service) - assert.Equal(t, "JSON API Tester", service.Name) + assert.Equal(t, "JSON API Tester", service.ToService().Name) } func RunDeleteService(t *testing.T) { service := core.SelectService(4) assert.NotNil(t, service) - assert.Equal(t, "JSON API Tester", service.Name) - err := service.Delete() + assert.Equal(t, "JSON API Tester", service.ToService().Name) + err := core.DeleteService(service.ToService()) assert.Nil(t, err) } @@ -405,7 +405,7 @@ func RunCreateService_Hits(t *testing.T) { assert.NotNil(t, services) for i := 0; i <= 10; i++ { for _, s := range services { - service := s.Check() + service := core.ServiceCheck(s.ToService()) assert.NotNil(t, service) } } @@ -423,7 +423,7 @@ func RunService_Failures(t *testing.T) { t.SkipNow() service := core.SelectService(6) assert.NotNil(t, service) - assert.NotEmpty(t, service.Failures) + assert.NotEmpty(t, service.ToService().Failures) } func RunService_LimitedHits(t *testing.T) { diff --git a/notifiers/email.go b/notifiers/email.go index ab9631c0..ad9715e9 100644 --- a/notifiers/email.go +++ b/notifiers/email.go @@ -20,12 +20,13 @@ const ( var ( emailer *Email emailArray []string - emailQueue []*types.Email + emailQueue []*EmailOutgoing + emailBox *rice.Box + mailer *gomail.Dialer ) type Email struct { *Notification - mailer *gomail.Dialer } // DEFINE YOUR NOTIFICATION HERE. @@ -36,41 +37,35 @@ func init() { Id: EMAIL_ID, Method: EMAIL_METHOD, Form: []NotificationForm{{ - id: 1, + Id: 1, Type: "text", Title: "SMTP Host", Placeholder: "Insert your SMTP Host here.", DbField: "Host", }, { - id: 1, + Id: 1, Type: "text", Title: "SMTP Username", Placeholder: "Insert your SMTP Username here.", DbField: "Username", }, { - id: 1, + Id: 1, Type: "password", Title: "SMTP Password", Placeholder: "Insert your SMTP Password here.", DbField: "Password", }, { - id: 1, + Id: 1, Type: "number", Title: "SMTP Port", Placeholder: "Insert your SMTP Port here.", DbField: "Port", }, { - id: 1, + Id: 1, Type: "text", Title: "Outgoing Email Address", Placeholder: "Insert your Outgoing Email Address", DbField: "Var1", - }, { - id: 1, - Type: "number", - Title: "Limits per Hour", - Placeholder: "How many emails can it send per hour", - DbField: "Limits", }}, }} @@ -84,7 +79,9 @@ func (u *Email) Select() *Notification { // WHEN NOTIFIER LOADS func (u *Email) Init() error { + emailBox = rice.MustFindBox("emails") err := u.Install() + utils.Log(1, fmt.Sprintf("Creating Mailer: %v:%v", u.Notification.Host, u.Notification.Port)) if err == nil { notifier, _ := SelectNotification(u.Id) @@ -94,29 +91,43 @@ func (u *Email) Init() error { if u.Enabled { utils.Log(1, fmt.Sprintf("Loading SMTP Emailer using host: %v:%v", u.Notification.Host, u.Notification.Port)) - u.mailer = gomail.NewDialer(u.Notification.Host, u.Notification.Port, u.Notification.Username, u.Notification.Password) - u.mailer.TLSConfig = &tls.Config{InsecureSkipVerify: true} + mailer = gomail.NewDialer(u.Notification.Host, u.Notification.Port, u.Notification.Username, u.Notification.Password) + mailer.TLSConfig = &tls.Config{InsecureSkipVerify: true} go u.Run() } } - - //go u.Run() return nil } func (u *Email) Test() error { - //email := &types.Email{ - // To: "info@socialeck.com", - // Subject: "Test Email", - // Template: "message.html", - // Data: nil, - // From: emailer.Var1, - //} - //SendEmail(core.EmailBox, email) + if u.Enabled { + email := &EmailOutgoing{ + To: "info@socialeck.com", + Subject: "Test Email", + Template: "message.html", + Data: nil, + From: emailer.Var1, + } + SendEmail(emailBox, email) + } return nil } +type emailMessage struct { + Service *types.Service +} + +type EmailOutgoing struct { + To string + Subject string + Template string + From string + Data interface{} + Source string + Sent bool +} + // AFTER NOTIFIER LOADS, IF ENABLED, START A QUEUE PROCESS func (u *Email) Run() error { var sentAddresses []string @@ -125,16 +136,18 @@ func (u *Email) Run() error { emailQueue = removeEmail(emailQueue, email) continue } - e := email - go func(email *types.Email) { + if u.CanSend() { err := u.dialSend(email) if err == nil { email.Sent = true sentAddresses = append(sentAddresses, email.To) utils.Log(1, fmt.Sprintf("Email '%v' sent to: %v using the %v template (size: %v)", email.Subject, email.To, email.Template, len([]byte(email.Source)))) emailQueue = removeEmail(emailQueue, email) + u.Log(fmt.Sprintf("Subject: %v to %v", email.Subject, email.To)) + } else { + utils.Log(3, fmt.Sprintf("Email Notifier could not send email: %v", err)) } - }(e) + } } time.Sleep(60 * time.Second) if u.Enabled { @@ -144,19 +157,30 @@ func (u *Email) Run() error { } // ON SERVICE FAILURE, DO YOUR OWN FUNCTIONS -func (u *Email) OnFailure() error { +func (u *Email) OnFailure(s *types.Service) error { if u.Enabled { - utils.Log(1, fmt.Sprintf("Notification %v is receiving a failure notification.", u.Method)) - // Do failing stuff here! + + msg := emailMessage{ + Service: s, + } + + email := &EmailOutgoing{ + To: "info@socialeck.com", + Subject: fmt.Sprintf("Service %v is Failing", s.Name), + Template: "failure.html", + Data: msg, + From: emailer.Var1, + } + SendEmail(emailBox, email) + } return nil } // ON SERVICE SUCCESS, DO YOUR OWN FUNCTIONS -func (u *Email) OnSuccess() error { +func (u *Email) OnSuccess(s *types.Service) error { if u.Enabled { - utils.Log(1, fmt.Sprintf("Notification %v is receiving a failure notification.", u.Method)) - // Do failing stuff here! + } return nil } @@ -172,7 +196,7 @@ func (u *Email) OnSave() error { // ON SERVICE FAILURE, DO YOUR OWN FUNCTIONS func (u *Email) Install() error { - inDb, err := emailer.Notification.isInDatabase() + inDb, err := emailer.Notification.IsInDatabase() if !inDb { newNotifer, err := InsertDatabase(u.Notification) if err != nil { @@ -184,20 +208,21 @@ func (u *Email) Install() error { return err } -func (u *Email) dialSend(email *types.Email) error { +func (u *Email) dialSend(email *EmailOutgoing) error { + fmt.Println("sending dailsend to emailer") m := gomail.NewMessage() m.SetHeader("From", email.From) m.SetHeader("To", email.To) m.SetHeader("Subject", email.Subject) m.SetBody("text/html", email.Source) - if err := u.mailer.DialAndSend(m); err != nil { + if err := mailer.DialAndSend(m); err != nil { utils.Log(3, fmt.Sprintf("Email '%v' sent to: %v using the %v template (size: %v) %v", email.Subject, email.To, email.Template, len([]byte(email.Source)), err)) return err } return nil } -func SendEmail(box *rice.Box, email *types.Email) { +func SendEmail(box *rice.Box, email *EmailOutgoing) { source := EmailTemplate(box, email.Template, email.Data) email.Source = source emailQueue = append(emailQueue, email) @@ -221,8 +246,8 @@ func EmailTemplate(box *rice.Box, tmpl string, data interface{}) string { return result } -func removeEmail(emails []*types.Email, em *types.Email) []*types.Email { - var newArr []*types.Email +func removeEmail(emails []*EmailOutgoing, em *EmailOutgoing) []*EmailOutgoing { + var newArr []*EmailOutgoing for _, e := range emails { if e != em { newArr = append(newArr, e) diff --git a/source/emails/failure.html b/notifiers/emails/failure.html similarity index 84% rename from source/emails/failure.html rename to notifiers/emails/failure.html index ae964e3c..2d11e1c4 100644 --- a/source/emails/failure.html +++ b/notifiers/emails/failure.html @@ -50,15 +50,13 @@
- View Service + View Service | - Statup Dashboard + Statup Dashboard |
\n
| \n
\n
| \n
{{.SmallText}}
{{ end }}{{.Issue}}
+{{$f.Issue}}
{{ end }}{{.Issue}}
+{{$f.Issue}}
{{ end }}