mirror of https://github.com/statping/statping
checkin updates - checking API - code organization
parent
f231d39369
commit
ac198b5340
2
Makefile
2
Makefile
|
@ -1,4 +1,4 @@
|
||||||
VERSION=0.79.85
|
VERSION=0.79.86
|
||||||
BINARY_NAME=statup
|
BINARY_NAME=statup
|
||||||
GOPATH:=$(GOPATH)
|
GOPATH:=$(GOPATH)
|
||||||
GOCMD=go
|
GOCMD=go
|
||||||
|
|
|
@ -27,7 +27,7 @@ type Checkin struct {
|
||||||
*types.Checkin
|
*types.Checkin
|
||||||
}
|
}
|
||||||
|
|
||||||
type checkinHit struct {
|
type CheckinHit struct {
|
||||||
*types.CheckinHit
|
*types.CheckinHit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,8 +68,8 @@ func ReturnCheckin(c *types.Checkin) *Checkin {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReturnCheckinHit converts *types.checkinHit to *core.checkinHit
|
// ReturnCheckinHit converts *types.checkinHit to *core.checkinHit
|
||||||
func ReturnCheckinHit(c *types.CheckinHit) *checkinHit {
|
func ReturnCheckinHit(c *types.CheckinHit) *CheckinHit {
|
||||||
return &checkinHit{CheckinHit: c}
|
return &CheckinHit{CheckinHit: c}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Checkin) Service() *Service {
|
func (c *Checkin) Service() *Service {
|
||||||
|
@ -133,8 +133,8 @@ func (c *Checkin) Expected() time.Duration {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Last returns the last checkinHit for a Checkin
|
// Last returns the last checkinHit for a Checkin
|
||||||
func (c *Checkin) Last() *checkinHit {
|
func (c *Checkin) Last() *CheckinHit {
|
||||||
var hit checkinHit
|
var hit CheckinHit
|
||||||
checkinHitsDB().Where("checkin = ?", c.Id).Last(&hit)
|
checkinHitsDB().Where("checkin = ?", c.Id).Last(&hit)
|
||||||
return &hit
|
return &hit
|
||||||
}
|
}
|
||||||
|
@ -143,13 +143,21 @@ func (c *Checkin) Link() string {
|
||||||
return fmt.Sprintf("%v/checkin/%v", CoreApp.Domain, c.ApiKey)
|
return fmt.Sprintf("%v/checkin/%v", CoreApp.Domain, c.ApiKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hits returns all of the CheckinHits for a given Checkin
|
// AllHits returns all of the CheckinHits for a given Checkin
|
||||||
func (c *Checkin) Hits() []*checkinHit {
|
func (c *Checkin) AllHits() []*types.CheckinHit {
|
||||||
var checkins []*checkinHit
|
var checkins []*types.CheckinHit
|
||||||
checkinHitsDB().Where("checkin = ?", c.Id).Order("id DESC").Find(&checkins)
|
checkinHitsDB().Where("checkin = ?", c.Id).Order("id DESC").Find(&checkins)
|
||||||
return checkins
|
return checkins
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hits returns all of the CheckinHits for a given Checkin
|
||||||
|
func (c *Checkin) AllFailures() []*types.Failure {
|
||||||
|
var failures []*types.Failure
|
||||||
|
col := failuresDB().Where("checkin = ?", c.Id).Where("method = 'checkin'").Order("id desc")
|
||||||
|
col.Find(&failures)
|
||||||
|
return failures
|
||||||
|
}
|
||||||
|
|
||||||
// Create will create a new Checkin
|
// Create will create a new Checkin
|
||||||
func (c *Checkin) Delete() error {
|
func (c *Checkin) Delete() error {
|
||||||
c.Close()
|
c.Close()
|
||||||
|
@ -181,7 +189,7 @@ func (c *Checkin) Update() (int64, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create will create a new successful checkinHit
|
// Create will create a new successful checkinHit
|
||||||
func (c *checkinHit) Create() (int64, error) {
|
func (c *CheckinHit) Create() (int64, error) {
|
||||||
if c.CreatedAt.IsZero() {
|
if c.CreatedAt.IsZero() {
|
||||||
c.CreatedAt = time.Now()
|
c.CreatedAt = time.Now()
|
||||||
}
|
}
|
||||||
|
@ -194,7 +202,7 @@ func (c *checkinHit) Create() (int64, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ago returns the duration of time between now and the last successful checkinHit
|
// Ago returns the duration of time between now and the last successful checkinHit
|
||||||
func (c *checkinHit) Ago() string {
|
func (c *CheckinHit) Ago() string {
|
||||||
got, _ := timeago.TimeAgoWithTime(time.Now(), c.CreatedAt)
|
got, _ := timeago.TimeAgoWithTime(time.Now(), c.CreatedAt)
|
||||||
return got
|
return got
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,12 @@ func usersDB() *gorm.DB {
|
||||||
|
|
||||||
// checkinDB returns the Checkin records for a service
|
// checkinDB returns the Checkin records for a service
|
||||||
func checkinDB() *gorm.DB {
|
func checkinDB() *gorm.DB {
|
||||||
return DbSession.Model(&types.Checkin{})
|
return DbSession.Table("checkins").Model(&types.Checkin{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkinHitsDB returns the Checkin Hits records for a service
|
||||||
|
func checkinHitsDB() *gorm.DB {
|
||||||
|
return DbSession.Model(&types.CheckinHit{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// messagesDb returns the Checkin records for a service
|
// messagesDb returns the Checkin records for a service
|
||||||
|
@ -77,11 +82,6 @@ func messagesDb() *gorm.DB {
|
||||||
return DbSession.Model(&types.Message{})
|
return DbSession.Model(&types.Message{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
// 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 {
|
func (s *Service) HitsBetween(t1, t2 time.Time, group string, column string) *gorm.DB {
|
||||||
selector := Dbtimestamp(group, column)
|
selector := Dbtimestamp(group, column)
|
||||||
|
@ -146,7 +146,7 @@ func (c *Checkin) AfterFind() (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// AfterFind for checkinHit will set the timezone
|
// AfterFind for checkinHit will set the timezone
|
||||||
func (c *checkinHit) AfterFind() (err error) {
|
func (c *CheckinHit) AfterFind() (err error) {
|
||||||
c.CreatedAt = utils.Timezoner(c.CreatedAt, CoreApp.Timezone)
|
c.CreatedAt = utils.Timezoner(c.CreatedAt, CoreApp.Timezone)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -213,7 +213,7 @@ func (c *Checkin) BeforeCreate() (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// BeforeCreate for checkinHit will set CreatedAt to UTC
|
// BeforeCreate for checkinHit will set CreatedAt to UTC
|
||||||
func (c *checkinHit) BeforeCreate() (err error) {
|
func (c *CheckinHit) BeforeCreate() (err error) {
|
||||||
if c.CreatedAt.IsZero() {
|
if c.CreatedAt.IsZero() {
|
||||||
c.CreatedAt = time.Now().UTC()
|
c.CreatedAt = time.Now().UTC()
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ 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).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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -81,7 +81,7 @@ func TestCreateCheckinHits(t *testing.T) {
|
||||||
CreatedAt: created,
|
CreatedAt: created,
|
||||||
})
|
})
|
||||||
hit.Create()
|
hit.Create()
|
||||||
hits := testCheckin.Hits()
|
hits := testCheckin.AllHits()
|
||||||
assert.Equal(t, 1, len(hits))
|
assert.Equal(t, 1, len(hits))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
220
handlers/api.go
220
handlers/api.go
|
@ -18,14 +18,13 @@ package handlers
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"github.com/hunterlong/statup/core"
|
"github.com/hunterlong/statup/core"
|
||||||
"github.com/hunterlong/statup/core/notifier"
|
"github.com/hunterlong/statup/core/notifier"
|
||||||
"github.com/hunterlong/statup/types"
|
"github.com/hunterlong/statup/types"
|
||||||
"github.com/hunterlong/statup/utils"
|
"github.com/hunterlong/statup/utils"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type apiResponse struct {
|
type apiResponse struct {
|
||||||
|
@ -37,6 +36,19 @@ type apiResponse struct {
|
||||||
Output interface{} `json:"output,omitempty"`
|
Output interface{} `json:"output,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isAuthorized(r *http.Request) bool {
|
||||||
|
var token string
|
||||||
|
tokens, ok := r.Header["Authorization"]
|
||||||
|
if ok && len(tokens) >= 1 {
|
||||||
|
token = tokens[0]
|
||||||
|
token = strings.TrimPrefix(token, "Bearer ")
|
||||||
|
}
|
||||||
|
if token == core.CoreApp.ApiSecret {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func apiIndexHandler(w http.ResponseWriter, r *http.Request) {
|
func apiIndexHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if !isAPIAuthorized(r) {
|
if !isAPIAuthorized(r) {
|
||||||
sendUnauthorizedJson(w, r)
|
sendUnauthorizedJson(w, r)
|
||||||
|
@ -62,204 +74,6 @@ func apiRenewHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
http.Redirect(w, r, "/settings", http.StatusSeeOther)
|
http.Redirect(w, r, "/settings", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiCheckinHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if !isAPIAuthorized(r) {
|
|
||||||
sendUnauthorizedJson(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
checkin := core.SelectCheckin(vars["api"])
|
|
||||||
//checkin.Receivehit()
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
json.NewEncoder(w).Encode(checkin)
|
|
||||||
}
|
|
||||||
|
|
||||||
func apiServiceDataHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
service := core.SelectService(utils.StringInt(vars["id"]))
|
|
||||||
if service == nil {
|
|
||||||
sendErrorJson(errors.New("service data not found"), w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fields := parseGet(r)
|
|
||||||
grouping := fields.Get("group")
|
|
||||||
if grouping == "" {
|
|
||||||
grouping = "hour"
|
|
||||||
}
|
|
||||||
startField := utils.StringInt(fields.Get("start"))
|
|
||||||
endField := utils.StringInt(fields.Get("end"))
|
|
||||||
|
|
||||||
if startField == 0 || endField == 0 {
|
|
||||||
startField = 0
|
|
||||||
endField = 99999999999
|
|
||||||
}
|
|
||||||
|
|
||||||
obj := core.GraphDataRaw(service, time.Unix(startField, 0).UTC(), time.Unix(endField, 0).UTC(), grouping, "latency")
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
json.NewEncoder(w).Encode(obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
func apiServicePingDataHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
service := core.SelectService(utils.StringInt(vars["id"]))
|
|
||||||
if service == nil {
|
|
||||||
sendErrorJson(errors.New("service not found"), w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fields := parseGet(r)
|
|
||||||
grouping := fields.Get("group")
|
|
||||||
startField := utils.StringInt(fields.Get("start"))
|
|
||||||
endField := utils.StringInt(fields.Get("end"))
|
|
||||||
obj := core.GraphDataRaw(service, time.Unix(startField, 0), time.Unix(endField, 0), grouping, "ping_time")
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
json.NewEncoder(w).Encode(obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
func apiServiceHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if !isAPIAuthorized(r) {
|
|
||||||
sendUnauthorizedJson(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
servicer := core.SelectServicer(utils.StringInt(vars["id"]))
|
|
||||||
if servicer == nil {
|
|
||||||
sendErrorJson(errors.New("service not found"), w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
service := servicer.Select()
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
json.NewEncoder(w).Encode(service)
|
|
||||||
}
|
|
||||||
|
|
||||||
func apiCreateServiceHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if !isAPIAuthorized(r) {
|
|
||||||
sendUnauthorizedJson(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var service *types.Service
|
|
||||||
decoder := json.NewDecoder(r.Body)
|
|
||||||
err := decoder.Decode(&service)
|
|
||||||
if err != nil {
|
|
||||||
sendErrorJson(err, w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
newService := core.ReturnService(service)
|
|
||||||
_, err = newService.Create(true)
|
|
||||||
if err != nil {
|
|
||||||
sendErrorJson(err, w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
sendJsonAction(newService, "create", w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func apiServiceUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if !isAPIAuthorized(r) {
|
|
||||||
sendUnauthorizedJson(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
service := core.SelectServicer(utils.StringInt(vars["id"]))
|
|
||||||
if service.Select() == nil {
|
|
||||||
sendErrorJson(errors.New("service not found"), w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
decoder := json.NewDecoder(r.Body)
|
|
||||||
decoder.Decode(&service)
|
|
||||||
err := service.Update(true)
|
|
||||||
if err != nil {
|
|
||||||
sendErrorJson(err, w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
go service.Check(true)
|
|
||||||
|
|
||||||
sendJsonAction(service, "update", w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func apiServiceDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if !isAPIAuthorized(r) {
|
|
||||||
sendUnauthorizedJson(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
service := core.SelectService(utils.StringInt(vars["id"]))
|
|
||||||
if service == nil {
|
|
||||||
sendErrorJson(errors.New("service not found"), w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err := service.Delete()
|
|
||||||
if err != nil {
|
|
||||||
sendErrorJson(err, w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
sendJsonAction(service, "delete", w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func apiAllServicesHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if !isAPIAuthorized(r) {
|
|
||||||
sendUnauthorizedJson(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
services := core.Services()
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
json.NewEncoder(w).Encode(services)
|
|
||||||
}
|
|
||||||
|
|
||||||
func apiNotifierGetHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if !isAPIAuthorized(r) {
|
|
||||||
sendUnauthorizedJson(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
_, notifierObj, err := notifier.SelectNotifier(vars["notifier"])
|
|
||||||
if err != nil {
|
|
||||||
sendErrorJson(err, w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
json.NewEncoder(w).Encode(notifierObj)
|
|
||||||
}
|
|
||||||
|
|
||||||
func apiNotifierUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if !isAPIAuthorized(r) {
|
|
||||||
sendUnauthorizedJson(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
notifer, not, err := notifier.SelectNotifier(vars["notifier"])
|
|
||||||
if err != nil {
|
|
||||||
sendErrorJson(err, w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
decoder := json.NewDecoder(r.Body)
|
|
||||||
err = decoder.Decode(¬ifer)
|
|
||||||
if err != nil {
|
|
||||||
sendErrorJson(err, w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, err = notifier.Update(not, notifer)
|
|
||||||
if err != nil {
|
|
||||||
sendErrorJson(err, w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
notifier.OnSave(notifer.Method)
|
|
||||||
sendJsonAction(notifer, "update", w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func apiNotifiersHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if !isAPIAuthorized(r) {
|
|
||||||
sendUnauthorizedJson(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var notifiers []*notifier.Notification
|
|
||||||
for _, n := range core.CoreApp.Notifications {
|
|
||||||
notif := n.(notifier.Notifier)
|
|
||||||
notifiers = append(notifiers, notif.Select())
|
|
||||||
}
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
json.NewEncoder(w).Encode(notifiers)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendErrorJson(err error, w http.ResponseWriter, r *http.Request) {
|
func sendErrorJson(err error, w http.ResponseWriter, r *http.Request) {
|
||||||
output := apiResponse{
|
output := apiResponse{
|
||||||
Status: "error",
|
Status: "error",
|
||||||
|
@ -287,6 +101,12 @@ func sendJsonAction(obj interface{}, method string, w http.ResponseWriter, r *ht
|
||||||
case *core.User:
|
case *core.User:
|
||||||
objName = "user"
|
objName = "user"
|
||||||
objId = v.Id
|
objId = v.Id
|
||||||
|
case *core.Checkin:
|
||||||
|
objName = "checkin"
|
||||||
|
objId = v.Id
|
||||||
|
case *core.CheckinHit:
|
||||||
|
objName = "checkin_hit"
|
||||||
|
objId = v.Id
|
||||||
case *types.Message:
|
case *types.Message:
|
||||||
objName = "message"
|
objName = "message"
|
||||||
objId = v.Id
|
objId = v.Id
|
||||||
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
// Statup
|
||||||
|
// Copyright (C) 2018. Hunter Long and the project contributors
|
||||||
|
// Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||||
|
//
|
||||||
|
// https://github.com/hunterlong/statup
|
||||||
|
//
|
||||||
|
// The licenses for most software and other practical works are designed
|
||||||
|
// to take away your freedom to share and change the works. By contrast,
|
||||||
|
// the GNU General Public License is intended to guarantee your freedom to
|
||||||
|
// share and change all versions of a program--to make sure it remains free
|
||||||
|
// software for all its users.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/hunterlong/statup/core"
|
||||||
|
"github.com/hunterlong/statup/types"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func apiAllCheckinsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !isAPIAuthorized(r) {
|
||||||
|
sendUnauthorizedJson(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
checkins := core.AllCheckins()
|
||||||
|
for _, c := range checkins {
|
||||||
|
c.Hits = c.AllHits()
|
||||||
|
c.Failures = c.AllFailures()
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(checkins)
|
||||||
|
}
|
||||||
|
|
||||||
|
func apiCheckinHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !isAPIAuthorized(r) {
|
||||||
|
sendUnauthorizedJson(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
checkin := core.SelectCheckin(vars["api"])
|
||||||
|
if checkin == nil {
|
||||||
|
sendErrorJson(fmt.Errorf("checkin %v was not found", vars["api"]), w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
checkin.Hits = checkin.AllHits()
|
||||||
|
checkin.Failures = checkin.AllFailures()
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(checkin)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkinCreateHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !IsAuthenticated(r) {
|
||||||
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var checkin *core.Checkin
|
||||||
|
decoder := json.NewDecoder(r.Body)
|
||||||
|
err := decoder.Decode(&checkin)
|
||||||
|
if err != nil {
|
||||||
|
sendErrorJson(err, w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = checkin.Create()
|
||||||
|
if err != nil {
|
||||||
|
sendErrorJson(err, w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sendJsonAction(checkin, "create", w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkinHitHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
checkin := core.SelectCheckin(vars["api"])
|
||||||
|
if checkin == nil {
|
||||||
|
sendErrorJson(fmt.Errorf("checkin %v was not found", vars["api"]), w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ip, _, _ := net.SplitHostPort(r.RemoteAddr)
|
||||||
|
checkinHit := core.ReturnCheckinHit(&types.CheckinHit{
|
||||||
|
Checkin: checkin.Id,
|
||||||
|
From: ip,
|
||||||
|
CreatedAt: time.Now().UTC(),
|
||||||
|
})
|
||||||
|
if checkin.Last() == nil {
|
||||||
|
checkin.Start()
|
||||||
|
go checkin.Routine()
|
||||||
|
}
|
||||||
|
_, err := checkinHit.Create()
|
||||||
|
if err != nil {
|
||||||
|
sendErrorJson(err, w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
sendJsonAction(checkinHit, "update", w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkinDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !IsAuthenticated(r) {
|
||||||
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
checkin := core.SelectCheckin(vars["api"])
|
||||||
|
if checkin == nil {
|
||||||
|
sendErrorJson(fmt.Errorf("checkin %v was not found", vars["api"]), w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := checkin.Delete()
|
||||||
|
if err != nil {
|
||||||
|
sendErrorJson(err, w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sendJsonAction(checkin, "delete", w, r)
|
||||||
|
}
|
|
@ -80,9 +80,9 @@ func logsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
utils.LockLines.Lock()
|
utils.LockLines.Lock()
|
||||||
logs := make([]string, 0)
|
logs := make([]string, 0)
|
||||||
len := len(utils.LastLines)
|
length := len(utils.LastLines)
|
||||||
// We need string log lines from end to start.
|
// We need string log lines from end to start.
|
||||||
for i := len - 1; i >= 0; i-- {
|
for i := length - 1; i >= 0; i-- {
|
||||||
logs = append(logs, utils.LastLines[i].FormatForHtml()+"\r\n")
|
logs = append(logs, utils.LastLines[i].FormatForHtml()+"\r\n")
|
||||||
}
|
}
|
||||||
utils.LockLines.Unlock()
|
utils.LockLines.Unlock()
|
||||||
|
|
|
@ -0,0 +1,148 @@
|
||||||
|
// Statup
|
||||||
|
// Copyright (C) 2018. Hunter Long and the project contributors
|
||||||
|
// Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||||
|
//
|
||||||
|
// https://github.com/hunterlong/statup
|
||||||
|
//
|
||||||
|
// The licenses for most software and other practical works are designed
|
||||||
|
// to take away your freedom to share and change the works. By contrast,
|
||||||
|
// the GNU General Public License is intended to guarantee your freedom to
|
||||||
|
// share and change all versions of a program--to make sure it remains free
|
||||||
|
// software for all its users.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/hunterlong/statup/core"
|
||||||
|
"github.com/hunterlong/statup/core/notifier"
|
||||||
|
"github.com/hunterlong/statup/types"
|
||||||
|
"github.com/hunterlong/statup/utils"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func apiNotifiersHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !isAPIAuthorized(r) {
|
||||||
|
sendUnauthorizedJson(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var notifiers []*notifier.Notification
|
||||||
|
for _, n := range core.CoreApp.Notifications {
|
||||||
|
notif := n.(notifier.Notifier)
|
||||||
|
notifiers = append(notifiers, notif.Select())
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(notifiers)
|
||||||
|
}
|
||||||
|
|
||||||
|
func apiNotifierGetHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !isAPIAuthorized(r) {
|
||||||
|
sendUnauthorizedJson(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
_, notifierObj, err := notifier.SelectNotifier(vars["notifier"])
|
||||||
|
if err != nil {
|
||||||
|
sendErrorJson(err, w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(notifierObj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func apiNotifierUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !isAPIAuthorized(r) {
|
||||||
|
sendUnauthorizedJson(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
notifer, not, err := notifier.SelectNotifier(vars["notifier"])
|
||||||
|
if err != nil {
|
||||||
|
sendErrorJson(err, w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
decoder := json.NewDecoder(r.Body)
|
||||||
|
err = decoder.Decode(¬ifer)
|
||||||
|
if err != nil {
|
||||||
|
sendErrorJson(err, w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = notifier.Update(not, notifer)
|
||||||
|
if err != nil {
|
||||||
|
sendErrorJson(err, w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
notifier.OnSave(notifer.Method)
|
||||||
|
sendJsonAction(notifer, "update", w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var err error
|
||||||
|
if !IsAuthenticated(r) {
|
||||||
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
form := parseForm(r)
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
method := vars["method"]
|
||||||
|
enabled := form.Get("enable")
|
||||||
|
host := form.Get("host")
|
||||||
|
port := int(utils.StringInt(form.Get("port")))
|
||||||
|
username := form.Get("username")
|
||||||
|
password := form.Get("password")
|
||||||
|
var1 := form.Get("var1")
|
||||||
|
var2 := form.Get("var2")
|
||||||
|
apiKey := form.Get("api_key")
|
||||||
|
apiSecret := form.Get("api_secret")
|
||||||
|
limits := int(utils.StringInt(form.Get("limits")))
|
||||||
|
|
||||||
|
fakeNotifer, notif, err := notifier.SelectNotifier(method)
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(3, fmt.Sprintf("issue saving notifier %v: %v", method, err))
|
||||||
|
executeResponse(w, r, "settings.html", core.CoreApp, "/settings")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
notifer := *fakeNotifer
|
||||||
|
|
||||||
|
if host != "" {
|
||||||
|
notifer.Host = host
|
||||||
|
}
|
||||||
|
if port != 0 {
|
||||||
|
notifer.Port = port
|
||||||
|
}
|
||||||
|
if username != "" {
|
||||||
|
notifer.Username = username
|
||||||
|
}
|
||||||
|
if password != "" && password != "##########" {
|
||||||
|
notifer.Password = password
|
||||||
|
}
|
||||||
|
if var1 != "" {
|
||||||
|
notifer.Var1 = var1
|
||||||
|
}
|
||||||
|
if var2 != "" {
|
||||||
|
notifer.Var2 = var2
|
||||||
|
}
|
||||||
|
if apiKey != "" {
|
||||||
|
notifer.ApiKey = apiKey
|
||||||
|
}
|
||||||
|
if apiSecret != "" {
|
||||||
|
notifer.ApiSecret = apiSecret
|
||||||
|
}
|
||||||
|
if limits != 0 {
|
||||||
|
notifer.Limits = limits
|
||||||
|
}
|
||||||
|
notifer.Enabled = types.NewNullBool(enabled == "on")
|
||||||
|
|
||||||
|
err = notif.(notifier.Tester).OnTest()
|
||||||
|
if err == nil {
|
||||||
|
w.Write([]byte("ok"))
|
||||||
|
} else {
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
}
|
||||||
|
}
|
|
@ -60,16 +60,3 @@ func prometheusHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
w.Write([]byte(output))
|
w.Write([]byte(output))
|
||||||
}
|
}
|
||||||
|
|
||||||
func isAuthorized(r *http.Request) bool {
|
|
||||||
var token string
|
|
||||||
tokens, ok := r.Header["Authorization"]
|
|
||||||
if ok && len(tokens) >= 1 {
|
|
||||||
token = tokens[0]
|
|
||||||
token = strings.TrimPrefix(token, "Bearer ")
|
|
||||||
}
|
|
||||||
if token == core.CoreApp.ApiSecret {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
|
@ -241,7 +241,7 @@ func TestApiDeleteUserHandler(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestApiServiceDataHandler(t *testing.T) {
|
func TestApiServiceDataHandler(t *testing.T) {
|
||||||
grouping := []string{"minute", "hour", "day"}
|
grouping := []string{"minute", "hour", "day", "week"}
|
||||||
for _, g := range grouping {
|
for _, g := range grouping {
|
||||||
params := "?start=0&end=999999999999&group=" + g
|
params := "?start=0&end=999999999999&group=" + g
|
||||||
rr, err := httpRequestAPI(t, "GET", "/api/services/5/data"+params, nil)
|
rr, err := httpRequestAPI(t, "GET", "/api/services/5/data"+params, nil)
|
||||||
|
@ -279,6 +279,38 @@ func TestApiCheckinHandler(t *testing.T) {
|
||||||
assert.Equal(t, 200, rr.Code)
|
assert.Equal(t, 200, rr.Code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestApiCreateCheckinHandler(t *testing.T) {
|
||||||
|
data := `{
|
||||||
|
"service_id": 1,
|
||||||
|
"name": "New API Checkin",
|
||||||
|
"interval": 60,
|
||||||
|
"grace": 30}`
|
||||||
|
rr, err := httpRequestAPI(t, "POST", "/api/checkin", strings.NewReader(data))
|
||||||
|
assert.Nil(t, err)
|
||||||
|
body := rr.Body.String()
|
||||||
|
var obj apiResponse
|
||||||
|
formatJSON(body, &obj)
|
||||||
|
t.Log(body)
|
||||||
|
assert.Equal(t, 200, rr.Code)
|
||||||
|
assert.Equal(t, "create", obj.Method)
|
||||||
|
assert.Equal(t, "success", obj.Status)
|
||||||
|
assert.Equal(t, int64(1), obj.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApiAllCheckinsHandler(t *testing.T) {
|
||||||
|
rr, err := httpRequestAPI(t, "GET", "/api/checkins", nil)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
body := rr.Body.String()
|
||||||
|
assert.Equal(t, 200, rr.Code)
|
||||||
|
var obj []types.Checkin
|
||||||
|
formatJSON(body, &obj)
|
||||||
|
t.Log(body)
|
||||||
|
assert.Equal(t, int(1), len(obj))
|
||||||
|
assert.Equal(t, int64(1), obj[0].Id)
|
||||||
|
assert.Equal(t, int64(1), obj[0].ServiceId)
|
||||||
|
assert.Equal(t, "New API Checkin", obj[0].Name)
|
||||||
|
}
|
||||||
|
|
||||||
func httpRequestAPI(t *testing.T, method, url string, body io.Reader) (*httptest.ResponseRecorder, error) {
|
func httpRequestAPI(t *testing.T, method, url string, body io.Reader) (*httptest.ResponseRecorder, error) {
|
||||||
req, err := http.NewRequest(method, url, body)
|
req, err := http.NewRequest(method, url, body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -85,11 +85,6 @@ func Router() *mux.Router {
|
||||||
r.Handle("/service/{id}", http.HandlerFunc(servicesViewHandler)).Methods("GET")
|
r.Handle("/service/{id}", http.HandlerFunc(servicesViewHandler)).Methods("GET")
|
||||||
r.Handle("/service/{id}/edit", http.HandlerFunc(servicesViewHandler))
|
r.Handle("/service/{id}/edit", http.HandlerFunc(servicesViewHandler))
|
||||||
r.Handle("/service/{id}/delete_failures", http.HandlerFunc(servicesDeleteFailuresHandler)).Methods("GET")
|
r.Handle("/service/{id}/delete_failures", http.HandlerFunc(servicesDeleteFailuresHandler)).Methods("GET")
|
||||||
r.Handle("/service/{id}/checkin", http.HandlerFunc(checkinCreateHandler)).Methods("POST")
|
|
||||||
|
|
||||||
// CHECKIN Routes
|
|
||||||
r.Handle("/checkin/{id}/delete", http.HandlerFunc(checkinDeleteHandler)).Methods("GET")
|
|
||||||
r.Handle("/checkin/{id}", http.HandlerFunc(checkinHitHandler))
|
|
||||||
|
|
||||||
// API Routes
|
// API Routes
|
||||||
r.Handle("/api", http.HandlerFunc(apiIndexHandler))
|
r.Handle("/api", http.HandlerFunc(apiIndexHandler))
|
||||||
|
@ -103,7 +98,6 @@ func Router() *mux.Router {
|
||||||
r.Handle("/api/services/{id}/ping", http.HandlerFunc(apiServicePingDataHandler)).Methods("GET")
|
r.Handle("/api/services/{id}/ping", http.HandlerFunc(apiServicePingDataHandler)).Methods("GET")
|
||||||
r.Handle("/api/services/{id}", http.HandlerFunc(apiServiceUpdateHandler)).Methods("POST")
|
r.Handle("/api/services/{id}", http.HandlerFunc(apiServiceUpdateHandler)).Methods("POST")
|
||||||
r.Handle("/api/services/{id}", http.HandlerFunc(apiServiceDeleteHandler)).Methods("DELETE")
|
r.Handle("/api/services/{id}", http.HandlerFunc(apiServiceDeleteHandler)).Methods("DELETE")
|
||||||
r.Handle("/api/checkin/{api}", http.HandlerFunc(apiCheckinHandler))
|
|
||||||
|
|
||||||
// API USER Routes
|
// API USER Routes
|
||||||
r.Handle("/api/users", http.HandlerFunc(apiAllUsersHandler)).Methods("GET")
|
r.Handle("/api/users", http.HandlerFunc(apiAllUsersHandler)).Methods("GET")
|
||||||
|
@ -125,6 +119,14 @@ func Router() *mux.Router {
|
||||||
r.Handle("/api/messages/{id}", http.HandlerFunc(apiMessageUpdateHandler)).Methods("POST")
|
r.Handle("/api/messages/{id}", http.HandlerFunc(apiMessageUpdateHandler)).Methods("POST")
|
||||||
r.Handle("/api/messages/{id}", http.HandlerFunc(apiMessageDeleteHandler)).Methods("DELETE")
|
r.Handle("/api/messages/{id}", http.HandlerFunc(apiMessageDeleteHandler)).Methods("DELETE")
|
||||||
|
|
||||||
|
// API CHECKIN Routes
|
||||||
|
r.Handle("/api/checkins", http.HandlerFunc(apiAllCheckinsHandler)).Methods("GET")
|
||||||
|
r.Handle("/api/checkin/{api}", http.HandlerFunc(apiCheckinHandler)).Methods("GET")
|
||||||
|
r.Handle("/api/checkin", http.HandlerFunc(checkinCreateHandler)).Methods("POST")
|
||||||
|
r.Handle("/api/checkin/{api}", http.HandlerFunc(checkinDeleteHandler)).Methods("DELETE")
|
||||||
|
r.Handle("/checkin/{api}", http.HandlerFunc(checkinHitHandler))
|
||||||
|
|
||||||
|
// Static Files Routes
|
||||||
r.PathPrefix("/files/postman.json").Handler(http.StripPrefix("/files/", http.FileServer(source.TmplBox.HTTPBox())))
|
r.PathPrefix("/files/postman.json").Handler(http.StripPrefix("/files/", http.FileServer(source.TmplBox.HTTPBox())))
|
||||||
r.PathPrefix("/files/swagger.json").Handler(http.StripPrefix("/files/", http.FileServer(source.TmplBox.HTTPBox())))
|
r.PathPrefix("/files/swagger.json").Handler(http.StripPrefix("/files/", http.FileServer(source.TmplBox.HTTPBox())))
|
||||||
r.PathPrefix("/files/grafana.json").Handler(http.StripPrefix("/files/", http.FileServer(source.TmplBox.HTTPBox())))
|
r.PathPrefix("/files/grafana.json").Handler(http.StripPrefix("/files/", http.FileServer(source.TmplBox.HTTPBox())))
|
||||||
|
|
|
@ -17,12 +17,11 @@ package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"errors"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/hunterlong/statup/core"
|
"github.com/hunterlong/statup/core"
|
||||||
"github.com/hunterlong/statup/types"
|
"github.com/hunterlong/statup/types"
|
||||||
"github.com/hunterlong/statup/utils"
|
"github.com/hunterlong/statup/utils"
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -117,6 +116,137 @@ func servicesViewHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
executeResponse(w, r, "service.html", out, nil)
|
executeResponse(w, r, "service.html", out, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func apiServiceHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !isAPIAuthorized(r) {
|
||||||
|
sendUnauthorizedJson(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
servicer := core.SelectServicer(utils.StringInt(vars["id"]))
|
||||||
|
if servicer == nil {
|
||||||
|
sendErrorJson(errors.New("service not found"), w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
service := servicer.Select()
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(service)
|
||||||
|
}
|
||||||
|
|
||||||
|
func apiCreateServiceHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !isAPIAuthorized(r) {
|
||||||
|
sendUnauthorizedJson(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var service *types.Service
|
||||||
|
decoder := json.NewDecoder(r.Body)
|
||||||
|
err := decoder.Decode(&service)
|
||||||
|
if err != nil {
|
||||||
|
sendErrorJson(err, w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
newService := core.ReturnService(service)
|
||||||
|
_, err = newService.Create(true)
|
||||||
|
if err != nil {
|
||||||
|
sendErrorJson(err, w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sendJsonAction(newService, "create", w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func apiServiceUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !isAPIAuthorized(r) {
|
||||||
|
sendUnauthorizedJson(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
service := core.SelectServicer(utils.StringInt(vars["id"]))
|
||||||
|
if service.Select() == nil {
|
||||||
|
sendErrorJson(errors.New("service not found"), w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
decoder := json.NewDecoder(r.Body)
|
||||||
|
decoder.Decode(&service)
|
||||||
|
err := service.Update(true)
|
||||||
|
if err != nil {
|
||||||
|
sendErrorJson(err, w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go service.Check(true)
|
||||||
|
|
||||||
|
sendJsonAction(service, "update", w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func apiServiceDataHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
service := core.SelectService(utils.StringInt(vars["id"]))
|
||||||
|
if service == nil {
|
||||||
|
sendErrorJson(errors.New("service data not found"), w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fields := parseGet(r)
|
||||||
|
grouping := fields.Get("group")
|
||||||
|
if grouping == "" {
|
||||||
|
grouping = "hour"
|
||||||
|
}
|
||||||
|
startField := utils.StringInt(fields.Get("start"))
|
||||||
|
endField := utils.StringInt(fields.Get("end"))
|
||||||
|
|
||||||
|
if startField == 0 || endField == 0 {
|
||||||
|
startField = 0
|
||||||
|
endField = 99999999999
|
||||||
|
}
|
||||||
|
|
||||||
|
obj := core.GraphDataRaw(service, time.Unix(startField, 0).UTC(), time.Unix(endField, 0).UTC(), grouping, "latency")
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func apiServicePingDataHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
service := core.SelectService(utils.StringInt(vars["id"]))
|
||||||
|
if service == nil {
|
||||||
|
sendErrorJson(errors.New("service not found"), w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fields := parseGet(r)
|
||||||
|
grouping := fields.Get("group")
|
||||||
|
startField := utils.StringInt(fields.Get("start"))
|
||||||
|
endField := utils.StringInt(fields.Get("end"))
|
||||||
|
obj := core.GraphDataRaw(service, time.Unix(startField, 0), time.Unix(endField, 0), grouping, "ping_time")
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func apiServiceDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !isAPIAuthorized(r) {
|
||||||
|
sendUnauthorizedJson(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
service := core.SelectService(utils.StringInt(vars["id"]))
|
||||||
|
if service == nil {
|
||||||
|
sendErrorJson(errors.New("service not found"), w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.Delete()
|
||||||
|
if err != nil {
|
||||||
|
sendErrorJson(err, w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sendJsonAction(service, "delete", w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func apiAllServicesHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !isAPIAuthorized(r) {
|
||||||
|
sendUnauthorizedJson(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
services := core.Services()
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(services)
|
||||||
|
}
|
||||||
|
|
||||||
func servicesDeleteFailuresHandler(w http.ResponseWriter, r *http.Request) {
|
func servicesDeleteFailuresHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if !IsAuthenticated(r) {
|
if !IsAuthenticated(r) {
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
@ -127,56 +257,3 @@ func servicesDeleteFailuresHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
service.DeleteFailures()
|
service.DeleteFailures()
|
||||||
executeResponse(w, r, "services.html", core.CoreApp.Services, "/services")
|
executeResponse(w, r, "services.html", core.CoreApp.Services, "/services")
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkinDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if !IsAuthenticated(r) {
|
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
checkin := core.SelectCheckinId(utils.StringInt(vars["id"]))
|
|
||||||
service := core.SelectService(checkin.ServiceId)
|
|
||||||
fmt.Println(checkin, service)
|
|
||||||
checkin.Delete()
|
|
||||||
executeResponse(w, r, "service.html", service, fmt.Sprintf("/service/%v", service.Id))
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkinCreateHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if !IsAuthenticated(r) {
|
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
r.ParseForm()
|
|
||||||
service := core.SelectService(utils.StringInt(vars["id"]))
|
|
||||||
fmt.Println(service.Name)
|
|
||||||
name := r.PostForm.Get("name")
|
|
||||||
interval := utils.StringInt(r.PostForm.Get("interval"))
|
|
||||||
grace := utils.StringInt(r.PostForm.Get("grace"))
|
|
||||||
checkin := core.ReturnCheckin(&types.Checkin{
|
|
||||||
Name: name,
|
|
||||||
ServiceId: service.Id,
|
|
||||||
Interval: interval,
|
|
||||||
GracePeriod: grace,
|
|
||||||
})
|
|
||||||
checkin.Create()
|
|
||||||
executeResponse(w, r, "service.html", service, fmt.Sprintf("/service/%v", service.Id))
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkinHitHandler(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(),
|
|
||||||
})
|
|
||||||
if checkin.Last() == nil {
|
|
||||||
checkin.Start()
|
|
||||||
go checkin.Routine()
|
|
||||||
}
|
|
||||||
checkinHit.Create()
|
|
||||||
w.Write([]byte("ok"))
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
}
|
|
||||||
|
|
|
@ -16,10 +16,8 @@
|
||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/hunterlong/statup/core"
|
"github.com/hunterlong/statup/core"
|
||||||
"github.com/hunterlong/statup/core/notifier"
|
|
||||||
"github.com/hunterlong/statup/source"
|
"github.com/hunterlong/statup/source"
|
||||||
"github.com/hunterlong/statup/types"
|
"github.com/hunterlong/statup/types"
|
||||||
"github.com/hunterlong/statup/utils"
|
"github.com/hunterlong/statup/utils"
|
||||||
|
@ -134,69 +132,3 @@ func parseGet(r *http.Request) url.Values {
|
||||||
r.ParseForm()
|
r.ParseForm()
|
||||||
return r.Form
|
return r.Form
|
||||||
}
|
}
|
||||||
|
|
||||||
func testNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
var err error
|
|
||||||
if !IsAuthenticated(r) {
|
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
form := parseForm(r)
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
method := vars["method"]
|
|
||||||
enabled := form.Get("enable")
|
|
||||||
host := form.Get("host")
|
|
||||||
port := int(utils.StringInt(form.Get("port")))
|
|
||||||
username := form.Get("username")
|
|
||||||
password := form.Get("password")
|
|
||||||
var1 := form.Get("var1")
|
|
||||||
var2 := form.Get("var2")
|
|
||||||
apiKey := form.Get("api_key")
|
|
||||||
apiSecret := form.Get("api_secret")
|
|
||||||
limits := int(utils.StringInt(form.Get("limits")))
|
|
||||||
|
|
||||||
fakeNotifer, notif, err := notifier.SelectNotifier(method)
|
|
||||||
if err != nil {
|
|
||||||
utils.Log(3, fmt.Sprintf("issue saving notifier %v: %v", method, err))
|
|
||||||
executeResponse(w, r, "settings.html", core.CoreApp, "/settings")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
notifer := *fakeNotifer
|
|
||||||
|
|
||||||
if host != "" {
|
|
||||||
notifer.Host = host
|
|
||||||
}
|
|
||||||
if port != 0 {
|
|
||||||
notifer.Port = port
|
|
||||||
}
|
|
||||||
if username != "" {
|
|
||||||
notifer.Username = username
|
|
||||||
}
|
|
||||||
if password != "" && password != "##########" {
|
|
||||||
notifer.Password = password
|
|
||||||
}
|
|
||||||
if var1 != "" {
|
|
||||||
notifer.Var1 = var1
|
|
||||||
}
|
|
||||||
if var2 != "" {
|
|
||||||
notifer.Var2 = var2
|
|
||||||
}
|
|
||||||
if apiKey != "" {
|
|
||||||
notifer.ApiKey = apiKey
|
|
||||||
}
|
|
||||||
if apiSecret != "" {
|
|
||||||
notifer.ApiSecret = apiSecret
|
|
||||||
}
|
|
||||||
if limits != 0 {
|
|
||||||
notifer.Limits = limits
|
|
||||||
}
|
|
||||||
notifer.Enabled = types.NewNullBool(enabled == "on")
|
|
||||||
|
|
||||||
err = notif.(notifier.Tester).OnTest()
|
|
||||||
if err == nil {
|
|
||||||
w.Write([]byte("ok"))
|
|
||||||
} else {
|
|
||||||
w.Write([]byte(err.Error()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/hunterlong/statup/core/notifier"
|
"github.com/hunterlong/statup/core/notifier"
|
||||||
"github.com/hunterlong/statup/types"
|
"github.com/hunterlong/statup/types"
|
||||||
|
"github.com/hunterlong/statup/utils"
|
||||||
"github.com/oliveroneill/exponent-server-sdk-golang/sdk"
|
"github.com/oliveroneill/exponent-server-sdk-golang/sdk"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -58,12 +59,37 @@ func (u *mobilePush) Select() *notifier.Notification {
|
||||||
return u.Notification
|
return u.Notification
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func dataJson(s *types.Service, f *types.Failure) map[string]string {
|
||||||
|
serviceId := "0"
|
||||||
|
if s != nil {
|
||||||
|
serviceId = utils.ToString(s.Id)
|
||||||
|
}
|
||||||
|
online := "online"
|
||||||
|
if !s.Online {
|
||||||
|
online = "offline"
|
||||||
|
}
|
||||||
|
issue := ""
|
||||||
|
if f != nil {
|
||||||
|
issue = f.Issue
|
||||||
|
}
|
||||||
|
link := fmt.Sprintf("statup://service?id=%v", serviceId)
|
||||||
|
out := map[string]string{
|
||||||
|
"status": online,
|
||||||
|
"id": serviceId,
|
||||||
|
"issue": issue,
|
||||||
|
"link": link,
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// OnFailure will trigger failing service
|
// OnFailure will trigger failing service
|
||||||
func (u *mobilePush) OnFailure(s *types.Service, f *types.Failure) {
|
func (u *mobilePush) OnFailure(s *types.Service, f *types.Failure) {
|
||||||
|
data := dataJson(s, f)
|
||||||
msg := &expo.PushMessage{
|
msg := &expo.PushMessage{
|
||||||
Body: fmt.Sprintf("Your service '%v' is currently failing! Reason: %v", s.Name, f.Issue),
|
Body: fmt.Sprintf("Your service '%v' is currently failing! Reason: %v", s.Name, f.Issue),
|
||||||
Sound: "default",
|
Sound: "default",
|
||||||
Title: "Service Offline",
|
Title: "Service Offline",
|
||||||
|
Data: data,
|
||||||
Priority: expo.DefaultPriority,
|
Priority: expo.DefaultPriority,
|
||||||
}
|
}
|
||||||
u.AddQueue(s.Id, msg)
|
u.AddQueue(s.Id, msg)
|
||||||
|
@ -72,12 +98,14 @@ func (u *mobilePush) OnFailure(s *types.Service, f *types.Failure) {
|
||||||
|
|
||||||
// OnSuccess will trigger successful service
|
// OnSuccess will trigger successful service
|
||||||
func (u *mobilePush) OnSuccess(s *types.Service) {
|
func (u *mobilePush) OnSuccess(s *types.Service) {
|
||||||
|
data := dataJson(s, nil)
|
||||||
if !u.Online {
|
if !u.Online {
|
||||||
u.ResetUniqueQueue(s.Id)
|
u.ResetUniqueQueue(s.Id)
|
||||||
msg := &expo.PushMessage{
|
msg := &expo.PushMessage{
|
||||||
Body: fmt.Sprintf("Your service '%v' is back online!", s.Name),
|
Body: fmt.Sprintf("Your service '%v' is back online!", s.Name),
|
||||||
Sound: "default",
|
Sound: "default",
|
||||||
Title: "Service Online",
|
Title: "Service Online",
|
||||||
|
Data: data,
|
||||||
Priority: expo.DefaultPriority,
|
Priority: expo.DefaultPriority,
|
||||||
}
|
}
|
||||||
u.AddQueue(s.Id, msg)
|
u.AddQueue(s.Id, msg)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{{define "form_checkin"}}
|
{{define "form_checkin"}}
|
||||||
<form action="/service/{{.Id}}/checkin" method="POST">
|
<form class="ajax_form" action="/api/checkin" data-redirect="/service/{{.Id}}" method="POST">
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<label for="checkin_interval" class="col-form-label">Checkin Name</label>
|
<label for="checkin_interval" class="col-form-label">Checkin Name</label>
|
||||||
|
@ -14,8 +14,9 @@
|
||||||
<input type="number" name="grace" class="form-control" id="grace_period" placeholder="10">
|
<input type="number" name="grace" class="form-control" id="grace_period" placeholder="10">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-3">
|
<div class="col-3">
|
||||||
<label for="checkin_interval" class="col-form-label"></label>
|
<label for="submit" class="col-form-label"></label>
|
||||||
<button type="submit" class="btn btn-success d-block">Save Checkin</button>
|
<input type="hidden" name="service_id" class="form-control" id="checkin_name" placeholder="New Checkin">
|
||||||
|
<button type="submit" id="submit" class="btn btn-success d-block">Save Checkin</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<button type="submit" class="btn btn-primary btn-block">Sign in</button>
|
<button type="submit" class="btn btn-primary btn-block mb-3">Sign in</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1103,6 +1103,210 @@
|
||||||
"response": []
|
"response": []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Checkins",
|
||||||
|
"item": [
|
||||||
|
{
|
||||||
|
"name": "Run Checkin",
|
||||||
|
"event": [
|
||||||
|
{
|
||||||
|
"listen": "test",
|
||||||
|
"script": {
|
||||||
|
"id": "652a1c5b-5379-43c2-9cd8-ebd8f8dad0f6",
|
||||||
|
"exec": [
|
||||||
|
"pm.test(\"Hit the Checkin API Endpoint\", function () {",
|
||||||
|
" var jsonData = pm.response.json();",
|
||||||
|
" pm.expect(jsonData.status).to.eql(\"success\");",
|
||||||
|
" pm.expect(jsonData.type).to.eql(\"checkin_type\");",
|
||||||
|
"});"
|
||||||
|
],
|
||||||
|
"type": "text/javascript"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"header": [],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": ""
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"raw": "{{endpoint}}/checkin/HqQ4zoq",
|
||||||
|
"host": [
|
||||||
|
"{{endpoint}}"
|
||||||
|
],
|
||||||
|
"path": [
|
||||||
|
"checkin",
|
||||||
|
"HqQ4zoq"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "View Checkin",
|
||||||
|
"event": [
|
||||||
|
{
|
||||||
|
"listen": "test",
|
||||||
|
"script": {
|
||||||
|
"id": "93ff6b86-368b-406f-9d75-07338714ebca",
|
||||||
|
"exec": [
|
||||||
|
"pm.test(\"View Checkin\", function () {",
|
||||||
|
" var jsonData = pm.response.json();",
|
||||||
|
" pm.expect(jsonData.name).to.eql(\"Server Checkin\");",
|
||||||
|
" pm.expect(jsonData.grace).to.eql(60);",
|
||||||
|
"});"
|
||||||
|
],
|
||||||
|
"type": "text/javascript"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"header": [],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": ""
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"raw": "{{endpoint}}/api/checkin/HqQ4zoq",
|
||||||
|
"host": [
|
||||||
|
"{{endpoint}}"
|
||||||
|
],
|
||||||
|
"path": [
|
||||||
|
"api",
|
||||||
|
"checkin",
|
||||||
|
"HqQ4zoq"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "View All Checkin's",
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"header": [],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": ""
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"raw": "{{endpoint}}/api/checkins",
|
||||||
|
"host": [
|
||||||
|
"{{endpoint}}"
|
||||||
|
],
|
||||||
|
"path": [
|
||||||
|
"api",
|
||||||
|
"checkins"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Create Checkin",
|
||||||
|
"request": {
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"name": "Content-Type",
|
||||||
|
"value": "application/json",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\n \"service_id\": 14,\n \"name\": \"Server Checkin\",\n \"interval\": 900,\n \"grace\": 60\n}"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"raw": "{{endpoint}}/api/checkin",
|
||||||
|
"host": [
|
||||||
|
"{{endpoint}}"
|
||||||
|
],
|
||||||
|
"path": [
|
||||||
|
"api",
|
||||||
|
"checkin"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": [
|
||||||
|
{
|
||||||
|
"name": "Create Checkin",
|
||||||
|
"originalRequest": {
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"name": "Content-Type",
|
||||||
|
"value": "application/json",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\n \"service_id\": 14,\n \"name\": \"Server Checkin\",\n \"interval\": 900,\n \"grace\": 60\n}"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"raw": "{{endpoint}}/api/checkin",
|
||||||
|
"host": [
|
||||||
|
"{{endpoint}}"
|
||||||
|
],
|
||||||
|
"path": [
|
||||||
|
"api",
|
||||||
|
"checkin"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"status": "OK",
|
||||||
|
"code": 200,
|
||||||
|
"_postman_previewlanguage": "json",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "Date",
|
||||||
|
"value": "Tue, 20 Nov 2018 20:37:10 GMT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "Content-Length",
|
||||||
|
"value": "265"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cookie": [],
|
||||||
|
"body": "{\n \"status\": \"success\",\n \"type\": \"checkin\",\n \"method\": \"create\",\n \"id\": 4,\n \"output\": {\n \"id\": 4,\n \"service_id\": 14,\n \"name\": \"Server Checkin\",\n \"interval\": 900,\n \"grace\": 60,\n \"api_key\": \"AJ0GsdG\",\n \"created_at\": \"2018-11-20T20:37:10.437194Z\",\n \"updated_at\": \"2018-11-20T20:37:10.437194Z\",\n \"hits\": null\n }\n}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Delete Checkin",
|
||||||
|
"request": {
|
||||||
|
"method": "DELETE",
|
||||||
|
"header": [],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": ""
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"raw": "{{endpoint}}/api/checkin/HqQ4zoq",
|
||||||
|
"host": [
|
||||||
|
"{{endpoint}}"
|
||||||
|
],
|
||||||
|
"path": [
|
||||||
|
"api",
|
||||||
|
"checkin",
|
||||||
|
"HqQ4zoq"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -122,10 +122,10 @@
|
||||||
<th scope="col"></th>
|
<th scope="col"></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody style="font-size: 10pt;">
|
||||||
{{range $s.LimitedCheckins}}
|
{{range $s.LimitedCheckins}}
|
||||||
{{ $ch := . }}
|
{{ $ch := . }}
|
||||||
<tr 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>
|
||||||
<td>every {{Duration $ch.Period}}<br>after {{Duration $ch.Grace}}</td>
|
<td>every {{Duration $ch.Period}}<br>after {{Duration $ch.Grace}}</td>
|
||||||
<td>{{ if $ch.Last.CreatedAt.IsZero}}
|
<td>{{ if $ch.Last.CreatedAt.IsZero}}
|
||||||
|
@ -141,7 +141,7 @@
|
||||||
{{ if lt $ch.Expected 0}}{{Duration $ch.Expected}} ago{{else}}in {{Duration $ch.Expected}}{{end}}
|
{{ if lt $ch.Expected 0}}{{Duration $ch.Expected}} ago{{else}}in {{Duration $ch.Expected}}{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
</td>
|
</td>
|
||||||
<td><a href="/checkin/{{$ch.Id}}/delete" class="btn btn-sm btn-danger">Delete</a></td>
|
<td><a href="/api/checkin/{{$ch.Id}}" data-method="DELETE" data-obj="checkin_{{$ch.Id}}" data-id="{{$ch.Id}}" class="ajax_delete btn btn-sm btn-danger">Delete</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{{end}}
|
{{end}}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
@ -21,22 +21,24 @@ import (
|
||||||
|
|
||||||
// Checkin struct will allow an application to send a recurring HTTP GET to confirm a service is online
|
// Checkin struct will allow an application to send a recurring HTTP GET to confirm a service is online
|
||||||
type Checkin struct {
|
type Checkin struct {
|
||||||
Id int64 `gorm:"primary_key;column:id"`
|
Id int64 `gorm:"primary_key;column:id" json:"id"`
|
||||||
ServiceId int64 `gorm:"index;column:service"`
|
ServiceId int64 `gorm:"index;column:service" json:"service_id"`
|
||||||
Name string `gorm:"column:name"`
|
Name string `gorm:"column:name" json:"name"`
|
||||||
Interval int64 `gorm:"column:check_interval"`
|
Interval int64 `gorm:"column:check_interval" json:"interval"`
|
||||||
GracePeriod int64 `gorm:"column:grace_period"`
|
GracePeriod int64 `gorm:"column:grace_period" json:"grace"`
|
||||||
ApiKey string `gorm:"column:api_key"`
|
ApiKey string `gorm:"column:api_key" json:"api_key"`
|
||||||
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:"-"`
|
||||||
|
Hits []*CheckinHit `gorm:"-" json:"hits"`
|
||||||
|
Failures []*Failure `gorm:"-" json:"failures"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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"`
|
Id int64 `gorm:"primary_key;column:id" json:"id"`
|
||||||
Checkin int64 `gorm:"index;column:checkin"`
|
Checkin int64 `gorm:"index;column:checkin" json:"checkin"`
|
||||||
From string `gorm:"column:from_location"`
|
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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue