From 9a6c91a9979b2b7a36bae1e1207bd3f6a39390d3 Mon Sep 17 00:00:00 2001 From: Hunter Long Date: Thu, 4 Oct 2018 01:18:55 -0700 Subject: [PATCH] checkins --- core/checkin.go | 43 ++++++++++++++++++-------- core/database.go | 20 ++++++++++++ core/sample.go | 58 +++++++++++++++-------------------- core/services.go | 8 ++--- handlers/api.go | 2 +- handlers/handlers.go | 19 ++++++++++++ handlers/routes.go | 50 +++++++++++++++++------------- handlers/services.go | 1 - source/tmpl/form_service.html | 2 +- source/tmpl/service.html | 33 ++++++++++++-------- types/checkin.go | 15 +++++---- types/service.go | 2 +- 12 files changed, 157 insertions(+), 96 deletions(-) diff --git a/core/checkin.go b/core/checkin.go index 3d70c948..7d3dcda8 100644 --- a/core/checkin.go +++ b/core/checkin.go @@ -49,26 +49,41 @@ func SelectCheckin(api string) *Checkin { return &checkin } -func FindCheckin(api string) *types.Checkin { - for _, ser := range CoreApp.Services { - service := ser.Select() - for _, c := range service.Checkins { - if c.ApiKey == api { - return c - } - } - } - return nil +func (u Checkin) Period() time.Duration { + duration, _ := time.ParseDuration(fmt.Sprintf("%vs", u.Interval)) + return duration +} + +func (u Checkin) Grace() time.Duration { + duration, _ := time.ParseDuration(fmt.Sprintf("%vs", u.GracePeriod)) + return duration +} + +func (u Checkin) Expected() time.Duration { + last := u.Last().CreatedAt + now := time.Now() + lastDir := now.Sub(last) + sub := time.Duration(u.Period() - lastDir) + return sub +} + +func (u Checkin) Last() CheckinHit { + var hit CheckinHit + checkinHitsDB().Where("checkin = ?", u.Id).Last(&hit) + return hit } func (u *Checkin) Hits() []CheckinHit { var checkins []CheckinHit - checkinDB().Where("checkin = ?", u.Id).Order("id DESC").Find(&checkins) + checkinHitsDB().Where("checkin = ?", u.Id).Order("id DESC").Find(&checkins) return checkins } func (u *Checkin) Create() (int64, error) { - u.CreatedAt = time.Now() + if u.CreatedAt.IsZero() { + u.CreatedAt = time.Now() + } + u.ApiKey = utils.NewSHA1Hash(7) row := checkinDB().Create(u) if row.Error == nil { utils.Log(2, row.Error) @@ -78,7 +93,9 @@ func (u *Checkin) Create() (int64, error) { } func (u *CheckinHit) Create() (int64, error) { - u.CreatedAt = time.Now() + if u.CreatedAt.IsZero() { + u.CreatedAt = time.Now() + } row := checkinHitsDB().Create(u) if row.Error == nil { utils.Log(2, row.Error) diff --git a/core/database.go b/core/database.go index 7fa6a9ec..37652595 100644 --- a/core/database.go +++ b/core/database.go @@ -108,6 +108,16 @@ func (u *User) AfterFind() (err error) { return } +func (s *Checkin) AfterFind() (err error) { + s.CreatedAt = utils.Timezoner(s.CreatedAt, CoreApp.Timezone) + return +} + +func (s *CheckinHit) AfterFind() (err error) { + s.CreatedAt = utils.Timezoner(s.CreatedAt, CoreApp.Timezone) + return +} + func (u *Hit) BeforeCreate() (err error) { if u.CreatedAt.IsZero() { u.CreatedAt = time.Now().UTC() @@ -130,6 +140,16 @@ func (u *Service) BeforeCreate() (err error) { return } +func (u *Checkin) BeforeCreate() (err error) { + u.CreatedAt = time.Now().UTC() + return +} + +func (u *CheckinHit) BeforeCreate() (err error) { + u.CreatedAt = time.Now().UTC() + return +} + // InsertCore create the single row for the Core settings in Statup func (db *DbConfig) InsertCore() (*Core, error) { CoreApp = &Core{Core: &types.Core{ diff --git a/core/sample.go b/core/sample.go index 3a6a5f2a..1399dcd6 100644 --- a/core/sample.go +++ b/core/sample.go @@ -84,6 +84,31 @@ func InsertSampleData() error { s4.Create(false) s5.Create(false) + checkin1 := ReturnCheckin(&types.Checkin{ + Service: s1.Id, + Interval: 300, + GracePeriod: 300, + }) + checkin1.Create() + + checkin2 := ReturnCheckin(&types.Checkin{ + Service: s2.Id, + Interval: 900, + GracePeriod: 300, + }) + checkin2.Create() + + checkTime := time.Now().Add(-24 * time.Hour) + for i := 0; i <= 60; i++ { + checkHit := ReturnCheckinHit(&types.CheckinHit{ + Checkin: checkin1.Id, + From: "192.168.0.1", + CreatedAt: checkTime.UTC(), + }) + checkHit.Create() + checkTime = checkTime.Add(10 * time.Minute) + } + utils.Log(1, "Sample data has finished importing") return nil @@ -114,39 +139,6 @@ func InsertSampleHits() error { return nil } -func sampleGraphData(i float64, upward *bool) float64 { - alpha := 0.0003 - if *upward { - i += 0.3 - if i >= 6500 { - i += 0.1 - *upward = false - } else if i >= 4500 { - i += 3 - } else if i >= 2300 { - i += 1 - } else if i >= 1150 { - i += 2 - } else if i >= 500 { - i += 1 - } - } else { - i -= 0.3 - if i <= 6500 { - i -= 0.1 - } else if i <= 4500 { - i -= 3 - } else if i <= 2300 { - i -= 1 - } else if i <= 1150 { - i -= 2 - } else if i <= 500 { - i -= 1 - } - } - return i * alpha -} - func insertSampleCore() error { core := &types.Core{ Name: "Statup Sample Data", diff --git a/core/services.go b/core/services.go index d89946a0..58c19fa0 100644 --- a/core/services.go +++ b/core/services.go @@ -49,9 +49,9 @@ func SelectService(id int64) *Service { return nil } -func (s *Service) Checkins() []*Checkin { - var hits []*Checkin - servicesDB().Where("service = ?", s.Id).Find(&hits) +func (s *Service) Checkin() Checkin { + var hits Checkin + servicesDB().Where("service = ?", s.Id).First(&hits) return hits } @@ -66,7 +66,7 @@ func (c *Core) SelectAllServices() ([]*Service, error) { CoreApp.Services = nil for _, service := range services { service.Start() - service.Checkins() + service.Checkin() service.AllFailures() CoreApp.Services = append(CoreApp.Services, service) } diff --git a/handlers/api.go b/handlers/api.go index 760cd01f..f6d960dd 100644 --- a/handlers/api.go +++ b/handlers/api.go @@ -72,7 +72,7 @@ func apiCheckinHandler(w http.ResponseWriter, r *http.Request) { return } vars := mux.Vars(r) - checkin := core.FindCheckin(vars["api"]) + checkin := core.SelectCheckin(vars["api"]) //checkin.Receivehit() w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(checkin) diff --git a/handlers/handlers.go b/handlers/handlers.go index 7291bd7e..435abe76 100644 --- a/handlers/handlers.go +++ b/handlers/handlers.go @@ -137,6 +137,25 @@ var handlerFuncs = func(w http.ResponseWriter, r *http.Request) template.FuncMap "ToString": func(v interface{}) string { return utils.ToString(v) }, + "Ago": func(t time.Time) string { + return utils.Timestamp(t).Ago() + }, + "Duration": func(t time.Duration) string { + var out string + duration, _ := time.ParseDuration(fmt.Sprintf("%vs", t.Seconds())) + if duration.Minutes() < 1 { + out = fmt.Sprintf("%0.0f second", duration.Seconds()) + if duration.Seconds() >= 2 { + out += "s" + } + } else { + out = fmt.Sprintf("%0.0f minute", duration.Minutes()) + if duration.Minutes() >= 2 { + out += "s" + } + } + return out + }, "ToUnix": func(t time.Time) int64 { return t.UTC().Unix() }, diff --git a/handlers/routes.go b/handlers/routes.go index ac6010f1..b1fab9e2 100644 --- a/handlers/routes.go +++ b/handlers/routes.go @@ -53,6 +53,31 @@ func Router() *mux.Router { r.Handle("/dashboard", http.HandlerFunc(dashboardHandler)).Methods("GET") r.Handle("/dashboard", http.HandlerFunc(loginHandler)).Methods("POST") r.Handle("/logout", http.HandlerFunc(logoutHandler)) + r.Handle("/checkin/{id}", http.HandlerFunc(checkinUpdateHandler)) + r.Handle("/plugins/download/{name}", http.HandlerFunc(pluginsDownloadHandler)) + r.Handle("/plugins/{name}/save", http.HandlerFunc(pluginSavedHandler)).Methods("POST") + r.Handle("/help", http.HandlerFunc(helpHandler)) + r.Handle("/logs", http.HandlerFunc(logsHandler)) + r.Handle("/logs/line", http.HandlerFunc(logsLineHandler)) + + // USER Routes + r.Handle("/users", http.HandlerFunc(usersHandler)).Methods("GET") + r.Handle("/users", http.HandlerFunc(createUserHandler)).Methods("POST") + 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") + + // SETTINGS Routes + 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") + r.Handle("/settings/delete_assets", http.HandlerFunc(deleteAssetsHandler)).Methods("GET") + r.Handle("/settings/notifier/{method}", http.HandlerFunc(saveNotificationHandler)).Methods("POST") + r.Handle("/settings/notifier/{method}/test", http.HandlerFunc(testNotificationHandler)).Methods("POST") + r.Handle("/settings/export", http.HandlerFunc(exportHandler)).Methods("GET") + + // SERVICE Routes r.Handle("/services", http.HandlerFunc(servicesHandler)).Methods("GET") r.Handle("/services", http.HandlerFunc(createServiceHandler)).Methods("POST") r.Handle("/services/reorder", http.HandlerFunc(reorderServiceHandler)).Methods("POST") @@ -62,27 +87,8 @@ func Router() *mux.Router { r.Handle("/service/{id}/delete", http.HandlerFunc(servicesDeleteHandler)) r.Handle("/service/{id}/delete_failures", http.HandlerFunc(servicesDeleteFailuresHandler)).Methods("GET") r.Handle("/service/{id}/checkin", http.HandlerFunc(checkinCreateUpdateHandler)).Methods("POST") - r.Handle("/checkin/{id}", http.HandlerFunc(checkinUpdateHandler)) - r.Handle("/users", http.HandlerFunc(usersHandler)).Methods("GET") - r.Handle("/users", http.HandlerFunc(createUserHandler)).Methods("POST") - 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(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") - r.Handle("/settings/delete_assets", http.HandlerFunc(deleteAssetsHandler)).Methods("GET") - r.Handle("/settings/notifier/{method}", http.HandlerFunc(saveNotificationHandler)).Methods("POST") - r.Handle("/settings/notifier/{method}/test", http.HandlerFunc(testNotificationHandler)).Methods("POST") - r.Handle("/settings/export", http.HandlerFunc(exportHandler)).Methods("GET") - r.Handle("/plugins/download/{name}", http.HandlerFunc(pluginsDownloadHandler)) - r.Handle("/plugins/{name}/save", http.HandlerFunc(pluginSavedHandler)).Methods("POST") - r.Handle("/help", http.HandlerFunc(helpHandler)) - r.Handle("/logs", http.HandlerFunc(logsHandler)) - r.Handle("/logs/line", http.HandlerFunc(logsLineHandler)) - // SERVICE API Routes + // API SERVICE Routes r.Handle("/api/services", http.HandlerFunc(apiAllServicesHandler)).Methods("GET") r.Handle("/api/services", http.HandlerFunc(apiCreateServiceHandler)).Methods("POST") r.Handle("/api/services/{id}", http.HandlerFunc(apiServiceHandler)).Methods("GET") @@ -91,7 +97,7 @@ func Router() *mux.Router { r.Handle("/api/services/{id}", http.HandlerFunc(apiServiceUpdateHandler)).Methods("POST") r.Handle("/api/services/{id}", http.HandlerFunc(apiServiceDeleteHandler)).Methods("DELETE") - // USER API Routes + // API USER Routes r.Handle("/api/users", http.HandlerFunc(apiAllUsersHandler)).Methods("GET") r.Handle("/api/users", http.HandlerFunc(apiCreateUsersHandler)).Methods("POST") r.Handle("/api/users/{id}", http.HandlerFunc(apiUserHandler)).Methods("GET") @@ -103,8 +109,8 @@ func Router() *mux.Router { r.Handle("/api/renew", http.HandlerFunc(apiRenewHandler)) r.Handle("/api/checkin/{api}", http.HandlerFunc(apiCheckinHandler)) r.Handle("/metrics", http.HandlerFunc(prometheusHandler)) - r.NotFoundHandler = http.HandlerFunc(error404Handler) r.Handle("/tray", http.HandlerFunc(trayHandler)) + r.NotFoundHandler = http.HandlerFunc(error404Handler) return r } diff --git a/handlers/services.go b/handlers/services.go index 2963399c..1f7345eb 100644 --- a/handlers/services.go +++ b/handlers/services.go @@ -268,7 +268,6 @@ func checkinCreateUpdateHandler(w http.ResponseWriter, r *http.Request) { Service: service.Id, Interval: interval, GracePeriod: grace, - ApiKey: utils.NewSHA1Hash(18), }) checkin.Create() executeResponse(w, r, "service.html", service, fmt.Sprintf("/service/%v", service.Id)) diff --git a/source/tmpl/form_service.html b/source/tmpl/form_service.html index 61f05e41..3daefc9f 100644 --- a/source/tmpl/form_service.html +++ b/source/tmpl/form_service.html @@ -52,7 +52,7 @@ -
+
diff --git a/source/tmpl/service.html b/source/tmpl/service.html index fb1c0c7a..8886a325 100644 --- a/source/tmpl/service.html +++ b/source/tmpl/service.html @@ -96,18 +96,27 @@
-
-

Service Checkins

- {{ range $s.Checkins }} -
Check #{{.Id}} Checked in
- -
    - {{ range .Hits }} -
  • Received Checkin at {{.CreatedAt}}
  • - {{end}} -
- {{ end }} - +
+

Service Checkin

+ + + + + + + + + + + {{$ch := $s.Checkin}} + + + + + + + +
CheckinReport Period
Grace Period
Last SeenExpected
{{CoreApp.Domain}}/checkin/{{$ch.ApiKey}}every {{Duration $ch.Period}}
{{Duration $ch.Grace}} grace period
{{Ago $ch.Last.CreatedAt}}{{ if lt $ch.Expected 0}}{{Duration $ch.Expected}} ago{{else}}in {{Duration $ch.Expected}}{{end}}
{{template "form_checkin" $s}}
diff --git a/types/checkin.go b/types/checkin.go index bf2b1e49..efe1e995 100644 --- a/types/checkin.go +++ b/types/checkin.go @@ -20,14 +20,13 @@ import ( ) type Checkin struct { - Id int64 `gorm:"primary_key;column:id"` - Service int64 `gorm:"index;column:service"` - Interval int64 `gorm:"column:check_interval"` - GracePeriod int64 `gorm:"column:grace_period"` - ApiKey string `gorm:"column:api_key"` - CreatedAt time.Time `gorm:"column:created_at" json:"created_at"` - UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"` - Hits []CheckinHit `json:"hits"` + Id int64 `gorm:"primary_key;column:id"` + Service int64 `gorm:"index;column:service"` + Interval int64 `gorm:"column:check_interval"` + GracePeriod int64 `gorm:"column:grace_period"` + ApiKey string `gorm:"column:api_key"` + CreatedAt time.Time `gorm:"column:created_at" json:"created_at"` + UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"` } type CheckinHit struct { diff --git a/types/service.go b/types/service.go index 72878cb9..459846d9 100644 --- a/types/service.go +++ b/types/service.go @@ -46,7 +46,7 @@ type Service struct { LastStatusCode int `gorm:"-" json:"status_code"` LastOnline time.Time `gorm:"-" json:"last_online"` Failures []interface{} `gorm:"-" json:"failures,omitempty"` - Checkins []*Checkin `gorm:"-" json:"checkins,omitempty"` + //Checkins []*Checkin `gorm:"-" json:"checkins,omitempty"` } type ServiceInterface interface {