template update - checkins/failure API

pull/116/head
Hunter Long 2018-12-06 11:03:55 -08:00
parent cdcfa2f481
commit 0a22788385
53 changed files with 179 additions and 141 deletions

View File

@ -168,7 +168,7 @@ func ExportIndexHTML() []byte {
} }
w := httptest.NewRecorder() w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", nil) r := httptest.NewRequest("GET", "/", nil)
handlers.ExecuteResponse(w, r, "index.html", nil, nil) handlers.ExecuteResponse(w, r, "index.gohtml", nil, nil)
return w.Body.Bytes() return w.Body.Bytes()
} }

View File

@ -236,10 +236,10 @@ func recordSuccess(s *Service) {
notifier.OnSuccess(s.Service) notifier.OnSuccess(s.Service)
} }
// recordFailure will create a new 'failure' record in the database for a offline service // recordFailure will create a new 'Failure' record in the database for a offline service
func recordFailure(s *Service, issue string) { func recordFailure(s *Service, issue string) {
s.Online = false s.Online = false
fail := &failure{&types.Failure{ fail := &Failure{&types.Failure{
Service: s.Id, Service: s.Id,
Issue: issue, Issue: issue,
PingTime: s.PingTime, PingTime: s.PingTime,

View File

@ -20,6 +20,7 @@ import (
"github.com/ararog/timeago" "github.com/ararog/timeago"
"github.com/hunterlong/statping/types" "github.com/hunterlong/statping/types"
"github.com/hunterlong/statping/utils" "github.com/hunterlong/statping/utils"
"sort"
"time" "time"
) )
@ -31,6 +32,11 @@ type CheckinHit struct {
*types.CheckinHit *types.CheckinHit
} }
// Select returns a *types.Checkin
func (c *Checkin) Select() *types.Checkin {
return c.Checkin
}
// Routine for checking if the last Checkin was within its interval // Routine for checking if the last Checkin was within its interval
func (c *Checkin) Routine() { func (c *Checkin) Routine() {
if c.Last() == nil { if c.Last() == nil {
@ -42,13 +48,13 @@ CheckinLoop:
select { select {
case <-c.Running: case <-c.Running:
utils.Log(1, fmt.Sprintf("Stopping checkin routine: %v", c.Name)) utils.Log(1, fmt.Sprintf("Stopping checkin routine: %v", c.Name))
c.Failing = false
break CheckinLoop break CheckinLoop
case <-time.After(reCheck): case <-time.After(reCheck):
utils.Log(1, fmt.Sprintf("Checkin %v is expected at %v, checking every %v", c.Name, utils.FormatDuration(c.Expected()), utils.FormatDuration(c.Period()))) utils.Log(1, fmt.Sprintf("Checkin %v is expected at %v, checking every %v", c.Name, utils.FormatDuration(c.Expected()), utils.FormatDuration(c.Period())))
if c.Expected().Seconds() <= 0 { if c.Expected().Seconds() <= 0 {
issue := fmt.Sprintf("Checkin %v is failing, no request since %v", c.Name, c.Last().CreatedAt) issue := fmt.Sprintf("Checkin %v is failing, no request since %v", c.Name, c.Last().CreatedAt)
utils.Log(3, issue) utils.Log(3, issue)
c.Service()
c.CreateFailure() c.CreateFailure()
} }
reCheck = c.Period() reCheck = c.Period()
@ -73,23 +79,36 @@ func ReturnCheckinHit(c *types.CheckinHit) *CheckinHit {
} }
func (c *Checkin) Service() *Service { func (c *Checkin) Service() *Service {
service := SelectService(c.ServiceId) return SelectService(c.ServiceId)
return service
} }
func (c *Checkin) CreateFailure() (int64, error) { func (c *Checkin) CreateFailure() (int64, error) {
service := c.Service() service := c.Service()
fail := &types.Failure{ c.Failing = true
fail := &Failure{&types.Failure{
Issue: fmt.Sprintf("Checkin %v was not reported %v ago, it expects a request every %v", c.Name, utils.FormatDuration(c.Expected()), utils.FormatDuration(c.Period())), Issue: fmt.Sprintf("Checkin %v was not reported %v ago, it expects a request every %v", c.Name, utils.FormatDuration(c.Expected()), utils.FormatDuration(c.Period())),
Method: "checkin", Method: "checkin",
MethodId: c.Id, MethodId: c.Id,
Service: service.Id, Service: service.Id,
PingTime: c.Expected().Seconds() * 0.001, Checkin: c.Id,
} PingTime: c.Expected().Seconds(),
}}
row := failuresDB().Create(&fail) row := failuresDB().Create(&fail)
sort.Sort(types.FailSort(c.Failures))
c.Failures = append(c.Failures, fail)
if len(c.Failures) > limitedFailures {
c.Failures = c.Failures[1:]
}
return fail.Id, row.Error return fail.Id, row.Error
} }
// LimitedHits will return the last amount of successful hits from a checkin
func (c *Checkin) LimitedHits(amount int64) []*types.CheckinHit {
var hits []*types.CheckinHit
checkinHitsDB().Where("checkin = ?", c.Id).Order("id desc").Limit(amount).Find(&hits)
return hits
}
// AllCheckins returns all checkin in system // AllCheckins returns all checkin in system
func AllCheckins() []*Checkin { func AllCheckins() []*Checkin {
var checkins []*Checkin var checkins []*Checkin
@ -99,16 +118,14 @@ func AllCheckins() []*Checkin {
// SelectCheckin will find a Checkin based on the API supplied // SelectCheckin will find a Checkin based on the API supplied
func SelectCheckin(api string) *Checkin { func SelectCheckin(api string) *Checkin {
var checkin Checkin for _, s := range Services() {
checkinDB().Where("api_key = ?", api).First(&checkin) for _, c := range s.Select().Checkins {
return &checkin if c.Select().ApiKey == api {
} return c.(*Checkin)
}
// SelectCheckin will find a Checkin based on the API supplied }
func SelectCheckinId(id int64) *Checkin { }
var checkin Checkin return nil
checkinDB().Where("id = ?", id).First(&checkin)
return &checkin
} }
// Period will return the duration of the Checkin interval // Period will return the duration of the Checkin interval
@ -150,6 +167,18 @@ func (c *Checkin) AllHits() []*types.CheckinHit {
return checkins return checkins
} }
// Hits returns all of the CheckinHits for a given Checkin
func (c *Checkin) LimitedFailures(amount int64) []types.FailureInterface {
var failures []*Failure
var failInterfaces []types.FailureInterface
col := failuresDB().Where("checkin = ?", c.Id).Where("method = 'checkin'").Limit(amount).Order("id desc")
col.Find(&failures)
for _, f := range failures {
failInterfaces = append(failInterfaces, f)
}
return failInterfaces
}
// Hits returns all of the CheckinHits for a given Checkin // Hits returns all of the CheckinHits for a given Checkin
func (c *Checkin) AllFailures() []*types.Failure { func (c *Checkin) AllFailures() []*types.Failure {
var failures []*types.Failure var failures []*types.Failure
@ -169,12 +198,14 @@ func (c *Checkin) Delete() error {
func (c *Checkin) Create() (int64, error) { func (c *Checkin) Create() (int64, error) {
c.ApiKey = utils.RandomString(7) c.ApiKey = utils.RandomString(7)
row := checkinDB().Create(&c) row := checkinDB().Create(&c)
c.Start()
go c.Routine()
if row.Error != nil { if row.Error != nil {
utils.Log(2, row.Error) utils.Log(2, row.Error)
return 0, row.Error return 0, row.Error
} }
service := SelectService(c.ServiceId)
service.Checkins = append(service.Checkins, c)
c.Start()
go c.Routine()
return c.Id, row.Error return c.Id, row.Error
} }

View File

@ -120,8 +120,8 @@ func (h *Hit) AfterFind() (err error) {
return return
} }
// AfterFind for failure will set the timezone // AfterFind for Failure will set the timezone
func (f *failure) AfterFind() (err error) { func (f *Failure) AfterFind() (err error) {
f.CreatedAt = utils.Timezoner(f.CreatedAt, CoreApp.Timezone) f.CreatedAt = utils.Timezoner(f.CreatedAt, CoreApp.Timezone)
return return
} }
@ -163,8 +163,8 @@ func (h *Hit) BeforeCreate() (err error) {
return return
} }
// BeforeCreate for failure will set CreatedAt to UTC // BeforeCreate for Failure will set CreatedAt to UTC
func (f *failure) BeforeCreate() (err error) { func (f *Failure) BeforeCreate() (err error) {
if f.CreatedAt.IsZero() { if f.CreatedAt.IsZero() {
f.CreatedAt = time.Now().UTC() f.CreatedAt = time.Now().UTC()
} }

View File

@ -25,7 +25,7 @@ import (
"time" "time"
) )
type failure struct { type Failure struct {
*types.Failure *types.Failure
} }
@ -33,9 +33,9 @@ const (
limitedFailures = 32 limitedFailures = 32
) )
// CreateFailure will create a new failure record for a service // CreateFailure will create a new Failure record for a service
func (s *Service) CreateFailure(fail types.FailureInterface) (int64, error) { func (s *Service) CreateFailure(fail types.FailureInterface) (int64, error) {
f := fail.(*failure) f := fail.(*Failure)
f.Service = s.Id f.Service = s.Id
row := failuresDB().Create(f) row := failuresDB().Create(f)
if row.Error != nil { if row.Error != nil {
@ -51,8 +51,8 @@ func (s *Service) CreateFailure(fail types.FailureInterface) (int64, error) {
} }
// AllFailures will return all failures attached to a service // AllFailures will return all failures attached to a service
func (s *Service) AllFailures() []*failure { func (s *Service) AllFailures() []*Failure {
var fails []*failure var fails []*Failure
col := failuresDB().Where("service = ?", s.Id).Not("method = 'checkin'").Order("id desc") col := failuresDB().Where("service = ?", s.Id).Not("method = 'checkin'").Order("id desc")
err := col.Find(&fails) err := col.Find(&fails)
if err.Error != nil { if err.Error != nil {
@ -72,32 +72,32 @@ func (s *Service) DeleteFailures() {
} }
// LimitedFailures will return the last amount of failures from a service // LimitedFailures will return the last amount of failures from a service
func (s *Service) LimitedFailures(amount int64) []*failure { func (s *Service) LimitedFailures(amount int64) []*Failure {
var failArr []*failure var failArr []*Failure
failuresDB().Where("service = ?", s.Id).Not("method = 'checkin'").Order("id desc").Limit(amount).Find(&failArr) failuresDB().Where("service = ?", s.Id).Not("method = 'checkin'").Order("id desc").Limit(amount).Find(&failArr)
return failArr return failArr
} }
// LimitedFailures will return the last amount of failures from a service // LimitedFailures will return the last amount of failures from a service
func (s *Service) LimitedCheckinFailures(amount int64) []*failure { func (s *Service) LimitedCheckinFailures(amount int64) []*Failure {
var failArr []*failure var failArr []*Failure
failuresDB().Where("service = ?", s.Id).Where("method = 'checkin'").Order("id desc").Limit(amount).Find(&failArr) failuresDB().Where("service = ?", s.Id).Where("method = 'checkin'").Order("id desc").Limit(amount).Find(&failArr)
return failArr return failArr
} }
// Ago returns a human readable timestamp for a failure // Ago returns a human readable timestamp for a Failure
func (f *failure) Ago() string { func (f *Failure) Ago() string {
got, _ := timeago.TimeAgoWithTime(time.Now(), f.CreatedAt) got, _ := timeago.TimeAgoWithTime(time.Now(), f.CreatedAt)
return got return got
} }
// Select returns a *types.Failure // Select returns a *types.Failure
func (f *failure) Select() *types.Failure { func (f *Failure) Select() *types.Failure {
return f.Failure return f.Failure
} }
// Delete will remove a failure record from the database // Delete will remove a Failure record from the database
func (f *failure) Delete() error { func (f *Failure) Delete() error {
db := failuresDB().Delete(f) db := failuresDB().Delete(f)
return db.Error return db.Error
} }
@ -146,8 +146,8 @@ func (s *Service) TotalFailuresSince(ago time.Time) (uint64, error) {
return count, err.Error return count, err.Error
} }
// ParseError returns a human readable error for a failure // ParseError returns a human readable error for a Failure
func (f *failure) ParseError() string { func (f *Failure) ParseError() string {
if f.Method == "checkin" { if f.Method == "checkin" {
return fmt.Sprintf("Checkin is Offline") return fmt.Sprintf("Checkin is Offline")
} }

View File

@ -359,13 +359,13 @@ func InsertLargeSampleData() error {
func insertFailureRecords(since time.Time, amount int64) { func insertFailureRecords(since time.Time, amount int64) {
for i := int64(14); i <= 15; i++ { for i := int64(14); i <= 15; i++ {
service := SelectService(i) service := SelectService(i)
utils.Log(1, fmt.Sprintf("Adding %v failure records to service %v", amount, service.Name)) utils.Log(1, fmt.Sprintf("Adding %v Failure records to service %v", amount, service.Name))
createdAt := since createdAt := since
for fi := int64(1); fi <= amount; fi++ { for fi := int64(1); fi <= amount; fi++ {
createdAt = createdAt.Add(2 * time.Minute) createdAt = createdAt.Add(2 * time.Minute)
failure := &failure{&types.Failure{ failure := &Failure{&types.Failure{
Service: service.Id, Service: service.Id,
Issue: "testing right here", Issue: "testing right here",
CreatedAt: createdAt, CreatedAt: createdAt,

View File

@ -55,39 +55,22 @@ func SelectService(id int64) *Service {
return nil return nil
} }
// SelectServicer returns a types.ServiceInterface from in memory
func SelectServicer(id int64) types.ServiceInterface {
for _, s := range Services() {
if s.Select().Id == id {
return s
}
}
return nil
}
// CheckinProcess runs the checkin routine for each checkin attached to service // CheckinProcess runs the checkin routine for each checkin attached to service
func (s *Service) CheckinProcess() { func (s *Service) CheckinProcess() {
checkins := s.Checkins() checkins := s.AllCheckins()
for _, c := range checkins { for _, c := range checkins {
c.Start() c.Start()
go c.Routine() go c.Routine()
} }
} }
// Checkins will return a slice of Checkins for a Service // AllCheckins will return a slice of AllCheckins for a Service
func (s *Service) Checkins() []*Checkin { func (s *Service) AllCheckins() []*Checkin {
var checkin []*Checkin var checkin []*Checkin
checkinDB().Where("service = ?", s.Id).Find(&checkin) checkinDB().Where("service = ?", s.Id).Find(&checkin)
return checkin return checkin
} }
// LimitedCheckins will return a slice of Checkins for a Service
func (s *Service) LimitedCheckins() []*Checkin {
var checkin []*Checkin
checkinDB().Where("service = ?", s.Id).Limit(10).Find(&checkin)
return checkin
}
// SelectAllServices returns a slice of *core.Service to be store on []*core.Services, should only be called once on startup. // SelectAllServices returns a slice of *core.Service to be store on []*core.Services, should only be called once on startup.
func (c *Core) SelectAllServices(start bool) ([]*Service, error) { func (c *Core) SelectAllServices(start bool) ([]*Service, error) {
var services []*Service var services []*Service
@ -106,9 +89,15 @@ func (c *Core) SelectAllServices(start bool) ([]*Service, error) {
for _, f := range fails { for _, f := range fails {
service.Failures = append(service.Failures, f) service.Failures = append(service.Failures, f)
} }
checkins := service.AllCheckins()
for _, c := range checkins {
c.Failures = c.LimitedFailures(limitedFailures)
c.Hits = c.LimitedHits(limitedFailures)
service.Checkins = append(service.Checkins, c)
}
CoreApp.Services = append(CoreApp.Services, service) CoreApp.Services = append(CoreApp.Services, service)
} }
sort.Sort(ServiceOrder(CoreApp.Services)) reorderServices()
return services, db.Error return services, db.Error
} }
@ -175,8 +164,8 @@ type DateScanObj struct {
Array []DateScan `json:"data"` Array []DateScan `json:"data"`
} }
// lastFailure returns the last failure a service had // lastFailure returns the last Failure a service had
func (s *Service) lastFailure() *failure { func (s *Service) lastFailure() *Failure {
limited := s.LimitedFailures(1) limited := s.LimitedFailures(1)
if len(limited) == 0 { if len(limited) == 0 {
return nil return nil
@ -196,7 +185,7 @@ func (s *Service) SmallText() string {
if len(last) == 0 { if len(last) == 0 {
return fmt.Sprintf("Online since %v", utils.Timezoner(s.CreatedAt, zone).Format("Monday 3:04:05PM, Jan _2 2006")) return fmt.Sprintf("Online since %v", utils.Timezoner(s.CreatedAt, zone).Format("Monday 3:04:05PM, Jan _2 2006"))
} else { } else {
return fmt.Sprintf("Online, last failure was %v", utils.Timezoner(hits[0].CreatedAt, zone).Format("Monday 3:04:05PM, Jan _2 2006")) return fmt.Sprintf("Online, last Failure was %v", utils.Timezoner(hits[0].CreatedAt, zone).Format("Monday 3:04:05PM, Jan _2 2006"))
} }
} }
if len(last) > 0 { if len(last) > 0 {

View File

@ -46,7 +46,7 @@ func TestCreateCheckin(t *testing.T) {
func TestSelectCheckin(t *testing.T) { func TestSelectCheckin(t *testing.T) {
service := SelectService(1) service := SelectService(1)
checkins := service.Checkins() checkins := service.AllCheckins()
assert.NotNil(t, checkins) assert.NotNil(t, checkins)
assert.Equal(t, 1, len(checkins)) assert.Equal(t, 1, len(checkins))
testCheckin = checkins[0] testCheckin = checkins[0]
@ -63,7 +63,7 @@ func TestUpdateCheckin(t *testing.T) {
assert.NotZero(t, id) assert.NotZero(t, id)
assert.NotEmpty(t, testCheckin.ApiKey) assert.NotEmpty(t, testCheckin.ApiKey)
service := SelectService(1) service := SelectService(1)
checkin := service.Checkins()[0] checkin := service.AllCheckins()[0]
assert.Equal(t, int64(60), checkin.Interval) assert.Equal(t, int64(60), checkin.Interval)
assert.Equal(t, int64(15), checkin.GracePeriod) assert.Equal(t, int64(15), checkin.GracePeriod)
t.Log(testCheckin.Expected()) t.Log(testCheckin.Expected())
@ -72,7 +72,7 @@ func TestUpdateCheckin(t *testing.T) {
func TestCreateCheckinHits(t *testing.T) { func TestCreateCheckinHits(t *testing.T) {
service := SelectService(1) service := SelectService(1)
checkins := service.Checkins() checkins := service.AllCheckins()
assert.Equal(t, 1, len(checkins)) assert.Equal(t, 1, len(checkins))
created := time.Now().UTC().Add(-60 * time.Second) created := time.Now().UTC().Add(-60 * time.Second)
hit := ReturnCheckinHit(&types.CheckinHit{ hit := ReturnCheckinHit(&types.CheckinHit{
@ -88,7 +88,7 @@ func TestCreateCheckinHits(t *testing.T) {
func TestSelectCheckinMethods(t *testing.T) { func TestSelectCheckinMethods(t *testing.T) {
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)
service := SelectService(1) service := SelectService(1)
checkins := service.Checkins() checkins := service.AllCheckins()
assert.NotNil(t, checkins) assert.NotNil(t, checkins)
lastHit := testCheckin.Last() lastHit := testCheckin.Last()
assert.Equal(t, float64(60), testCheckin.Period().Seconds()) assert.Equal(t, float64(60), testCheckin.Period().Seconds())

View File

@ -256,7 +256,7 @@ func TestServiceFailedTCPCheck(t *testing.T) {
} }
func TestCreateServiceFailure(t *testing.T) { func TestCreateServiceFailure(t *testing.T) {
fail := &failure{&types.Failure{ fail := &Failure{&types.Failure{
Issue: "This is not an issue, but it would container HTTP response errors.", Issue: "This is not an issue, but it would container HTTP response errors.",
Method: "http", Method: "http",
}} }}

View File

@ -21,6 +21,7 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/hunterlong/statping/core" "github.com/hunterlong/statping/core"
"github.com/hunterlong/statping/types" "github.com/hunterlong/statping/types"
"github.com/hunterlong/statping/utils"
"net" "net"
"net/http" "net/http"
"time" "time"
@ -34,7 +35,7 @@ func apiAllCheckinsHandler(w http.ResponseWriter, r *http.Request) {
checkins := core.AllCheckins() checkins := core.AllCheckins()
for _, c := range checkins { for _, c := range checkins {
c.Hits = c.AllHits() c.Hits = c.AllHits()
c.Failures = c.AllFailures() c.Failures = c.LimitedFailures(64)
} }
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(checkins) json.NewEncoder(w).Encode(checkins)
@ -51,8 +52,8 @@ func apiCheckinHandler(w http.ResponseWriter, r *http.Request) {
sendErrorJson(fmt.Errorf("checkin %v was not found", vars["api"]), w, r) sendErrorJson(fmt.Errorf("checkin %v was not found", vars["api"]), w, r)
return return
} }
checkin.Hits = checkin.AllHits() checkin.Hits = checkin.LimitedHits(32)
checkin.Failures = checkin.AllFailures() checkin.Failures = checkin.LimitedFailures(32)
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(checkin) json.NewEncoder(w).Encode(checkin)
} }
@ -69,6 +70,11 @@ func checkinCreateHandler(w http.ResponseWriter, r *http.Request) {
sendErrorJson(err, w, r) sendErrorJson(err, w, r)
return return
} }
service := core.SelectService(checkin.ServiceId)
if service == nil {
sendErrorJson(fmt.Errorf("missing service_id field"), w, r)
return
}
_, err = checkin.Create() _, err = checkin.Create()
if err != nil { if err != nil {
sendErrorJson(err, w, r) sendErrorJson(err, w, r)
@ -85,20 +91,25 @@ func checkinHitHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
ip, _, _ := net.SplitHostPort(r.RemoteAddr) ip, _, _ := net.SplitHostPort(r.RemoteAddr)
checkinHit := core.ReturnCheckinHit(&types.CheckinHit{
hit := &types.CheckinHit{
Checkin: checkin.Id, Checkin: checkin.Id,
From: ip, From: ip,
CreatedAt: time.Now().UTC(), CreatedAt: time.Now().UTC(),
}) }
checkinHit := core.ReturnCheckinHit(hit)
if checkin.Last() == nil { if checkin.Last() == nil {
checkin.Start() checkin.Start()
go checkin.Routine() go checkin.Routine()
} }
_, err := checkinHit.Create() _, err := checkinHit.Create()
checkin.Hits = append(checkin.Hits, checkinHit.CheckinHit)
if err != nil { if err != nil {
sendErrorJson(err, w, r) sendErrorJson(err, w, r)
return return
} }
checkin.Failing = false
checkin.LastHit = utils.Timezoner(time.Now().UTC(), core.CoreApp.Timezone)
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
sendJsonAction(checkinHit, "update", w, r) sendJsonAction(checkinHit, "update", w, r)
} }

View File

@ -29,9 +29,9 @@ import (
func dashboardHandler(w http.ResponseWriter, r *http.Request) { func dashboardHandler(w http.ResponseWriter, r *http.Request) {
if !IsAuthenticated(r) { if !IsAuthenticated(r) {
err := core.ErrorResponse{} err := core.ErrorResponse{}
ExecuteResponse(w, r, "login.html", err, nil) ExecuteResponse(w, r, "login.gohtml", err, nil)
} else { } else {
ExecuteResponse(w, r, "dashboard.html", core.CoreApp, nil) ExecuteResponse(w, r, "dashboard.gohtml", core.CoreApp, nil)
} }
} }
@ -51,7 +51,7 @@ func loginHandler(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/dashboard", http.StatusSeeOther) http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
} else { } else {
err := core.ErrorResponse{Error: "Incorrect login information submitted, try again."} err := core.ErrorResponse{Error: "Incorrect login information submitted, try again."}
ExecuteResponse(w, r, "login.html", err, nil) ExecuteResponse(w, r, "login.gohtml", err, nil)
} }
} }
@ -68,7 +68,7 @@ func helpHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
help := source.HelpMarkdown() help := source.HelpMarkdown()
ExecuteResponse(w, r, "help.html", help, nil) ExecuteResponse(w, r, "help.gohtml", help, nil)
} }
func logsHandler(w http.ResponseWriter, r *http.Request) { func logsHandler(w http.ResponseWriter, r *http.Request) {
@ -84,7 +84,7 @@ func logsHandler(w http.ResponseWriter, r *http.Request) {
logs = append(logs, utils.LastLines[i].FormatForHtml()+"\r\n") logs = append(logs, utils.LastLines[i].FormatForHtml()+"\r\n")
} }
utils.LockLines.Unlock() utils.LockLines.Unlock()
ExecuteResponse(w, r, "logs.html", logs, nil) ExecuteResponse(w, r, "logs.gohtml", logs, nil)
} }
func logsLineHandler(w http.ResponseWriter, r *http.Request) { func logsLineHandler(w http.ResponseWriter, r *http.Request) {

View File

@ -198,7 +198,7 @@ func ExecuteResponse(w http.ResponseWriter, r *http.Request, file string, data i
return return
} }
templates := []string{"base.html", "head.html", "nav.html", "footer.html", "scripts.html", "form_service.html", "form_notifier.html", "form_user.html", "form_checkin.html", "form_message.html"} templates := []string{"base.gohtml", "head.gohtml", "nav.gohtml", "footer.gohtml", "scripts.gohtml", "form_service.gohtml", "form_notifier.gohtml", "form_user.gohtml", "form_checkin.gohtml", "form_message.gohtml"}
javascripts := []string{"charts.js", "chart_index.js"} javascripts := []string{"charts.js", "chart_index.js"}
@ -275,5 +275,5 @@ func executeJSResponse(w http.ResponseWriter, r *http.Request, file string, data
// error404Handler is a HTTP handler for 404 error pages // error404Handler is a HTTP handler for 404 error pages
func error404Handler(w http.ResponseWriter, r *http.Request) { func error404Handler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound) w.WriteHeader(http.StatusNotFound)
ExecuteResponse(w, r, "error_404.html", nil, nil) ExecuteResponse(w, r, "error_404.gohtml", nil, nil)
} }

View File

@ -28,7 +28,7 @@ func indexHandler(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/setup", http.StatusSeeOther) http.Redirect(w, r, "/setup", http.StatusSeeOther)
return return
} }
ExecuteResponse(w, r, "index.html", core.CoreApp, nil) ExecuteResponse(w, r, "index.gohtml", core.CoreApp, nil)
} }
func healthCheckHandler(w http.ResponseWriter, r *http.Request) { func healthCheckHandler(w http.ResponseWriter, r *http.Request) {
@ -41,7 +41,7 @@ func healthCheckHandler(w http.ResponseWriter, r *http.Request) {
} }
func trayHandler(w http.ResponseWriter, r *http.Request) { func trayHandler(w http.ResponseWriter, r *http.Request) {
ExecuteResponse(w, r, "tray.html", core.CoreApp, nil) ExecuteResponse(w, r, "tray.gohtml", core.CoreApp, nil)
} }
// DesktopInit will run the Statping server on a specific IP and port using SQLite database // DesktopInit will run the Statping server on a specific IP and port using SQLite database

View File

@ -31,7 +31,7 @@ func messagesHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
messages, _ := core.SelectMessages() messages, _ := core.SelectMessages()
ExecuteResponse(w, r, "messages.html", messages, nil) ExecuteResponse(w, r, "messages.gohtml", messages, nil)
} }
func viewMessageHandler(w http.ResponseWriter, r *http.Request) { func viewMessageHandler(w http.ResponseWriter, r *http.Request) {
@ -46,7 +46,7 @@ func viewMessageHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound) w.WriteHeader(http.StatusNotFound)
return return
} }
ExecuteResponse(w, r, "message.html", message, nil) ExecuteResponse(w, r, "message.gohtml", message, nil)
} }
func apiAllMessagesHandler(w http.ResponseWriter, r *http.Request) { func apiAllMessagesHandler(w http.ResponseWriter, r *http.Request) {

View File

@ -104,7 +104,7 @@ func testNotificationHandler(w http.ResponseWriter, r *http.Request) {
fakeNotifer, notif, err := notifier.SelectNotifier(method) fakeNotifer, notif, err := notifier.SelectNotifier(method)
if err != nil { if err != nil {
utils.Log(3, fmt.Sprintf("issue saving notifier %v: %v", method, err)) utils.Log(3, fmt.Sprintf("issue saving notifier %v: %v", method, err))
ExecuteResponse(w, r, "settings.html", core.CoreApp, "/settings") ExecuteResponse(w, r, "settings.gohtml", core.CoreApp, "/settings")
return return
} }

View File

@ -162,7 +162,7 @@ func TestApiUpdateServiceHandler(t *testing.T) {
} }
func TestApiDeleteServiceHandler(t *testing.T) { func TestApiDeleteServiceHandler(t *testing.T) {
rr, err := httpRequestAPI(t, "DELETE", "/api/services/1", nil) rr, err := httpRequestAPI(t, "DELETE", "/api/services/2", nil)
assert.Nil(t, err) assert.Nil(t, err)
body := rr.Body.String() body := rr.Body.String()
var obj apiResponse var obj apiResponse
@ -270,7 +270,7 @@ func TestApiRenewHandler(t *testing.T) {
func TestApiCheckinHandler(t *testing.T) { func TestApiCheckinHandler(t *testing.T) {
t.SkipNow() t.SkipNow()
service := core.SelectService(1) service := core.SelectService(1)
checkin := service.Checkins() checkin := service.AllCheckins()
rr, err := httpRequestAPI(t, "GET", "/api/checkin/"+checkin[0].ApiKey, nil) rr, err := httpRequestAPI(t, "GET", "/api/checkin/"+checkin[0].ApiKey, nil)
assert.Nil(t, err) assert.Nil(t, err)
body := rr.Body.String() body := rr.Body.String()

View File

@ -51,7 +51,7 @@ func servicesHandler(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/", http.StatusSeeOther) http.Redirect(w, r, "/", http.StatusSeeOther)
return return
} }
ExecuteResponse(w, r, "services.html", core.CoreApp.Services, nil) ExecuteResponse(w, r, "services.gohtml", core.CoreApp.Services, nil)
} }
type serviceOrder struct { type serviceOrder struct {
@ -115,7 +115,7 @@ func servicesViewHandler(w http.ResponseWriter, r *http.Request) {
Data string Data string
}{serv, start.Format(utils.FlatpickrReadable), end.Format(utils.FlatpickrReadable), start.Unix(), end.Unix(), data.ToString()} }{serv, start.Format(utils.FlatpickrReadable), end.Format(utils.FlatpickrReadable), start.Unix(), end.Unix(), data.ToString()}
ExecuteResponse(w, r, "service.html", out, nil) ExecuteResponse(w, r, "service.gohtml", out, nil)
} }
func apiServiceHandler(w http.ResponseWriter, r *http.Request) { func apiServiceHandler(w http.ResponseWriter, r *http.Request) {
@ -124,14 +124,13 @@ func apiServiceHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
vars := mux.Vars(r) vars := mux.Vars(r)
servicer := core.SelectServicer(utils.ToInt(vars["id"])) servicer := core.SelectService(utils.ToInt(vars["id"]))
if servicer == nil { if servicer == nil {
sendErrorJson(errors.New("service not found"), w, r) sendErrorJson(errors.New("service not found"), w, r)
return return
} }
service := servicer.Select()
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(service) json.NewEncoder(w).Encode(servicer)
} }
func apiCreateServiceHandler(w http.ResponseWriter, r *http.Request) { func apiCreateServiceHandler(w http.ResponseWriter, r *http.Request) {
@ -161,8 +160,8 @@ func apiServiceUpdateHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
vars := mux.Vars(r) vars := mux.Vars(r)
service := core.SelectServicer(utils.ToInt(vars["id"])) service := core.SelectService(utils.ToInt(vars["id"]))
if service.Select() == nil { if service == nil {
sendErrorJson(errors.New("service not found"), w, r) sendErrorJson(errors.New("service not found"), w, r)
return return
} }
@ -256,5 +255,5 @@ func servicesDeleteFailuresHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
service := core.SelectService(utils.ToInt(vars["id"])) service := core.SelectService(utils.ToInt(vars["id"]))
service.DeleteFailures() service.DeleteFailures()
ExecuteResponse(w, r, "services.html", core.CoreApp.Services, "/services") ExecuteResponse(w, r, "services.gohtml", core.CoreApp.Services, "/services")
} }

View File

@ -32,7 +32,7 @@ func settingsHandler(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/", http.StatusSeeOther) http.Redirect(w, r, "/", http.StatusSeeOther)
return return
} }
ExecuteResponse(w, r, "settings.html", core.CoreApp, nil) ExecuteResponse(w, r, "settings.gohtml", core.CoreApp, nil)
} }
func saveSettingsHandler(w http.ResponseWriter, r *http.Request) { func saveSettingsHandler(w http.ResponseWriter, r *http.Request) {
@ -73,7 +73,7 @@ func saveSettingsHandler(w http.ResponseWriter, r *http.Request) {
utils.Log(3, fmt.Sprintf("issue updating Core: %v", err.Error())) utils.Log(3, fmt.Sprintf("issue updating Core: %v", err.Error()))
} }
//notifiers.OnSettingsSaved(core.CoreApp.ToCore()) //notifiers.OnSettingsSaved(core.CoreApp.ToCore())
ExecuteResponse(w, r, "settings.html", core.CoreApp, "/settings") ExecuteResponse(w, r, "settings.gohtml", core.CoreApp, "/settings")
} }
func saveSASSHandler(w http.ResponseWriter, r *http.Request) { func saveSASSHandler(w http.ResponseWriter, r *http.Request) {
@ -90,7 +90,7 @@ func saveSASSHandler(w http.ResponseWriter, r *http.Request) {
source.SaveAsset([]byte(mobile), utils.Directory, "scss/mobile.scss") source.SaveAsset([]byte(mobile), utils.Directory, "scss/mobile.scss")
source.CompileSASS(utils.Directory) source.CompileSASS(utils.Directory)
resetRouter() resetRouter()
ExecuteResponse(w, r, "settings.html", core.CoreApp, "/settings") ExecuteResponse(w, r, "settings.gohtml", core.CoreApp, "/settings")
} }
func saveAssetsHandler(w http.ResponseWriter, r *http.Request) { func saveAssetsHandler(w http.ResponseWriter, r *http.Request) {
@ -111,7 +111,7 @@ func saveAssetsHandler(w http.ResponseWriter, r *http.Request) {
utils.Log(3, "Default 'base.css' was inserted because SASS did not work.") utils.Log(3, "Default 'base.css' was inserted because SASS did not work.")
} }
resetRouter() resetRouter()
ExecuteResponse(w, r, "settings.html", core.CoreApp, "/settings") ExecuteResponse(w, r, "settings.gohtml", core.CoreApp, "/settings")
} }
func deleteAssetsHandler(w http.ResponseWriter, r *http.Request) { func deleteAssetsHandler(w http.ResponseWriter, r *http.Request) {
@ -121,7 +121,7 @@ func deleteAssetsHandler(w http.ResponseWriter, r *http.Request) {
} }
source.DeleteAllAssets(utils.Directory) source.DeleteAllAssets(utils.Directory)
resetRouter() resetRouter()
ExecuteResponse(w, r, "settings.html", core.CoreApp, "/settings") ExecuteResponse(w, r, "settings.gohtml", core.CoreApp, "/settings")
} }
func parseId(r *http.Request) int64 { func parseId(r *http.Request) int64 {

View File

@ -34,7 +34,7 @@ func setupHandler(w http.ResponseWriter, r *http.Request) {
data, _ = core.LoadUsingEnv() data, _ = core.LoadUsingEnv()
} }
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
ExecuteResponse(w, r, "setup.html", data, nil) ExecuteResponse(w, r, "setup.gohtml", data, nil)
} }
func processSetupHandler(w http.ResponseWriter, r *http.Request) { func processSetupHandler(w http.ResponseWriter, r *http.Request) {
@ -126,5 +126,5 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
} }
func setupResponseError(w http.ResponseWriter, r *http.Request, a interface{}) { func setupResponseError(w http.ResponseWriter, r *http.Request, a interface{}) {
ExecuteResponse(w, r, "setup.html", a, nil) ExecuteResponse(w, r, "setup.gohtml", a, nil)
} }

View File

@ -33,7 +33,7 @@ func usersHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
users, _ := core.SelectAllUsers() users, _ := core.SelectAllUsers()
ExecuteResponse(w, r, "users.html", users, nil) ExecuteResponse(w, r, "users.gohtml", users, nil)
} }
func usersEditHandler(w http.ResponseWriter, r *http.Request) { func usersEditHandler(w http.ResponseWriter, r *http.Request) {
@ -44,7 +44,7 @@ func usersEditHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
id, _ := strconv.Atoi(vars["id"]) id, _ := strconv.Atoi(vars["id"])
user, _ := core.SelectUser(int64(id)) user, _ := core.SelectUser(int64(id))
ExecuteResponse(w, r, "user.html", user, nil) ExecuteResponse(w, r, "user.gohtml", user, nil)
} }
func apiUserHandler(w http.ResponseWriter, r *http.Request) { func apiUserHandler(w http.ResponseWriter, r *http.Request) {

View File

@ -33,7 +33,7 @@ var mobile = &mobilePush{&notifier.Notification{
Method: "mobile", Method: "mobile",
Title: "Mobile Notifications", Title: "Mobile Notifications",
Description: `Receive push notifications on your Android or iPhone devices using the Statping App. You can scan the Authentication QR Code found in Settings to get the mobile app setup in seconds. Description: `Receive push notifications on your Android or iPhone devices using the Statping App. You can scan the Authentication QR Code found in Settings to get the mobile app setup in seconds.
<p align="center"><a href="https://play.google.com/store/apps/details?id=com.statup"><img src="https://img.cjx.io/google-play.svg"></a> <a href="https://testflight.apple.com/join/TuBIj25Q"><img src="https://img.cjx.io/app-store-badge.svg"></a></p>`, <p align="center"><a href="https://play.google.com/store/apps/details?id=com.statping"><img src="https://img.cjx.io/google-play.svg"></a> <a href="https://testflight.apple.com/join/TuBIj25Q"><img src="https://img.cjx.io/app-store-badge.svg"></a></p>`,
Author: "Hunter Long", Author: "Hunter Long",
AuthorUrl: "https://github.com/hunterlong", AuthorUrl: "https://github.com/hunterlong",
Delay: time.Duration(5 * time.Second), Delay: time.Duration(5 * time.Second),

View File

@ -104,7 +104,7 @@ func TestSlackNotifier(t *testing.T) {
slacker.OnSuccess(TestService) slacker.OnSuccess(TestService)
assert.Equal(t, 1, len(slacker.Queue)) assert.Equal(t, 1, len(slacker.Queue))
go notifier.Queue(slacker) go notifier.Queue(slacker)
time.Sleep(5 * time.Second) time.Sleep(6 * time.Second)
assert.Equal(t, 0, len(slacker.Queue)) assert.Equal(t, 0, len(slacker.Queue))
}) })

View File

@ -135,11 +135,11 @@ func CreateAllAssets(folder string) error {
MakePublicFolder(folder + "/assets/font") MakePublicFolder(folder + "/assets/font")
MakePublicFolder(folder + "/assets/files") MakePublicFolder(folder + "/assets/files")
utils.Log(1, "Inserting scss, css, and javascript files into assets folder") utils.Log(1, "Inserting scss, css, and javascript files into assets folder")
CopyAllToPublic(ScssBox, "scss") CopyAllToPublic(ScssBox, "scss/")
CopyAllToPublic(FontBox, "font") CopyAllToPublic(FontBox, "font/")
CopyAllToPublic(CssBox, "css") CopyAllToPublic(CssBox, "css/")
CopyAllToPublic(JsBox, "js") CopyAllToPublic(JsBox, "js/")
CopyToPublic(FontBox, folder+"/assets/font", "all.css") CopyToPublic(FontBox, folder+"/assets/font/", "all.css")
CopyToPublic(TmplBox, folder+"/assets", "robots.txt") CopyToPublic(TmplBox, folder+"/assets", "robots.txt")
CopyToPublic(TmplBox, folder+"/assets", "banner.png") CopyToPublic(TmplBox, folder+"/assets", "banner.png")
CopyToPublic(TmplBox, folder+"/assets", "favicon.ico") CopyToPublic(TmplBox, folder+"/assets", "favicon.ico")

View File

@ -1,8 +1,8 @@
{ {
"info": { "info": {
"_postman_id": "d74ac4a3-8915-46e8-8ed2-5044ea4ce53b", "_postman_id": "d74ac4a3-8915-46e8-8ed2-5044ea4ce53b",
"name": "Statping", "name": "Statup",
"description": "Statping API Requests", "description": "Statup API Requests",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
}, },
"item": [ "item": [
@ -10,7 +10,7 @@
"name": "Main", "name": "Main",
"item": [ "item": [
{ {
"name": "Statping Details", "name": "Statup Details",
"event": [ "event": [
{ {
"listen": "test", "listen": "test",
@ -164,7 +164,7 @@
], ],
"body": { "body": {
"mode": "raw", "mode": "raw",
"raw": "{\n\t\"token\": {\n\t\t\"value\": \"ExponentPushToken[SBryVgIxjgaMKCP5MBtt2J]\"\n\t},\n\t\"user\": {\n\t\t\"username\": \"Brent\"\n\t}\n}" "raw": ""
}, },
"url": { "url": {
"raw": "{{endpoint}}/api/services/1", "raw": "{{endpoint}}/api/services/1",
@ -250,7 +250,7 @@
"pm.test(\"Create Service\", function () {", "pm.test(\"Create Service\", function () {",
" var jsonData = pm.response.json();", " var jsonData = pm.response.json();",
" pm.expect(jsonData.output.name).to.eql(\"New Service\");", " pm.expect(jsonData.output.name).to.eql(\"New Service\");",
" pm.expect(jsonData.output.domain).to.eql(\"https://statping.com\");", " pm.expect(jsonData.output.domain).to.eql(\"https://statup.io\");",
" pm.expect(jsonData.output.type).to.eql(\"http\");", " pm.expect(jsonData.output.type).to.eql(\"http\");",
" pm.expect(jsonData.output.method).to.eql(\"GET\");", " pm.expect(jsonData.output.method).to.eql(\"GET\");",
" pm.expect(jsonData.output.expected_status).to.eql(200);", " pm.expect(jsonData.output.expected_status).to.eql(200);",
@ -280,7 +280,7 @@
], ],
"body": { "body": {
"mode": "raw", "mode": "raw",
"raw": "{\n \"name\": \"New Service\",\n \"domain\": \"https://statping.com\",\n \"expected\": \"\",\n \"expected_status\": 200,\n \"check_interval\": 30,\n \"type\": \"http\",\n \"method\": \"GET\",\n \"post_data\": \"\",\n \"port\": 0,\n \"timeout\": 30,\n \"order_id\": 0\n}" "raw": "{\n \"name\": \"New Service\",\n \"domain\": \"https://statup.io\",\n \"expected\": \"\",\n \"expected_status\": 200,\n \"check_interval\": 30,\n \"type\": \"http\",\n \"method\": \"GET\",\n \"post_data\": \"\",\n \"port\": 0,\n \"timeout\": 30,\n \"order_id\": 0\n}"
}, },
"url": { "url": {
"raw": "{{endpoint}}/api/services", "raw": "{{endpoint}}/api/services",
@ -439,14 +439,14 @@
"header": [], "header": [],
"body": {}, "body": {},
"url": { "url": {
"raw": "{{endpoint}}/api/services/1", "raw": "{{endpoint}}/api/services/2",
"host": [ "host": [
"{{endpoint}}" "{{endpoint}}"
], ],
"path": [ "path": [
"api", "api",
"services", "services",
"1" "2"
] ]
} }
}, },

View File

@ -75,15 +75,15 @@
{{end}} {{end}}
{{if Auth}} {{if Auth}}
{{$failures := $s.LimitedFailures 16}}
<nav class="nav nav-pills flex-column flex-sm-row mt-3" id="service_tabs" role="serviceLists"> <nav class="nav nav-pills flex-column flex-sm-row mt-3" id="service_tabs" role="serviceLists">
<a class="flex-sm-fill text-sm-center nav-link active" id="edit-tab" data-toggle="tab" href="#edit" role="tab" aria-controls="edit" aria-selected="false">Edit Service</a> <a class="flex-sm-fill text-sm-center nav-link active" id="edit-tab" data-toggle="tab" href="#edit" role="tab" aria-controls="edit" aria-selected="false">Edit Service</a>
<a class="flex-sm-fill text-sm-center nav-link" id="failures-tab" data-toggle="tab" href="#failures" role="tab" aria-controls="failures" aria-selected="true">Failures</a> <a class="flex-sm-fill text-sm-center nav-link{{ if not $failures }}disabled{{end}}" id="failures-tab" data-toggle="tab" href="#failures" role="tab" aria-controls="failures" aria-selected="true">Failures</a>
<a class="flex-sm-fill text-sm-center nav-link" id="checkins-tab" data-toggle="tab" href="#checkins" role="tab" aria-controls="checkins" aria-selected="false">Checkins</a> <a class="flex-sm-fill text-sm-center nav-link" id="checkins-tab" data-toggle="tab" href="#checkins" role="tab" aria-controls="checkins" aria-selected="false">Checkins</a>
<a class="flex-sm-fill text-sm-center nav-link" id="response-tab" data-toggle="tab" href="#response" role="tab" aria-controls="response" aria-selected="false">Response</a> <a class="flex-sm-fill text-sm-center nav-link" id="response-tab" data-toggle="tab" href="#response" role="tab" aria-controls="response" aria-selected="false">Response</a>
</nav> </nav>
<div class="tab-content" id="myTabContent"> <div class="tab-content" id="myTabContent">
<div class="tab-pane fade" id="failures" role="serviceLists" aria-labelledby="failures-tab"> <div class="tab-pane fade" id="failures" role="serviceLists" aria-labelledby="failures-tab">
{{$failures := $s.LimitedFailures 16}}
{{ if $failures }} {{ if $failures }}
<div class="list-group mt-3 mb-4"> <div class="list-group mt-3 mb-4">
{{ range $failures }} {{ range $failures }}
@ -99,7 +99,7 @@
{{ end }} {{ end }}
</div> </div>
<div class="tab-pane fade" id="checkins" role="serviceLists" aria-labelledby="checkins-tab"> <div class="tab-pane fade" id="checkins" role="serviceLists" aria-labelledby="checkins-tab">
{{if $s.LimitedCheckins}} {{if $s.AllCheckins}}
<table class="table"> <table class="table">
<thead> <thead>
<tr> <tr>
@ -111,7 +111,7 @@
</tr> </tr>
</thead> </thead>
<tbody style="font-size: 10pt;"> <tbody style="font-size: 10pt;">
{{range $s.LimitedCheckins}} {{range $s.AllCheckins}}
{{ $ch := . }} {{ $ch := . }}
<tr id="checkin_{{$ch.Id}}" class="{{ if lt $ch.Expected 0}}bg-warning text-black{{else}}bg-light{{end}}"> <tr id="checkin_{{$ch.Id}}" class="{{ if lt $ch.Expected 0}}bg-warning text-black{{else}}bg-light{{end}}">
<td>{{$ch.Name}}<br><a href="{{$ch.Link}}" target="_blank">{{$ch.Link}}</a></td> <td>{{$ch.Name}}<br><a href="{{$ch.Link}}" target="_blank">{{$ch.Link}}</a></td>

View File

@ -30,14 +30,20 @@ type Checkin struct {
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"` CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"` UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
Running chan bool `gorm:"-" json:"-"` Running chan bool `gorm:"-" json:"-"`
Failing bool `gorm:"-" json:"failing"`
LastHit time.Time `gorm:"-" json:"last_hit"`
Hits []*CheckinHit `gorm:"-" json:"hits"` Hits []*CheckinHit `gorm:"-" json:"hits"`
Failures []*Failure `gorm:"-" json:"failures"` Failures []FailureInterface `gorm:"-" json:"failures"`
}
type CheckinInterface interface {
Select() *Checkin
} }
// CheckinHit is a successful response from a Checkin // CheckinHit is a successful response from a Checkin
type CheckinHit struct { type CheckinHit struct {
Id int64 `gorm:"primary_key;column:id" json:"id"` Id int64 `gorm:"primary_key;column:id" json:"id"`
Checkin int64 `gorm:"index;column:checkin" json:"checkin"` Checkin int64 `gorm:"index;column:checkin" json:"-"`
From string `gorm:"column:from_location" json:"from"` From string `gorm:"column:from_location" json:"from"`
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"` CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
} }

View File

@ -27,6 +27,7 @@ type Failure struct {
Method string `gorm:"column:method" json:"method,omitempty"` Method string `gorm:"column:method" json:"method,omitempty"`
MethodId int64 `gorm:"column:method_id" json:"method_id,omitempty"` MethodId int64 `gorm:"column:method_id" json:"method_id,omitempty"`
Service int64 `gorm:"index;column:service" json:"-"` Service int64 `gorm:"index;column:service" json:"-"`
Checkin int64 `gorm:"index;column:checkin" json:"-"`
PingTime float64 `gorm:"column:ping_time" json:"ping"` PingTime float64 `gorm:"column:ping_time" json:"ping"`
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"` CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
} }

View File

@ -46,8 +46,9 @@ type Service struct {
SleepDuration time.Duration `gorm:"-" json:"-"` SleepDuration time.Duration `gorm:"-" json:"-"`
LastResponse string `gorm:"-" json:"-"` LastResponse string `gorm:"-" json:"-"`
LastStatusCode int `gorm:"-" json:"status_code"` LastStatusCode int `gorm:"-" json:"status_code"`
LastOnline time.Time `gorm:"-" json:"last_online"` LastOnline time.Time `gorm:"-" json:"last_success"`
Failures []FailureInterface `gorm:"-" json:"failures,omitempty"` Failures []FailureInterface `gorm:"-" json:"failures,omitempty"`
Checkins []CheckinInterface `gorm:"-" json:"checkins,omitempty"`
} }
type ServiceInterface interface { type ServiceInterface interface {

View File

@ -1 +1 @@
0.79.97 0.79.98