mirror of https://github.com/statping/statping
checkin - notifier docs
parent
bb3ab30a61
commit
f5e15258c8
|
@ -9,7 +9,7 @@
|
||||||
# Statup - Status Page & Monitoring Server
|
# Statup - Status Page & Monitoring Server
|
||||||
An easy to use Status Page for your websites and applications. Statup will automatically fetch the application and render a beautiful status page with tons of features for you to build an even better status page. This Status Page generator allows you to use MySQL, Postgres, or SQLite on multiple operating systems.
|
An easy to use Status Page for your websites and applications. Statup will automatically fetch the application and render a beautiful status page with tons of features for you to build an even better status page. This Status Page generator allows you to use MySQL, Postgres, or SQLite on multiple operating systems.
|
||||||
|
|
||||||
[](https://gitter.im/statup-app/general)
|
[](https://godoc.org/github.com/hunterlong/statup) [](https://gitter.im/statup-app/general)
|
||||||
|
|
||||||
## A Future-Proof Status Page
|
## A Future-Proof Status Page
|
||||||
Statup strives to remain future-proof and remain intact if a failure is created. Your Statup service should not be running on the same instance you're trying to monitor. If your server crashes your Status Page should still remaining online to notify your users of downtime.
|
Statup strives to remain future-proof and remain intact if a failure is created. Your Statup service should not be running on the same instance you're trying to monitor. If your server crashes your Status Page should still remaining online to notify your users of downtime.
|
||||||
|
|
|
@ -20,7 +20,6 @@ import (
|
||||||
"github.com/ararog/timeago"
|
"github.com/ararog/timeago"
|
||||||
"github.com/hunterlong/statup/types"
|
"github.com/hunterlong/statup/types"
|
||||||
"github.com/hunterlong/statup/utils"
|
"github.com/hunterlong/statup/utils"
|
||||||
"github.com/jinzhu/gorm"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -80,17 +79,18 @@ func (u *Checkin) Hits() []CheckinHit {
|
||||||
return checkins
|
return checkins
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Checkin) Update() (int64, error) {
|
func (u *Checkin) Create() (int64, error) {
|
||||||
fmt.Println("updating: ", u.Id, u.ApiKey)
|
u.ApiKey = utils.RandomString(7)
|
||||||
exists := checkinDB().Find(&u).RecordNotFound()
|
row := checkinDB().Create(&u)
|
||||||
var row *gorm.DB
|
if row.Error != nil {
|
||||||
if !exists {
|
utils.Log(2, row.Error)
|
||||||
row = checkinDB().Update(&u)
|
return 0, row.Error
|
||||||
} else {
|
|
||||||
u.ApiKey = utils.RandomString(7)
|
|
||||||
row = checkinDB().Create(&u)
|
|
||||||
}
|
}
|
||||||
fmt.Println("found: ", exists, u.Id, u.Service, u.Interval, u.GracePeriod, u.ApiKey)
|
return u.Id, row.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Checkin) Update() (int64, error) {
|
||||||
|
row := checkinDB().Update(&u)
|
||||||
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
|
||||||
|
|
|
@ -33,7 +33,7 @@ var example = &ExampleNotifier{&Notification{
|
||||||
Description: "Example Notifier",
|
Description: "Example Notifier",
|
||||||
Author: "Hunter Long",
|
Author: "Hunter Long",
|
||||||
AuthorUrl: "https://github.com/hunterlong",
|
AuthorUrl: "https://github.com/hunterlong",
|
||||||
Delay: time.Duration(200 * time.Millisecond),
|
Delay: time.Duration(3 * time.Second),
|
||||||
Limits: 7,
|
Limits: 7,
|
||||||
Form: []NotificationForm{{
|
Form: []NotificationForm{{
|
||||||
Type: "text",
|
Type: "text",
|
||||||
|
@ -182,7 +182,7 @@ func (n *ExampleNotifier) OnUpdatedNotifier(s *Notification) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new notifier that includes a form for the end user to insert their own values
|
// Create a new notifier that includes a form for the end user to insert their own values
|
||||||
func Example() {
|
func ExampleNotification() {
|
||||||
// Create a new variable for your Notifier
|
// Create a new variable for your Notifier
|
||||||
example = &ExampleNotifier{&Notification{
|
example = &ExampleNotifier{&Notification{
|
||||||
Method: "Example",
|
Method: "Example",
|
||||||
|
@ -198,29 +198,91 @@ func Example() {
|
||||||
Placeholder: "Insert your Host here.",
|
Placeholder: "Insert your Host here.",
|
||||||
DbField: "host",
|
DbField: "host",
|
||||||
SmallText: "you can also use SmallText to insert some helpful hints under this input",
|
SmallText: "you can also use SmallText to insert some helpful hints under this input",
|
||||||
|
}, {
|
||||||
|
Type: "text",
|
||||||
|
Title: "API Key",
|
||||||
|
Placeholder: "Include some type of API key here",
|
||||||
|
DbField: "api_key",
|
||||||
}},
|
}},
|
||||||
}}
|
}}
|
||||||
|
|
||||||
// AddNotifier accepts a notifier to load into the Statup Notification system
|
// AddNotifier accepts a Notifier to load into the Statup Notification system
|
||||||
AddNotifier(example)
|
err := AddNotifier(example)
|
||||||
|
fmt.Println(err)
|
||||||
|
// Output: <nil>
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add any type of interface to the AddQueue function when a service is successful
|
// Add a Notifier to the AddQueue function to insert it into the system
|
||||||
func Example_onSuccess() {
|
func ExampleAddNotifier() {
|
||||||
|
err := AddNotifier(example)
|
||||||
|
fmt.Println(err)
|
||||||
|
// Output: <nil>
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnSuccess will be triggered everytime a service is online
|
||||||
|
func ExampleNotification_OnSuccess() {
|
||||||
msg := fmt.Sprintf("this is a successful message as a string passing into AddQueue function")
|
msg := fmt.Sprintf("this is a successful message as a string passing into AddQueue function")
|
||||||
example.AddQueue(msg)
|
example.AddQueue(msg)
|
||||||
|
fmt.Println(len(example.Queue))
|
||||||
|
// Output: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add any type of interface to the AddQueue function when a service is successful
|
// Add any type of interface to the AddQueue function to be ran in the queue
|
||||||
func Example_onFailure() {
|
func ExampleNotification_AddQueue() {
|
||||||
msg := fmt.Sprintf("this is a failing message as a string passing into AddQueue function")
|
msg := fmt.Sprintf("this is a failing message as a string passing into AddQueue function")
|
||||||
example.AddQueue(msg)
|
example.AddQueue(msg)
|
||||||
|
queue := example.Queue
|
||||||
|
fmt.Printf("Example has %v items in the queue", len(queue))
|
||||||
|
// Output: Example has 2 items in the queue
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Send method will run the main functionality of your notifier
|
// The Send method will run the main functionality of your notifier
|
||||||
func Example_send() {
|
func ExampleNotification_Send() {
|
||||||
// example.Send(msg interface{})
|
msg := "this can be any type of interface"
|
||||||
for i := 0; i <= 10; i++ {
|
example.Send(msg)
|
||||||
fmt.Printf("do something awesome rather than a loop %v\n", i)
|
queue := example.Queue
|
||||||
}
|
fmt.Printf("Example has %v items in the queue", len(queue))
|
||||||
|
// Output:
|
||||||
|
// i received this string: this can be any type of interface
|
||||||
|
// Example has 2 items in the queue
|
||||||
|
}
|
||||||
|
|
||||||
|
// LastSent will return the time.Duration of the last sent message
|
||||||
|
func ExampleNotification_LastSent() {
|
||||||
|
last := example.LastSent()
|
||||||
|
fmt.Printf("Last message was sent %v seconds ago", last.Seconds())
|
||||||
|
// Output: Last message was sent 0 seconds ago
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logs will return a slice of previously sent items from your notifier
|
||||||
|
func ExampleNotification_Logs() {
|
||||||
|
logs := example.Logs()
|
||||||
|
fmt.Printf("Example has %v items in the log", len(logs))
|
||||||
|
// Output: Example has 0 items in the log
|
||||||
|
}
|
||||||
|
|
||||||
|
// SentLastMinute will return he amount of notifications sent in last 1 minute
|
||||||
|
func ExampleNotification_SentLastMinute() {
|
||||||
|
lastMinute := example.SentLastMinute()
|
||||||
|
fmt.Printf("%v notifications sent in the last minute", lastMinute)
|
||||||
|
// Output: 0 notifications sent in the last minute
|
||||||
|
}
|
||||||
|
|
||||||
|
// SentLastHour will return he amount of notifications sent in last 1 hour
|
||||||
|
func ExampleNotification_SentLastHour() {
|
||||||
|
lastHour := example.SentLastHour()
|
||||||
|
fmt.Printf("%v notifications sent in the last hour", lastHour)
|
||||||
|
// Output: 0 notifications sent in the last hour
|
||||||
|
}
|
||||||
|
|
||||||
|
// SentLastHour will return he amount of notifications sent in last 1 hour
|
||||||
|
func ExampleNotification_WithinLimits() {
|
||||||
|
ok, err := example.WithinLimits()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
fmt.Printf("Example notifier is still within its sending limits")
|
||||||
|
}
|
||||||
|
// Output: Example notifier is still within its sending limits
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,18 +94,14 @@ func SetDB(d *gorm.DB) {
|
||||||
db = d
|
db = d
|
||||||
}
|
}
|
||||||
|
|
||||||
func asNotifier(n interface{}) Notifier {
|
func asNotification(n Notifier) *Notification {
|
||||||
return n.(Notifier)
|
return n.Select()
|
||||||
}
|
|
||||||
|
|
||||||
func asNotification(n interface{}) *Notification {
|
|
||||||
return n.(Notifier).Select()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddNotifier accept a Notifier interface to be added into the array
|
// AddNotifier accept a Notifier interface to be added into the array
|
||||||
func AddNotifier(n interface{}) error {
|
func AddNotifier(n Notifier) error {
|
||||||
if isType(n, new(Notifier)) {
|
if isType(n, new(Notifier)) {
|
||||||
err := checkNotifierForm(asNotifier(n))
|
err := checkNotifierForm(n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -231,10 +231,9 @@ func TestRunAllQueueAndStop(t *testing.T) {
|
||||||
assert.True(t, example.IsRunning())
|
assert.True(t, example.IsRunning())
|
||||||
assert.Equal(t, 16, len(example.Queue))
|
assert.Equal(t, 16, len(example.Queue))
|
||||||
go Queue(example)
|
go Queue(example)
|
||||||
assert.Equal(t, 16, len(example.Queue))
|
time.Sleep(13 * time.Second)
|
||||||
time.Sleep(12 * time.Second)
|
assert.True(t, len(example.Queue) >= 10)
|
||||||
assert.Equal(t, 6, len(example.Queue))
|
|
||||||
example.close()
|
example.close()
|
||||||
assert.False(t, example.IsRunning())
|
assert.False(t, example.IsRunning())
|
||||||
assert.Equal(t, 6, len(example.Queue))
|
assert.True(t, len(example.Queue) >= 10)
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,10 +49,10 @@ func SelectService(id int64) *Service {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Checkin() *Checkin {
|
func (s *Service) Checkins() []*Checkin {
|
||||||
var checkin types.Checkin
|
var checkin []*Checkin
|
||||||
checkinDB().Find(&checkin, "service = ?", s.Id)
|
checkinDB().Where("service = ?", s.Id).Find(&checkin)
|
||||||
return &Checkin{&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.
|
||||||
|
@ -66,7 +66,7 @@ func (c *Core) SelectAllServices() ([]*Service, error) {
|
||||||
CoreApp.Services = nil
|
CoreApp.Services = nil
|
||||||
for _, service := range services {
|
for _, service := range services {
|
||||||
service.Start()
|
service.Start()
|
||||||
service.Checkin()
|
service.Checkins()
|
||||||
service.AllFailures()
|
service.AllFailures()
|
||||||
CoreApp.Services = append(CoreApp.Services, service)
|
CoreApp.Services = append(CoreApp.Services, service)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/hunterlong/statup/types"
|
"github.com/hunterlong/statup/types"
|
||||||
|
"github.com/hunterlong/statup/utils"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -224,7 +225,7 @@ func TestCreateFailingHTTPService(t *testing.T) {
|
||||||
func TestServiceFailedCheck(t *testing.T) {
|
func TestServiceFailedCheck(t *testing.T) {
|
||||||
service := SelectService(17)
|
service := SelectService(17)
|
||||||
assert.Equal(t, "Bad URL", service.Name)
|
assert.Equal(t, "Bad URL", service.Name)
|
||||||
service.Check(true)
|
service.Check(false)
|
||||||
assert.Equal(t, "Bad URL", service.Name)
|
assert.Equal(t, "Bad URL", service.Name)
|
||||||
assert.False(t, service.Online)
|
assert.False(t, service.Online)
|
||||||
}
|
}
|
||||||
|
@ -249,7 +250,7 @@ func TestCreateFailingTCPService(t *testing.T) {
|
||||||
|
|
||||||
func TestServiceFailedTCPCheck(t *testing.T) {
|
func TestServiceFailedTCPCheck(t *testing.T) {
|
||||||
service := SelectService(newServiceId)
|
service := SelectService(newServiceId)
|
||||||
service.Check(true)
|
service.Check(false)
|
||||||
assert.Equal(t, "Bad TCP", service.Name)
|
assert.Equal(t, "Bad TCP", service.Name)
|
||||||
assert.False(t, service.Online)
|
assert.False(t, service.Online)
|
||||||
}
|
}
|
||||||
|
@ -342,3 +343,57 @@ func TestDNScheckService(t *testing.T) {
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.NotZero(t, amount)
|
assert.NotZero(t, amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCreateCheckin(t *testing.T) {
|
||||||
|
checkin := ReturnCheckin(&types.Checkin{
|
||||||
|
Service: 1,
|
||||||
|
Interval: 10,
|
||||||
|
GracePeriod: 5,
|
||||||
|
ApiKey: utils.RandomString(7),
|
||||||
|
})
|
||||||
|
id, err := checkin.Create()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.NotZero(t, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectCheckin(t *testing.T) {
|
||||||
|
service := SelectService(1)
|
||||||
|
checkins := service.Checkins()
|
||||||
|
assert.NotNil(t, checkins)
|
||||||
|
assert.Equal(t, 1, len(checkins))
|
||||||
|
first := checkins[0]
|
||||||
|
assert.Equal(t, int64(10), first.Interval)
|
||||||
|
assert.Equal(t, 7, len(first.ApiKey))
|
||||||
|
assert.Equal(t, int64(5), first.GracePeriod)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateCheckinHits(t *testing.T) {
|
||||||
|
service := SelectService(1)
|
||||||
|
checkins := service.Checkins()
|
||||||
|
first := checkins[0]
|
||||||
|
created := time.Now().Add(-2 * time.Hour)
|
||||||
|
for i := 0; i <= 20; i++ {
|
||||||
|
hit := ReturnCheckinHit(&types.CheckinHit{
|
||||||
|
Checkin: first.Id,
|
||||||
|
From: "192.168.1.1",
|
||||||
|
CreatedAt: created,
|
||||||
|
})
|
||||||
|
hit.Create()
|
||||||
|
created = created.Add(10 * time.Second)
|
||||||
|
}
|
||||||
|
hits := first.Hits()
|
||||||
|
assert.Equal(t, 21, len(hits))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectCheckinMethods(t *testing.T) {
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
service := SelectService(1)
|
||||||
|
checkins := service.Checkins()
|
||||||
|
assert.NotNil(t, checkins)
|
||||||
|
first := checkins[0]
|
||||||
|
lastHit := first.Last()
|
||||||
|
assert.Equal(t, float64(10), first.Period().Seconds())
|
||||||
|
assert.Equal(t, float64(5), first.Grace().Seconds())
|
||||||
|
assert.Equal(t, time.Now().Day(), lastHit.CreatedAt.Day())
|
||||||
|
assert.Equal(t, "Just now", lastHit.Ago())
|
||||||
|
}
|
||||||
|
|
|
@ -261,7 +261,9 @@ func checkinCreateUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
service := core.SelectService(utils.StringInt(vars["id"]))
|
service := core.SelectService(utils.StringInt(vars["id"]))
|
||||||
checkin := service.Checkin()
|
api := r.PostForm.Get("api")
|
||||||
|
checkin := core.SelectCheckin(api)
|
||||||
|
|
||||||
interval := utils.StringInt(r.PostForm.Get("interval"))
|
interval := utils.StringInt(r.PostForm.Get("interval"))
|
||||||
grace := utils.StringInt(r.PostForm.Get("grace"))
|
grace := utils.StringInt(r.PostForm.Get("grace"))
|
||||||
checkin.Interval = interval
|
checkin.Interval = interval
|
||||||
|
|
|
@ -105,16 +105,18 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{{$ch := $s.Checkin}}
|
{{range $s.Checkins}}
|
||||||
|
{{ $ch := . }}
|
||||||
<tr class="{{ if lt $ch.Expected 0}}bg-danger text-white{{else}}bg-light{{end}}">
|
<tr class="{{ if lt $ch.Expected 0}}bg-danger text-white{{else}}bg-light{{end}}">
|
||||||
<td>{{CoreApp.Domain}}/checkin/{{$ch.ApiKey}}</td>
|
<td>{{CoreApp.Domain}}/checkin/{{$ch.ApiKey}}</td>
|
||||||
<td>every {{Duration $ch.Period}}<br>after {{Duration $ch.Grace}}</td>
|
<td>every {{Duration $ch.Period}}<br>after {{Duration $ch.Grace}}</td>
|
||||||
<td>{{Ago $ch.Last.CreatedAt}}</td>
|
<td>{{Ago $ch.Last.CreatedAt}}</td>
|
||||||
<td>{{ if lt $ch.Expected 0}}{{Duration $ch.Expected}} ago{{else}}in {{Duration $ch.Expected}}{{end}}</td>
|
<td>{{ if lt $ch.Expected 0}}{{Duration $ch.Expected}} ago{{else}}in {{Duration $ch.Expected}}{{end}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{{template "form_checkin" $ch}}
|
||||||
|
{{end}}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{{template "form_checkin" $ch}}
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue