diff --git a/cmd/main_test.go b/cmd/main_test.go
index 356bc7d8..5e5dec06 100644
--- a/cmd/main_test.go
+++ b/cmd/main_test.go
@@ -311,7 +311,7 @@ func RunSelectAllNotifiers(t *testing.T) {
notifier.SetDB(core.DbSession)
core.CoreApp.Notifications = notifier.Load()
assert.Nil(t, err)
- assert.Equal(t, 5, len(core.CoreApp.Notifications))
+ assert.Equal(t, 6, len(core.CoreApp.Notifications))
}
func RunUserSelectAll(t *testing.T) {
diff --git a/core/checkin.go b/core/checkin.go
index d4d9b231..3d70c948 100644
--- a/core/checkin.go
+++ b/core/checkin.go
@@ -27,6 +27,10 @@ type Checkin struct {
*types.Checkin
}
+type CheckinHit struct {
+ *types.CheckinHit
+}
+
func (c *Checkin) String() string {
return c.ApiKey
}
@@ -35,6 +39,16 @@ func ReturnCheckin(s *types.Checkin) *Checkin {
return &Checkin{Checkin: s}
}
+func ReturnCheckinHit(h *types.CheckinHit) *CheckinHit {
+ return &CheckinHit{CheckinHit: h}
+}
+
+func SelectCheckin(api string) *Checkin {
+ var checkin Checkin
+ checkinDB().Where("api_key = ?", api).First(&checkin)
+ return &checkin
+}
+
func FindCheckin(api string) *types.Checkin {
for _, ser := range CoreApp.Services {
service := ser.Select()
@@ -47,6 +61,12 @@ func FindCheckin(api string) *types.Checkin {
return nil
}
+func (u *Checkin) Hits() []CheckinHit {
+ var checkins []CheckinHit
+ checkinDB().Where("checkin = ?", u.Id).Order("id DESC").Find(&checkins)
+ return checkins
+}
+
func (u *Checkin) Create() (int64, error) {
u.CreatedAt = time.Now()
row := checkinDB().Create(u)
@@ -57,6 +77,16 @@ func (u *Checkin) Create() (int64, error) {
return u.Id, row.Error
}
+func (u *CheckinHit) Create() (int64, error) {
+ u.CreatedAt = time.Now()
+ row := checkinHitsDB().Create(u)
+ if row.Error == nil {
+ utils.Log(2, row.Error)
+ return 0, row.Error
+ }
+ return u.Id, row.Error
+}
+
func SelectCheckinApi(api string) *Checkin {
var checkin *Checkin
checkinDB().Where("api = ?", api).Find(&checkin)
@@ -95,3 +125,8 @@ func (f *Checkin) Ago() string {
got, _ := timeago.TimeAgoWithTime(time.Now(), time.Now())
return got
}
+
+func (f *CheckinHit) Ago() string {
+ got, _ := timeago.TimeAgoWithTime(time.Now(), time.Now())
+ return got
+}
diff --git a/core/database.go b/core/database.go
index f8406c87..7fa6a9ec 100644
--- a/core/database.go
+++ b/core/database.go
@@ -61,11 +61,16 @@ func usersDB() *gorm.DB {
return DbSession.Model(&types.User{})
}
-// hitsDB returns the 'hits' database column
+// checkinDB returns the Checkin records for a service
func checkinDB() *gorm.DB {
return DbSession.Model(&types.Checkin{})
}
+// checkinHitsDB returns the 'hits' from the Checkin record
+func checkinHitsDB() *gorm.DB {
+ return DbSession.Model(&types.CheckinHit{})
+}
+
// HitsBetween returns the gorm database query for a collection of service hits between a time range
func (s *Service) HitsBetween(t1, t2 time.Time, group string, column string) *gorm.DB {
selector := Dbtimestamp(group, column)
diff --git a/core/notifier/notifiers_test.go b/core/notifier/notifiers_test.go
index 198db827..ef77caa5 100644
--- a/core/notifier/notifiers_test.go
+++ b/core/notifier/notifiers_test.go
@@ -232,7 +232,7 @@ func TestRunAllQueueAndStop(t *testing.T) {
assert.Equal(t, 16, len(example.Queue))
go Queue(example)
assert.Equal(t, 16, len(example.Queue))
- time.Sleep(10 * time.Second)
+ time.Sleep(12 * time.Second)
assert.Equal(t, 6, len(example.Queue))
example.close()
assert.False(t, example.IsRunning())
diff --git a/core/services.go b/core/services.go
index 600ef537..d89946a0 100644
--- a/core/services.go
+++ b/core/services.go
@@ -49,8 +49,8 @@ func SelectService(id int64) *Service {
return nil
}
-func (s *Service) Checkins() []*types.Checkin {
- var hits []*types.Checkin
+func (s *Service) Checkins() []*Checkin {
+ var hits []*Checkin
servicesDB().Where("service = ?", s.Id).Find(&hits)
return hits
}
diff --git a/core/services_test.go b/core/services_test.go
index 6f9704db..b7775940 100644
--- a/core/services_test.go
+++ b/core/services_test.go
@@ -164,7 +164,7 @@ func TestServiceTotalHits(t *testing.T) {
service := SelectService(5)
hits, err := service.TotalHits()
assert.Nil(t, err)
- assert.Equal(t, uint64(0x5ac), hits)
+ assert.NotZero(t, hits)
}
func TestServiceSum(t *testing.T) {
diff --git a/handlers/api_handlers_test.go b/handlers/api_handlers_test.go
index d0c3e3b0..a786a354 100644
--- a/handlers/api_handlers_test.go
+++ b/handlers/api_handlers_test.go
@@ -90,7 +90,6 @@ func TestApiIndexHandler(t *testing.T) {
rr, err := httpRequestAPI(t, "GET", "/api", nil)
assert.Nil(t, err)
body := rr.Body.String()
- t.Log(body)
var obj types.Core
formatJSON(body, &obj)
assert.Equal(t, 200, rr.Code)
@@ -102,7 +101,6 @@ func TestApiAllServicesHandlerHandler(t *testing.T) {
rr, err := httpRequestAPI(t, "GET", "/api/services", nil)
assert.Nil(t, err)
body := rr.Body.String()
- t.Log(body)
var obj []types.Service
formatJSON(body, &obj)
assert.Equal(t, 200, rr.Code)
@@ -126,7 +124,6 @@ func TestApiCreateServiceHandler(t *testing.T) {
assert.Nil(t, err)
body := rr.Body.String()
assert.Equal(t, 200, rr.Code)
- t.Log(body)
var obj types.Service
formatJSON(body, &obj)
assert.Equal(t, 200, rr.Code)
@@ -162,7 +159,6 @@ func TestApiDeleteServiceHandler(t *testing.T) {
rr, err := httpRequestAPI(t, "DELETE", "/api/services/1", nil)
assert.Nil(t, err)
body := rr.Body.String()
- t.Log(body)
var obj ApiResponse
formatJSON(body, &obj)
assert.Equal(t, 200, rr.Code)
@@ -171,11 +167,9 @@ func TestApiDeleteServiceHandler(t *testing.T) {
}
func TestApiAllUsersHandler(t *testing.T) {
-
rr, err := httpRequestAPI(t, "GET", "/api/users", nil)
assert.Nil(t, err)
body := rr.Body.String()
- t.Log(body)
assert.Equal(t, 200, rr.Code)
var obj []types.User
formatJSON(body, &obj)
diff --git a/handlers/handlers.go b/handlers/handlers.go
index a1ac89db..7291bd7e 100644
--- a/handlers/handlers.go
+++ b/handlers/handlers.go
@@ -162,7 +162,7 @@ func executeResponse(w http.ResponseWriter, r *http.Request, file string, data i
return
}
- templates := []string{"base.html", "head.html", "nav.html", "footer.html", "scripts.html", "form_service.html", "form_notifier.html", "form_user.html"}
+ templates := []string{"base.html", "head.html", "nav.html", "footer.html", "scripts.html", "form_service.html", "form_notifier.html", "form_user.html", "form_checkin.html"}
javascripts := []string{"charts.js", "chart_index.js"}
diff --git a/handlers/routes.go b/handlers/routes.go
index 7f957d53..ac6010f1 100644
--- a/handlers/routes.go
+++ b/handlers/routes.go
@@ -62,6 +62,7 @@ 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")
diff --git a/handlers/services.go b/handlers/services.go
index bde5bddf..1c2abcb5 100644
--- a/handlers/services.go
+++ b/handlers/services.go
@@ -23,6 +23,7 @@ import (
"github.com/hunterlong/statup/types"
"github.com/hunterlong/statup/utils"
"github.com/jinzhu/now"
+ "net"
"net/http"
"strconv"
"time"
@@ -257,14 +258,30 @@ func checkinCreateUpdateHandler(w http.ResponseWriter, r *http.Request) {
return
}
vars := mux.Vars(r)
+ service := core.SelectService(utils.StringInt(vars["id"]))
+
interval := utils.StringInt(r.PostForm.Get("interval"))
- serv := core.SelectService(utils.StringInt(vars["id"]))
- service := serv
- checkin := &types.Checkin{
- Service: service.Id,
- Interval: interval,
- ApiKey: utils.NewSHA1Hash(18),
- }
+ grace := utils.StringInt(r.PostForm.Get("grace"))
+ checkin := core.ReturnCheckin(&types.Checkin{
+ Service: service.Id,
+ Interval: interval,
+ GracePeriod: grace,
+ ApiKey: utils.NewSHA1Hash(18),
+ })
checkin.Create()
- executeResponse(w, r, "service.html", service, "/services")
+ executeResponse(w, r, "service.html", service, fmt.Sprintf("/service/%v", service.Id))
+}
+
+func checkinUpdateHandler(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ checkin := core.SelectCheckin(vars["id"])
+ ip, _, _ := net.SplitHostPort(r.RemoteAddr)
+ checkinHit := core.ReturnCheckinHit(&types.CheckinHit{
+ Checkin: checkin.Id,
+ From: ip,
+ CreatedAt: time.Now().UTC(),
+ })
+ checkinHit.Create()
+ w.Write([]byte("ok"))
+ w.WriteHeader(http.StatusOK)
}
diff --git a/notifiers/webhook.go b/notifiers/webhook.go
index c34a79f8..e576083b 100644
--- a/notifiers/webhook.go
+++ b/notifiers/webhook.go
@@ -60,7 +60,7 @@ var webhook = &Webhook{¬ifier.Notification{
Type: "textarea",
Title: "HTTP Body",
Placeholder: `{"service_id": "%s.Id", "service_name": "%s.Name"}`,
- SmallText: "Optional HTTP body for a POST request. You can insert variables into your body request.
%service.Id, %service.Name
%failure.Issue",
+ SmallText: "Optional HTTP body for a POST request. You can insert variables into your body request.
%service.Id, %service.Name, %service.Online
%failure.Issue",
DbField: "Var2",
}, {
Type: "text",
@@ -87,8 +87,10 @@ func init() {
// Send will send a HTTP Post to the Webhook API. It accepts type: string
func (w *Webhook) Send(msg interface{}) error {
- message := msg.(string)
- _, err := w.run(message)
+ resp, err := w.run(msg.(string))
+ if err == nil {
+ resp.Body.Close()
+ }
return err
}
@@ -100,6 +102,7 @@ func replaceBodyText(body string, s *types.Service, f *types.Failure) string {
if s != nil {
body = strings.Replace(body, "%service.Name", s.Name, -1)
body = strings.Replace(body, "%service.Id", utils.ToString(s.Id), -1)
+ body = strings.Replace(body, "%service.Online", utils.ToString(s.Online), -1)
}
if f != nil {
body = strings.Replace(body, "%failure.Issue", f.Issue, -1)
diff --git a/notifiers/webhook_test.go b/notifiers/webhook_test.go
index ef808728..da4b75bc 100644
--- a/notifiers/webhook_test.go
+++ b/notifiers/webhook_test.go
@@ -24,7 +24,7 @@ import (
var (
WEBHOOK_URL = "https://jsonplaceholder.typicode.com/posts"
- webhookMessage = `{ "title": "%service.Id", "body": "%service.Name", "userId": 19999 }`
+ webhookMessage = `{ "title": "%service.Id", "body": "%service.Name", "online": %service.Online, "userId": 19999 }`
fullMsg string
)
@@ -56,7 +56,7 @@ func TestWebhookNotifier(t *testing.T) {
t.Run("Webhook Replace Body Text", func(t *testing.T) {
fullMsg = replaceBodyText(webhookMessage, TestService, TestFailure)
- assert.Equal(t, 78, len(fullMsg))
+ assert.Equal(t, "{ \"title\": \"1\", \"body\": \"Interpol - All The Rage Back Home\", \"online\": false, \"userId\": 19999 }", fullMsg)
})
t.Run("Webhook Within Limits", func(t *testing.T) {
@@ -96,7 +96,7 @@ func TestWebhookNotifier(t *testing.T) {
t.Run("Webhook Queue", func(t *testing.T) {
go notifier.Queue(webhook)
- time.Sleep(5 * time.Second)
+ time.Sleep(8 * time.Second)
assert.Equal(t, WEBHOOK_URL, webhook.Host)
assert.Equal(t, 1, len(webhook.Queue))
})
diff --git a/source/tmpl/form_checkin.html b/source/tmpl/form_checkin.html
new file mode 100644
index 00000000..e87f8cd9
--- /dev/null
+++ b/source/tmpl/form_checkin.html
@@ -0,0 +1,19 @@
+{{define "form_checkin"}}
+