notifications!!!

pull/429/head
Hunter Long 2020-03-13 20:13:20 -07:00
parent d8a02b31a2
commit 12275f7522
34 changed files with 481 additions and 1318 deletions

View File

@ -1,13 +1,13 @@
package main
import (
"github.com/statping/statping/notifiers"
"github.com/statping/statping/types/checkins"
"github.com/statping/statping/types/failures"
"github.com/statping/statping/types/groups"
"github.com/statping/statping/types/hits"
"github.com/statping/statping/types/incidents"
"github.com/statping/statping/types/messages"
"github.com/statping/statping/types/notifications"
"github.com/statping/statping/types/services"
"github.com/statping/statping/types/users"
)
@ -18,5 +18,5 @@ var (
)
func init() {
DbModels = []interface{}{&services.Service{}, &users.User{}, &hits.Hit{}, &failures.Failure{}, &messages.Message{}, &groups.Group{}, &checkins.Checkin{}, &checkins.CheckinHit{}, &notifiers.Notification{}, &incidents.Incident{}, &incidents.IncidentUpdate{}}
DbModels = []interface{}{&services.Service{}, &users.User{}, &hits.Hit{}, &failures.Failure{}, &messages.Message{}, &groups.Group{}, &checkins.Checkin{}, &checkins.CheckinHit{}, &notifications.Notification{}, &incidents.Incident{}, &incidents.IncidentUpdate{}}
}

View File

@ -1,12 +1,14 @@
package core
package main
import (
"github.com/statping/statping/database"
"github.com/statping/statping/notifiers"
"github.com/statping/statping/types/core"
"github.com/statping/statping/types/services"
)
func InitApp() error {
if _, err := Select(); err != nil {
if _, err := core.Select(); err != nil {
return err
}
@ -16,7 +18,9 @@ func InitApp() error {
go services.CheckServices()
notifiers.InitNotifiers()
database.StartMaintenceRoutine()
App.Setup = true
core.App.Setup = true
return nil
}

View File

@ -19,7 +19,6 @@ import (
"flag"
"fmt"
"github.com/getsentry/sentry-go"
"github.com/statping/statping/notifiers"
"os"
"os/signal"
"syscall"
@ -30,7 +29,6 @@ import (
"github.com/pkg/errors"
"github.com/statping/statping/handlers"
"github.com/statping/statping/types/configs"
"github.com/statping/statping/types/core"
"github.com/statping/statping/types/services"
"github.com/statping/statping/utils"
)
@ -165,11 +163,11 @@ func main() {
exit(err)
}
log.Infoln("Migrating Notifiers...")
if err := notifiers.Migrate(); err != nil {
exit(errors.Wrap(err, "error migrating notifiers"))
}
log.Infoln("Notifiers Migrated")
//log.Infoln("Migrating Notifiers...")
//if err := notifier.Migrate(); err != nil {
// exit(errors.Wrap(err, "error migrating notifiers"))
//}
//log.Infoln("Notifiers Migrated")
if err := mainProcess(); err != nil {
exit(err)
@ -205,7 +203,7 @@ func mainProcess() error {
return errors.Wrap(err, errStr)
}
if err := core.InitApp(); err != nil {
if err := InitApp(); err != nil {
return err
}

View File

@ -18,7 +18,7 @@
{{if USING_ASSETS}}
<link href="css/vendor.css" rel="stylesheet">
<link href="css/style.css" rel="stylesheet">
<link href="css/base.css" rel="stylesheet">
<link href="css/main.css" rel="stylesheet">
{{else}}
<% _.each(htmlWebpackPlugin.tags.headTags, function(headTag) { %>
<%= headTag %> <% }) %>

View File

@ -94,8 +94,8 @@ export default {
this.form[field] = this.notifier[field]
});
await Api.notifier_save(this.form)
const notifiers = await Api.notifiers()
await this.$store.commit('setNotifiers', notifiers)
// const notifiers = await Api.notifiers()
// await this.$store.commit('setNotifiers', notifiers)
this.saved = true
this.loading = false
setTimeout(() => {

View File

@ -19,12 +19,12 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/statping/statping/notifiers"
"github.com/statping/statping/types/checkins"
"github.com/statping/statping/types/core"
"github.com/statping/statping/types/groups"
"github.com/statping/statping/types/incidents"
"github.com/statping/statping/types/messages"
"github.com/statping/statping/types/notifications"
"github.com/statping/statping/types/null"
"github.com/statping/statping/types/services"
"github.com/statping/statping/types/users"
@ -143,7 +143,7 @@ func sendJsonAction(obj interface{}, method string, w http.ResponseWriter, r *ht
case *services.Service:
objName = "service"
objId = v.Id
case *notifiers.Notification:
case *notifications.Notification:
objName = "notifier"
objId = v.Id
case *core.Core:

View File

@ -1,42 +1,43 @@
// Statping
// Copyright (C) 2018. Hunter Long and the project contributors
// Written by Hunter Long <info@socialeck.com> and the project contributors
//// Statping
//// Copyright (C) 2018. Hunter Long and the project contributors
//// Written by Hunter Long <info@socialeck.com> and the project contributors
////
//// https://github.com/statping/statping
////
//// 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/>.
//
// https://github.com/statping/statping
//
// 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/statping/statping/notifiers"
"github.com/statping/statping/types/notifications"
"github.com/statping/statping/types/null"
"github.com/statping/statping/types/services"
"github.com/statping/statping/utils"
"net/http"
)
func apiNotifiersHandler(w http.ResponseWriter, r *http.Request) {
var notifs []notifiers.Notifier
all := notifiers.All()
for _, v := range all {
notifs = append(notifs, v)
notifiers := services.AllNotifiers()
var notifs []*notifications.Notification
for _, n := range notifiers {
notifs = append(notifs, n.Select())
}
returnJson(notifs, w, r)
}
func apiNotifierGetHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
notifier, err := notifiers.Find(vars["notifier"])
notifier, err := notifications.Find(vars["notifier"])
if err != nil {
sendErrorJson(err, w, r)
return
@ -46,11 +47,12 @@ func apiNotifierGetHandler(w http.ResponseWriter, r *http.Request) {
func apiNotifierUpdateHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
notifer, err := notifiers.Find(vars["notifier"])
notifer, err := notifications.Find(vars["notifier"])
if err != nil {
sendErrorJson(err, w, r)
return
}
defer r.Body.Close()
decoder := json.NewDecoder(r.Body)
err = decoder.Decode(&notifer)
@ -83,7 +85,7 @@ func testNotificationHandler(w http.ResponseWriter, r *http.Request) {
apiSecret := form.Get("api_secret")
limits := int(utils.ToInt(form.Get("limits")))
notifier, err := notifiers.Find(method)
notifier, err := notifications.Find(method)
if err != nil {
log.Errorln(fmt.Sprintf("issue saving notifier %v: %v", method, err))
sendErrorJson(err, w, r)

View File

@ -17,7 +17,6 @@ package handlers
import (
"fmt"
"github.com/statping/statping/notifiers"
"github.com/statping/statping/types/failures"
"github.com/statping/statping/types/services"
"github.com/statping/statping/utils"
@ -104,25 +103,22 @@ func prometheusHandler(w http.ResponseWriter, r *http.Request) {
}
for _, n := range notifiers.All() {
for _, n := range services.AllNotifiers() {
notif := n.Select()
PrometheusComment(fmt.Sprintf("Notifier %s:", notif.Method))
enabled := 0
if notif.Enabled.Bool {
enabled = 1
PrometheusExportKey("notifier_on_success", notif.Id, notif.Method, notif.Hits.OnSuccess)
PrometheusExportKey("notifier_on_failure", notif.Id, notif.Method, notif.Hits.OnFailure)
PrometheusExportKey("notifier_on_user_new", notif.Id, notif.Method, notif.Hits.OnNewUser)
PrometheusExportKey("notifier_on_user_update", notif.Id, notif.Method, notif.Hits.OnUpdatedUser)
PrometheusExportKey("notifier_on_user_delete", notif.Id, notif.Method, notif.Hits.OnDeletedUser)
PrometheusExportKey("notifier_on_service_new", notif.Id, notif.Method, notif.Hits.OnNewService)
PrometheusExportKey("notifier_on_service_update", notif.Id, notif.Method, notif.Hits.OnUpdatedService)
PrometheusExportKey("notifier_on_service_delete", notif.Id, notif.Method, notif.Hits.OnDeletedService)
PrometheusExportKey("notifier_on_notifier_new", notif.Id, notif.Method, notif.Hits.OnNewNotifier)
PrometheusExportKey("notifier_on_notifier_update", notif.Id, notif.Method, notif.Hits.OnUpdatedNotifier)
PrometheusExportKey("notifier_on_notifier_save", notif.Id, notif.Method, notif.Hits.OnSave)
}
PrometheusExportKey("notifier_enabled", notif.Id, notif.Method, enabled)
PrometheusExportKey("notifier_on_success", notif.Id, notif.Method, notif.Hits.OnSuccess)
PrometheusExportKey("notifier_on_failure", notif.Id, notif.Method, notif.Hits.OnFailure)
PrometheusExportKey("notifier_on_user_new", notif.Id, notif.Method, notif.Hits.OnNewUser)
PrometheusExportKey("notifier_on_user_update", notif.Id, notif.Method, notif.Hits.OnUpdatedUser)
PrometheusExportKey("notifier_on_user_delete", notif.Id, notif.Method, notif.Hits.OnDeletedUser)
PrometheusExportKey("notifier_on_service_new", notif.Id, notif.Method, notif.Hits.OnNewService)
PrometheusExportKey("notifier_on_service_update", notif.Id, notif.Method, notif.Hits.OnUpdatedService)
PrometheusExportKey("notifier_on_service_delete", notif.Id, notif.Method, notif.Hits.OnDeletedService)
PrometheusExportKey("notifier_on_notifier_new", notif.Id, notif.Method, notif.Hits.OnNewNotifier)
PrometheusExportKey("notifier_on_notifier_update", notif.Id, notif.Method, notif.Hits.OnUpdatedNotifier)
PrometheusExportKey("notifier_on_notifier_save", notif.Id, notif.Method, notif.Hits.OnSave)
}
PrometheusComment("HTTP Metrics")

View File

@ -17,10 +17,10 @@ package handlers
import (
"errors"
"github.com/statping/statping/notifiers"
"github.com/statping/statping/types/configs"
"github.com/statping/statping/types/core"
"github.com/statping/statping/types/null"
"github.com/statping/statping/types/services"
"github.com/statping/statping/utils"
"net/http"
"time"
@ -89,11 +89,11 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
return
}
log.Infoln("Migrating Notifiers...")
if err := notifiers.Migrate(); err != nil {
sendErrorJson(err, w, r)
return
}
//log.Infoln("Migrating Notifiers...")
//if err := notifications.Migrate(); err != nil {
// sendErrorJson(err, w, r)
// return
//}
c := &core.Core{
Name: "Statping Sample Data",
@ -117,12 +117,17 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
core.App = c
log.Infoln("Initializing new Statping instance")
if err := core.InitApp(); err != nil {
if _, err := services.SelectAllServices(true); err != nil {
log.Errorln(err)
sendErrorJson(err, w, r)
return
}
go services.CheckServices()
core.App.Setup = true
CacheStorage.Delete("/")
resetCookies()
time.Sleep(1 * time.Second)

View File

@ -16,21 +16,26 @@
package notifiers
import (
"fmt"
"github.com/statping/statping/types/failures"
"github.com/statping/statping/types/notifications"
"github.com/statping/statping/types/notifier"
"github.com/statping/statping/types/services"
"github.com/statping/statping/utils"
"strings"
"time"
)
var _ Notifier = (*commandLine)(nil)
var _ notifier.Notifier = (*commandLine)(nil)
type commandLine struct {
*Notification
*notifications.Notification
}
var Command = &commandLine{&Notification{
func (c *commandLine) Select() *notifications.Notification {
return c.Notification
}
var Command = &commandLine{&notifications.Notification{
Method: "command",
Title: "Shell Command",
Description: "Shell Command allows you to run a customized shell/bash Command on the local machine it's running on.",
@ -39,7 +44,7 @@ var Command = &commandLine{&Notification{
Delay: time.Duration(1 * time.Second),
Icon: "fas fa-terminal",
Host: "/bin/bash",
Form: []NotificationForm{{
Form: []notifications.NotificationForm{{
Type: "text",
Title: "Shell or Bash",
Placeholder: "/bin/bash",
@ -65,28 +70,18 @@ func runCommand(app string, cmd ...string) (string, string, error) {
return outStr, errStr, err
}
func (u *commandLine) Select() *Notification {
return u.Notification
}
// OnFailure for commandLine will trigger failing service
func (u *commandLine) OnFailure(s *services.Service, f *failures.Failure) {
u.AddQueue(fmt.Sprintf("service_%v", s.Id), u.Var2)
func (u *commandLine) OnFailure(s *services.Service, f *failures.Failure) error {
msg := u.GetValue("host")
_, _, err := runCommand(u.Host, msg)
return err
}
// OnSuccess for commandLine will trigger successful service
func (u *commandLine) OnSuccess(s *services.Service) {
if !s.Online {
u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id))
u.AddQueue(fmt.Sprintf("service_%v", s.Id), u.Var1)
}
}
// OnSave for commandLine triggers when this notifier has been saved
func (u *commandLine) OnSave() error {
u.AddQueue("saved", u.Var1)
u.AddQueue("saved", u.Var2)
return nil
func (u *commandLine) OnSuccess(s *services.Service) error {
msg := u.GetValue("host")
_, _, err := runCommand(u.Host, msg)
return err
}
// OnTest for commandLine triggers when this notifier has been saved
@ -97,10 +92,3 @@ func (u *commandLine) OnTest() error {
utils.Log.Infoln(out)
return err
}
// Send for commandLine will send message to expo Command push notifications endpoint
func (u *commandLine) Send(msg interface{}) error {
cmd := msg.(string)
_, _, err := runCommand(u.Host, cmd)
return err
}

View File

@ -21,19 +21,21 @@ import (
"errors"
"fmt"
"github.com/statping/statping/types/failures"
"github.com/statping/statping/types/notifications"
"github.com/statping/statping/types/notifier"
"github.com/statping/statping/types/services"
"github.com/statping/statping/utils"
"strings"
"time"
)
var _ Notifier = (*discord)(nil)
var _ notifier.Notifier = (*discord)(nil)
type discord struct {
*Notification
*notifications.Notification
}
var Discorder = &discord{&Notification{
var Discorder = &discord{&notifications.Notification{
Method: "discord",
Title: "discord",
Description: "Send notifications to your discord channel using discord webhooks. Insert your discord channel Webhook URL to receive notifications. Based on the <a href=\"https://discordapp.com/developers/docs/resources/Webhook\">discord webhooker API</a>.",
@ -42,7 +44,7 @@ var Discorder = &discord{&Notification{
Delay: time.Duration(5 * time.Second),
Host: "https://discordapp.com/api/webhooks/****/*****",
Icon: "fab fa-discord",
Form: []NotificationForm{{
Form: []notifications.NotificationForm{{
Type: "text",
Title: "discord webhooker URL",
Placeholder: "Insert your Webhook URL here",
@ -51,38 +53,25 @@ var Discorder = &discord{&Notification{
}
// Send will send a HTTP Post to the discord API. It accepts type: []byte
func (u *discord) Send(msg interface{}) error {
message := msg.(string)
_, _, err := utils.HttpRequest(Discorder.GetValue("host"), "POST", "application/json", nil, strings.NewReader(message), time.Duration(10*time.Second), true)
func (u *discord) sendRequest(msg string) error {
_, _, err := utils.HttpRequest(Discorder.GetValue("host"), "POST", "application/json", nil, strings.NewReader(msg), time.Duration(10*time.Second), true)
return err
}
func (u *discord) Select() *Notification {
func (u *discord) Select() *notifications.Notification {
return u.Notification
}
// OnFailure will trigger failing service
func (u *discord) OnFailure(s *services.Service, f *failures.Failure) {
func (u *discord) OnFailure(s *services.Service, f *failures.Failure) error {
msg := fmt.Sprintf(`{"content": "Your service '%v' is currently failing! Reason: %v"}`, s.Name, f.Issue)
u.AddQueue(fmt.Sprintf("service_%v", s.Id), msg)
return u.sendRequest(msg)
}
// OnSuccess will trigger successful service
func (u *discord) OnSuccess(s *services.Service) {
if !s.Online || !s.SuccessNotified {
u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id))
var msg interface{}
msg = s.DownText
u.AddQueue(fmt.Sprintf("service_%v", s.Id), msg)
}
}
// OnSave triggers when this notifier has been saved
func (u *discord) OnSave() error {
msg := fmt.Sprintf(`{"content": "The discord notifier on Statping was just updated."}`)
u.AddQueue("saved", msg)
return nil
func (u *discord) OnSuccess(s *services.Service) error {
msg := fmt.Sprintf(`{"content": "Your service '%s' is currently online!"}`, s.Name)
return u.sendRequest(msg)
}
// OnSave triggers when this notifier has been saved

View File

@ -1,9 +0,0 @@
// Package notifiers holds all the notifiers for Statping, which also includes
// user created notifiers that have been accepted in a Push Request. Read the wiki
// to see a full example of a notifier with all events, visit Statping's
// notifier example code: https://github.com/statping/statping/wiki/Notifier-Example
//
// This package shouldn't contain any exports, to see how notifiers work
// visit the core/notifier package at: https://godoc.org/github.com/statping/statping/core/notifier
// and learn how to create your own custom notifier.
package notifiers

View File

@ -21,6 +21,8 @@ import (
"fmt"
"github.com/go-mail/mail"
"github.com/statping/statping/types/failures"
"github.com/statping/statping/types/notifications"
"github.com/statping/statping/types/notifier"
"github.com/statping/statping/types/null"
"github.com/statping/statping/types/services"
"github.com/statping/statping/utils"
@ -28,7 +30,7 @@ import (
"time"
)
var _ Notifier = (*email)(nil)
var _ notifier.Notifier = (*emailer)(nil)
const (
mainEmailTemplate = `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
@ -110,18 +112,22 @@ var (
mailer *mail.Dialer
)
type email struct {
*Notification
type emailer struct {
*notifications.Notification
}
var Emailer = &email{&Notification{
func (e *emailer) Select() *notifications.Notification {
return e.Notification
}
var email = &emailer{&notifications.Notification{
Method: "email",
Title: "email",
Description: "Send emails via SMTP when services are online or offline.",
Author: "Hunter Long",
AuthorUrl: "https://github.com/hunterlong",
Icon: "far fa-envelope",
Form: []NotificationForm{{
Form: []notifications.NotificationForm{{
Type: "text",
Title: "SMTP Host",
Placeholder: "Insert your SMTP Host here.",
@ -157,17 +163,7 @@ var Emailer = &email{&Notification{
Placeholder: "",
SmallText: "To Disable TLS/SSL insert 'true'",
DbField: "api_key",
}},
}}
// Send will send the SMTP email with your authentication It accepts type: *emailOutgoing
func (u *email) Send(msg interface{}) error {
email := msg.(*emailOutgoing)
err := u.dialSend(email)
if err != nil {
return err
}
return nil
}}},
}
type emailOutgoing struct {
@ -181,7 +177,7 @@ type emailOutgoing struct {
}
// OnFailure will trigger failing service
func (u *email) OnFailure(s *services.Service, f *failures.Failure) {
func (u *emailer) OnFailure(s *services.Service, f *failures.Failure) error {
email := &emailOutgoing{
To: u.Var2,
Subject: fmt.Sprintf("Service %v is Failing", s.Name),
@ -189,40 +185,24 @@ func (u *email) OnFailure(s *services.Service, f *failures.Failure) {
Data: interface{}(s),
From: u.Var1,
}
u.AddQueue(fmt.Sprintf("service_%v", s.Id), email)
return u.dialSend(email)
}
// OnSuccess will trigger successful service
func (u *email) OnSuccess(s *services.Service) {
if !s.Online || !s.SuccessNotified {
var msg string
msg = s.DownText
u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id))
email := &emailOutgoing{
To: u.Var2,
Subject: msg,
Template: mainEmailTemplate,
Data: interface{}(s),
From: u.Var1,
}
u.AddQueue(fmt.Sprintf("service_%v", s.Id), email)
func (u *emailer) OnSuccess(s *services.Service) error {
msg := s.DownText
email := &emailOutgoing{
To: u.Var2,
Subject: msg,
Template: mainEmailTemplate,
Data: interface{}(s),
From: u.Var1,
}
}
func (u *email) Select() *Notification {
return u.Notification
}
// OnSave triggers when this notifier has been saved
func (u *email) OnSave() error {
utils.Log.Infoln(fmt.Sprintf("Notification %v is receiving updated information.", u.Method))
// Do updating stuff here
return nil
return u.dialSend(email)
}
// OnTest triggers when this notifier has been saved
func (u *email) OnTest() error {
func (u *emailer) OnTest() error {
testService := &services.Service{
Id: 1,
Name: "Example Service",
@ -247,8 +227,8 @@ func (u *email) OnTest() error {
return u.dialSend(email)
}
func (u *email) dialSend(email *emailOutgoing) error {
mailer = mail.NewDialer(Emailer.Host, Emailer.Port, Emailer.Username, Emailer.Password)
func (u *emailer) dialSend(email *emailOutgoing) error {
mailer = mail.NewDialer(u.Host, u.Port, u.Username, u.Password)
emailSource(email)
m := mail.NewMessage()
// if email setting TLS is Disabled

View File

@ -1,203 +0,0 @@
// Statping
// Copyright (C) 2018. Hunter Long and the project contributors
// Written by Hunter Long <info@socialeck.com> and the project contributors
//
// https://github.com/statping/statping
//
// 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 notifiers
import (
"fmt"
"github.com/statping/statping/types/failures"
"github.com/statping/statping/types/services"
"github.com/statping/statping/types/users"
"github.com/statping/statping/utils"
)
// OnSave will trigger a notifier when it has been saved - Notifier interface
func OnSave(method string) {
for _, comm := range allNotifiers {
if utils.IsType(comm, new(Notifier)) {
notifier := comm.Select()
if notifier.Method == method {
comm.OnSave()
}
}
}
}
// OnFailure will be triggered when a service is failing - BasicEvents interface
func OnFailure(s *services.Service, f *failures.Failure) {
if !s.AllowNotifications.Bool {
return
}
// check if User wants to receive every Status Change
if s.UpdateNotify.Bool {
// send only if User hasn't been already notified about the Downtime
if !s.UserNotified {
s.UserNotified = true
goto sendMessages
} else {
return
}
}
sendMessages:
for _, comm := range allNotifiers {
if utils.IsType(comm, new(BasicEvents)) && isEnabled(comm) && (s.Online || inLimits(comm)) {
notifier := comm.(*Notification)
log.
WithField("trigger", "OnFailure").
WithFields(utils.ToFields(notifier, s)).Debugln(fmt.Sprintf("Sending [OnFailure] '%v' notification for service %v", notifier.Method, s.Name))
comm.(BasicEvents).OnFailure(s, f)
comm.Select().Hits.OnFailure++
}
}
}
// OnSuccess will be triggered when a service is successful - BasicEvents interface
func OnSuccess(s *services.Service) {
if !s.AllowNotifications.Bool {
return
}
// check if User wants to receive every Status Change
if s.UpdateNotify.Bool && s.UserNotified {
s.UserNotified = false
}
for _, comm := range allNotifiers {
if utils.IsType(comm, new(BasicEvents)) && isEnabled(comm) && (!s.Online || inLimits(comm)) {
notifier := comm.(*Notification)
log.
WithField("trigger", "OnSuccess").
WithFields(utils.ToFields(notifier, s)).Debugln(fmt.Sprintf("Sending [OnSuccess] '%v' notification for service %v", notifier.Method, s.Name))
comm.(BasicEvents).OnSuccess(s)
comm.Select().Hits.OnSuccess++
}
}
}
// OnNewService is triggered when a new service is created - ServiceEvents interface
func OnNewService(s *services.Service) {
for _, comm := range allNotifiers {
if utils.IsType(comm, new(ServiceEvents)) && isEnabled(comm) && inLimits(comm) {
log.
WithField("trigger", "OnNewService").
Debugln(fmt.Sprintf("Sending new service notification for service %v", s.Name))
comm.(ServiceEvents).OnNewService(s)
comm.Select().Hits.OnNewService++
}
}
}
// OnUpdatedService is triggered when a service is updated - ServiceEvents interface
func OnUpdatedService(s *services.Service) {
if !s.AllowNotifications.Bool {
return
}
for _, comm := range allNotifiers {
if utils.IsType(comm, new(ServiceEvents)) && isEnabled(comm) && inLimits(comm) {
log.Debugln(fmt.Sprintf("Sending updated service notification for service %v", s.Name))
comm.(ServiceEvents).OnUpdatedService(s)
comm.Select().Hits.OnUpdatedService++
}
}
}
// OnDeletedService is triggered when a service is deleted - ServiceEvents interface
func OnDeletedService(s *services.Service) {
if !s.AllowNotifications.Bool {
return
}
for _, comm := range allNotifiers {
if utils.IsType(comm, new(ServiceEvents)) && isEnabled(comm) && inLimits(comm) {
log.Debugln(fmt.Sprintf("Sending deleted service notification for service %v", s.Name))
comm.(ServiceEvents).OnDeletedService(s)
comm.Select().Hits.OnDeletedService++
}
}
}
// OnNewUser is triggered when a new user is created - UserEvents interface
func OnNewUser(u *users.User) {
for _, comm := range allNotifiers {
if utils.IsType(comm, new(UserEvents)) && isEnabled(comm) && inLimits(comm) {
log.Debugln(fmt.Sprintf("Sending new user notification for user %v", u.Username))
comm.(UserEvents).OnNewUser(u)
comm.Select().Hits.OnNewUser++
}
}
}
// OnUpdatedUser is triggered when a new user is updated - UserEvents interface
func OnUpdatedUser(u *users.User) {
for _, comm := range allNotifiers {
if utils.IsType(comm, new(UserEvents)) && isEnabled(comm) && inLimits(comm) {
log.Debugln(fmt.Sprintf("Sending updated user notification for user %v", u.Username))
comm.(UserEvents).OnUpdatedUser(u)
comm.Select().Hits.OnUpdatedUser++
}
}
}
// OnDeletedUser is triggered when a new user is deleted - UserEvents interface
func OnDeletedUser(u *users.User) {
for _, comm := range allNotifiers {
if utils.IsType(comm, new(UserEvents)) && isEnabled(comm) && inLimits(comm) {
log.Debugln(fmt.Sprintf("Sending deleted user notification for user %v", u.Username))
comm.(UserEvents).OnDeletedUser(u)
comm.Select().Hits.OnDeletedUser++
}
}
}
//// OnUpdatedCore is triggered when the CoreApp settings are saved - CoreEvents interface
//func OnUpdatedCore(c *core.Core) {
// for _, comm := range allNotifiers {
// if utils.IsType(comm, new(CoreEvents)) && isEnabled(comm) && inLimits(comm) {
// log.Debugln(fmt.Sprintf("Sending updated core notification"))
// comm.(CoreEvents).OnUpdatedCore(c)
// }
// }
//}
//
//// OnStart is triggered when the Statping service has started
//func OnStart(c *core.Core) {
// for _, comm := range allNotifiers {
// if utils.IsType(comm, new(CoreEvents)) && isEnabled(comm) && inLimits(comm) {
// comm.(CoreEvents).OnUpdatedCore(c)
// }
// }
//}
// OnNewNotifier is triggered when a new notifier is loaded
func OnNewNotifier(n *Notification) {
for _, comm := range allNotifiers {
if utils.IsType(comm, new(NotifierEvents)) && isEnabled(comm) && inLimits(comm) {
comm.(NotifierEvents).OnNewNotifier(n)
comm.Select().Hits.OnNewNotifier++
}
}
}
// OnUpdatedNotifier is triggered when a notifier has been updated
func OnUpdatedNotifier(n *Notification) {
for _, comm := range allNotifiers {
if utils.IsType(comm, new(NotifierEvents)) && isEnabled(comm) && inLimits(comm) {
log.Infoln(fmt.Sprintf("Sending updated notifier for %v", n.Id))
comm.(NotifierEvents).OnUpdatedNotifier(n)
comm.Select().Hits.OnUpdatedNotifier++
}
}
}

View File

@ -1,120 +0,0 @@
// Package notifier contains the main functionality for the Statping Notification system
//
// Example Notifier
//
// Below is an example of a Notifier with multiple Form values to custom your inputs. Place your notifier go file
// into the /notifiers/ directory and follow the example below.
//
// type ExampleNotifier struct {
// *Notification
// }
//
// var example = &ExampleNotifier{&Notification{
// Method: "example",
// Title: "Example Notifier",
// Description: "This is an example of a notifier for Statping!",
// Author: "Hunter Long",
// AuthorUrl: "https://github.com/hunterlong",
// Delay: time.Duration(3 * time.Second),
// Limits: 7,
// Form: []NotificationForm{{
// Type: "text",
// Title: "Host",
// Placeholder: "Insert your Host here.",
// DbField: "host",
// SmallText: "this is where you would put the host",
// }, {
// Type: "text",
// Title: "Username",
// Placeholder: "Insert your Username here.",
// DbField: "username",
// }, {
// Type: "password",
// Title: "Password",
// Placeholder: "Insert your Password here.",
// DbField: "password",
// }, {
// Type: "number",
// Title: "Port",
// Placeholder: "Insert your Port here.",
// DbField: "port",
// }, {
// Type: "text",
// Title: "API Key",
// Placeholder: "Insert your API Key here",
// DbField: "api_key",
// }, {
// Type: "text",
// Title: "API Secret",
// Placeholder: "Insert your API Secret here",
// DbField: "api_secret",
// }, {
// Type: "text",
// Title: "Var 1",
// Placeholder: "Insert your Var1 here",
// DbField: "var1",
// }, {
// Type: "text",
// Title: "Var2",
// Placeholder: "Var2 goes here",
// DbField: "var2",
// }},
// }}
//
// Load the Notifier
//
// Include the init() function with AddNotifier and your notification struct. This is ran on start of Statping
// and will automatically create a new row in the database so the end user can save their own values.
//
// func init() {
// AddNotifier(example)
// }
//
// Required Methods for Notifier Interface
//
// Below are the required methods to have your notifier implement the Notifier interface. The Send method
// will be where you would include the logic for your notification.
//
// // REQUIRED
// func (n *ExampleNotifier) Send(msg interface{}) error {
// message := msg.(string)
// fmt.Printf("i received this string: %v\n", message)
// return nil
// }
//
// // REQUIRED
// func (n *ExampleNotifier) Select() *Notification {
// return n.Notification
// }
//
// // REQUIRED
// func (n *ExampleNotifier) OnSave() error {
// msg := fmt.Sprintf("received on save trigger")
// n.AddQueue(msg)
// return errors.New("onsave triggered")
// }
//
// Basic Events for Notifier
//
// You must include OnSuccess and OnFailure methods for your notifier. Anytime a service is online or offline
// these methods will be ran with the service corresponding to it.
//
// // REQUIRED - BASIC EVENT
// func (n *ExampleNotifier) OnSuccess(s *services.Service) {
// msg := fmt.Sprintf("received a count trigger for service: %v\n", s.Name)
// n.AddQueue(msg)
// }
//
// // REQUIRED - BASIC EVENT
// func (n *ExampleNotifier) OnFailure(s *services.Service, f *types.Failure) {
// msg := fmt.Sprintf("received a failure trigger for service: %v\n", s.Name)
// n.AddQueue(msg)
// }
//
// Additional Events
//
// You can implement your notifier to different types of events that are triggered. Checkout the wiki to
// see more details and examples of how to build your own notifier.
//
// More info on: https://github.com/statping/statping/wiki/Notifiers
package _interface

View File

@ -1,72 +0,0 @@
// Statping
// Copyright (C) 2018. Hunter Long and the project contributors
// Written by Hunter Long <info@socialeck.com> and the project contributors
//
// https://github.com/statping/statping
//
// 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 notifiers
import (
"github.com/statping/statping/types/failures"
"github.com/statping/statping/types/services"
"github.com/statping/statping/types/users"
)
// Notifier interface is required to create a new Notifier
type Notifier interface {
OnSave() error // OnSave is triggered when the notifier is saved
Send(interface{}) error // OnSave is triggered when the notifier is saved
Select() *Notification // Select returns the *Notification for a notifier
}
// BasicEvents includes the most minimal events, failing and successful service triggers
type BasicEvents interface {
OnSuccess(*services.Service) // OnSuccess is triggered when a service is successful
OnFailure(*services.Service, *failures.Failure) // OnFailure is triggered when a service is failing
}
// Tester interface will include a function to Test users settings before saving
type Tester interface {
OnTest() error
}
// ServiceEvents are events for Services
type ServiceEvents interface {
OnNewService(*services.Service)
OnUpdatedService(*services.Service)
OnDeletedService(*services.Service)
}
// UserEvents are events for Users
type UserEvents interface {
OnNewUser(*users.User)
OnUpdatedUser(*users.User)
OnDeletedUser(*users.User)
}
// CoreEvents are events for the main Core app
//type CoreEvents interface {
// OnUpdatedCore(*core.Core)
// OnStart(*core.Core)
//}
// NotifierEvents are events for other Notifiers
type NotifierEvents interface {
OnNewNotifier(*Notification)
OnUpdatedNotifier(*Notification)
}
// HTTPRouter interface will allow your notifier to accept http GET/POST requests
type HTTPRouter interface {
OnGET() error
OnPOST() error
}

View File

@ -18,6 +18,8 @@ package notifiers
import (
"fmt"
"github.com/statping/statping/types/failures"
"github.com/statping/statping/types/notifications"
"github.com/statping/statping/types/notifier"
"github.com/statping/statping/types/services"
"github.com/statping/statping/utils"
"net/url"
@ -25,24 +27,28 @@ import (
"time"
)
var _ Notifier = (*lineNotifier)(nil)
var _ notifier.Notifier = (*lineNotifier)(nil)
const (
lineNotifyMethod = "line_notify"
)
type lineNotifier struct {
*Notification
*notifications.Notification
}
var LineNotify = &lineNotifier{&Notification{
func (l *lineNotifier) Select() *notifications.Notification {
return l.Notification
}
var LineNotify = &lineNotifier{&notifications.Notification{
Method: lineNotifyMethod,
Title: "LINE Notify",
Description: "LINE Notify will send notifications to your LINE Notify account when services are offline or online. Based on the <a href=\"https://notify-bot.line.me/doc/en/\">LINE Notify API</a>.",
Author: "Kanin Peanviriyakulkit",
AuthorUrl: "https://github.com/dogrocker",
Icon: "far fa-bell",
Form: []NotificationForm{{
Form: []notifications.NotificationForm{{
Type: "text",
Title: "Access Token",
Placeholder: "Insert your Line Notify Access Token here.",
@ -51,8 +57,7 @@ var LineNotify = &lineNotifier{&Notification{
}
// Send will send a HTTP Post with the Authorization to the notify-api.line.me server. It accepts type: string
func (u *lineNotifier) Send(msg interface{}) error {
message := msg.(string)
func (u *lineNotifier) sendMessage(message string) error {
v := url.Values{}
v.Set("message", message)
headers := []string{fmt.Sprintf("Authorization=Bearer %v", u.ApiSecret)}
@ -60,31 +65,20 @@ func (u *lineNotifier) Send(msg interface{}) error {
return err
}
func (u *lineNotifier) Select() *Notification {
return u.Notification
}
// OnFailure will trigger failing service
func (u *lineNotifier) OnFailure(s *services.Service, f *failures.Failure) {
func (u *lineNotifier) OnFailure(s *services.Service, f *failures.Failure) error {
msg := fmt.Sprintf("Your service '%v' is currently offline!", s.Name)
u.AddQueue(fmt.Sprintf("service_%v", s.Id), msg)
return u.sendMessage(msg)
}
// OnSuccess will trigger successful service
func (u *lineNotifier) OnSuccess(s *services.Service) {
if !s.Online || !s.SuccessNotified {
var msg string
msg = s.DownText
u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id))
u.AddQueue(fmt.Sprintf("service_%v", s.Id), msg)
}
func (u *lineNotifier) OnSuccess(s *services.Service) error {
msg := fmt.Sprintf("Service %s is online!", s.Name)
return u.sendMessage(msg)
}
// OnSave triggers when this notifier has been saved
func (u *lineNotifier) OnSave() error {
msg := fmt.Sprintf("Notification %v is receiving updated information.", u.Method)
utils.Log.Infoln(msg)
u.AddQueue("saved", msg)
return nil
// OnTest triggers when this notifier has been saved
func (u *lineNotifier) OnTest() error {
msg := fmt.Sprintf("Testing if Line Notifier is working!")
return u.sendMessage(msg)
}

View File

@ -20,20 +20,26 @@ import (
"encoding/json"
"fmt"
"github.com/statping/statping/types/failures"
"github.com/statping/statping/types/notifications"
"github.com/statping/statping/types/notifier"
"github.com/statping/statping/types/services"
"github.com/statping/statping/utils"
"time"
)
var _ Notifier = (*mobilePush)(nil)
var _ notifier.Notifier = (*mobilePush)(nil)
const mobileIdentifier = "com.statping"
type mobilePush struct {
*Notification
*notifications.Notification
}
var Mobile = &mobilePush{&Notification{
func (m *mobilePush) Select() *notifications.Notification {
return m.Notification
}
var Mobile = &mobilePush{&notifications.Notification{
Method: "mobile",
Title: "Mobile Notifications",
Description: `Receive push notifications on your Mobile device using the Statping App. You can scan the Authentication QR Code found in Settings to get the Mobile app setup in seconds.
@ -42,7 +48,7 @@ var Mobile = &mobilePush{&Notification{
AuthorUrl: "https://github.com/hunterlong",
Delay: time.Duration(5 * time.Second),
Icon: "fas fa-mobile-alt",
Form: []NotificationForm{{
Form: []notifications.NotificationForm{{
Type: "text",
Title: "Device Identifiers",
Placeholder: "A list of your Mobile device push notification ID's.",
@ -57,10 +63,6 @@ var Mobile = &mobilePush{&Notification{
}}},
}
func (u *mobilePush) Select() *Notification {
return u.Notification
}
func dataJson(s *services.Service, f *failures.Failure) map[string]interface{} {
serviceId := "0"
if s != nil {
@ -85,7 +87,7 @@ func dataJson(s *services.Service, f *failures.Failure) map[string]interface{} {
}
// OnFailure will trigger failing service
func (u *mobilePush) OnFailure(s *services.Service, f *failures.Failure) {
func (u *mobilePush) OnFailure(s *services.Service, f *failures.Failure) error {
data := dataJson(s, f)
msg := &pushArray{
Message: fmt.Sprintf("Your service '%v' is currently failing! Reason: %v", s.Name, f.Issue),
@ -93,30 +95,19 @@ func (u *mobilePush) OnFailure(s *services.Service, f *failures.Failure) {
Topic: mobileIdentifier,
Data: data,
}
u.AddQueue(fmt.Sprintf("service_%v", s.Id), msg)
return u.Send(msg)
}
// OnSuccess will trigger successful service
func (u *mobilePush) OnSuccess(s *services.Service) {
func (u *mobilePush) OnSuccess(s *services.Service) error {
data := dataJson(s, nil)
if !s.Online || !s.SuccessNotified {
var msgStr string
msgStr = s.DownText
u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id))
msg := &pushArray{
Message: msgStr,
Title: "Service Online",
Topic: mobileIdentifier,
Data: data,
}
u.AddQueue(fmt.Sprintf("service_%v", s.Id), msg)
msg := &pushArray{
Message: "Service is Online!",
Title: "Service Online",
Topic: mobileIdentifier,
Data: data,
}
}
// OnSave triggers when this notifier has been saved
func (u *mobilePush) OnSave() error {
return nil
return u.Send(msg)
}
// OnTest triggers when this notifier has been saved
@ -143,12 +134,10 @@ func (u *mobilePush) OnTest() error {
firstLog := output.Logs[0].Error
return fmt.Errorf("Mobile Notification error: %v", firstLog)
}
return err
}
// Send will send message to Statping push notifications endpoint
func (u *mobilePush) Send(msg interface{}) error {
pushMessage := msg.(*pushArray)
func (u *mobilePush) Send(pushMessage *pushArray) error {
pushMessage.Tokens = []string{u.Var1}
pushMessage.Platform = utils.ToInt(u.Var2)
_, err := pushRequest(pushMessage)

View File

@ -1,135 +0,0 @@
package notifiers
import (
"github.com/statping/statping/database"
"github.com/statping/statping/types/null"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"testing"
)
var form1 = NotificationForm{
Type: "text",
Title: "Example Input",
DbField: "Host",
Required: true,
IsHidden: false,
IsList: false,
IsSwitch: false,
}
var form2 = NotificationForm{
Type: "text",
Title: "Example Input 2",
DbField: "ApiKey",
Required: true,
IsHidden: false,
IsList: false,
IsSwitch: false,
}
var example = &exampleNotif{&Notification{
Method: "example",
Enabled: null.NewNullBool(true),
Limits: 3,
Removable: false,
Form: []NotificationForm{form1, form2},
Delay: 30,
}}
type exampleNotif struct {
*Notification
}
func (e *exampleNotif) OnSave() error {
return nil
}
func (e *exampleNotif) Select() *Notification {
return e.Notification
}
func (e *exampleNotif) Send(data interface{}) error {
return nil
}
func TestInit(t *testing.T) {
db, err := database.OpenTester()
require.Nil(t, err)
db.CreateTable(&Notification{})
db.Create(example.Select())
SetDB(db)
}
func TestFind(t *testing.T) {
appendList(example)
itemer, err := Find(example.Method)
require.Nil(t, err)
item := itemer.Select()
require.NotNil(t, item)
assert.Equal(t, "example", item.Method)
assert.Len(t, allNotifiers, 1)
}
func TestAll(t *testing.T) {
items := All()
assert.Len(t, items, 1)
assert.Len(t, allNotifiers, 1)
}
func TestCreate(t *testing.T) {
assert.Len(t, allNotifiers, 1)
example := &Notification{
Method: "anotherexample",
Title: "Example 2",
Description: "New Message here",
}
err := example.Create()
require.Nil(t, err)
assert.NotZero(t, example.Id)
assert.Equal(t, "anotherexample", example.Method)
assert.Equal(t, "Example 2", example.Title)
assert.NotZero(t, example.CreatedAt)
items := All()
assert.Len(t, items, 2)
assert.Len(t, allNotifiers, 2)
}
func TestUpdate(t *testing.T) {
itemer, err := Find("anotherexample")
require.Nil(t, err)
require.NotNil(t, itemer)
item := itemer.Select()
require.NotNil(t, item)
item.Host = "Updated Host Var"
err = item.Update()
require.Nil(t, err)
assert.Equal(t, "Updated Host Var", item.Host)
}
func TestDelete(t *testing.T) {
all := All()
assert.Len(t, all, 2)
itemer, err := Find("example2")
require.Nil(t, err)
item := itemer.Select()
require.NotNil(t, item)
err = item.Delete()
require.Nil(t, err)
all = All()
assert.Len(t, all, 2)
}
func TestClose(t *testing.T) {
assert.Nil(t, db.Close())
}

View File

@ -2,105 +2,28 @@ package notifiers
import (
"fmt"
"github.com/statping/statping/types/failures"
"github.com/statping/statping/types/services"
"github.com/statping/statping/utils"
"strings"
)
var (
allowedVars = []string{"host", "username", "password", "port", "api_key", "api_secret", "var1", "var2"}
)
func SendEvent(data ...interface{}) {
d1 := data[0]
service, ok := d1.(*services.Service)
if !ok {
return
}
d2 := data[1]
if d2 == nil {
OnSuccess(service)
}
fail, ok := d2.(*failures.Failure)
if !ok {
return
}
OnFailure(service, fail)
}
func checkNotifierForm(n *Notification) error {
for _, f := range n.Form {
contains := contains(f.DbField, allowedVars)
if !contains {
return fmt.Errorf("the DbField '%v' is not allowed, allowed vars: %v", f.DbField, allowedVars)
}
}
return nil
}
func contains(s string, arr []string) bool {
for _, v := range arr {
if strings.ToLower(s) == v {
return true
}
}
return false
}
// AddNotifier accept a Notifier interface to be added into the array
func AddNotifiers(notifiers ...Notifier) error {
log.Infof("Initiating %d Notifiers\n", len(notifiers))
for _, n := range notifiers {
notif := n.Select()
log.Infof("Initiating %s Notifier\n", notif.Method)
if err := checkNotifierForm(notif); err != nil {
log.Errorf(err.Error())
return err
}
log.Infof("Creating %s Notifier\n", notif.Method)
if err := notif.Create(); err != nil {
return err
}
if notif.Enabled.Bool {
notif.Close()
notif.Start()
go Queue(notif)
}
}
return nil
}
// startAllNotifiers will start the go routine for each loaded notifier
func startAllNotifiers() {
for _, notify := range All() {
n := notify.Select()
log.Infof("Initiating %s Notifier\n", n.Method)
if utils.IsType(notify, new(Notifier)) {
if n.Enabled.Bool {
n.Close()
n.Start()
go Queue(notify)
}
}
}
}
func Migrate() error {
return AddNotifiers(
func InitNotifiers() {
Add(
slacker,
Command,
Discorder,
Emailer,
email,
LineNotify,
Mobile,
Slacker,
Telegram,
Twilio,
Webhook,
Mobile,
)
}
func Add(notifs ...services.ServiceNotifier) {
for _, n := range notifs {
services.AddNotifier(n)
if err := n.Select().Create(); err != nil {
fmt.Println(err)
}
}
}

View File

@ -20,6 +20,8 @@ import (
"errors"
"fmt"
"github.com/statping/statping/types/failures"
"github.com/statping/statping/types/notifications"
"github.com/statping/statping/types/notifier"
"github.com/statping/statping/types/services"
"github.com/statping/statping/utils"
"strings"
@ -27,7 +29,7 @@ import (
"time"
)
var _ Notifier = (*slack)(nil)
var _ notifier.Notifier = (*slack)(nil)
const (
slackMethod = "slack"
@ -37,10 +39,14 @@ const (
)
type slack struct {
*Notification
*notifications.Notification
}
var Slacker = &slack{&Notification{
func (s *slack) Select() *notifications.Notification {
return s.Notification
}
var slacker = &slack{&notifications.Notification{
Method: slackMethod,
Title: "slack",
Description: "Send notifications to your slack channel when a service is offline. Insert your Incoming webhooker URL for your channel to receive notifications. Based on the <a href=\"https://api.slack.com/incoming-webhooks\">slack API</a>.",
@ -49,7 +55,7 @@ var Slacker = &slack{&Notification{
Delay: time.Duration(10 * time.Second),
Host: "https://webhooksurl.slack.com/***",
Icon: "fab fa-slack",
Form: []NotificationForm{{
Form: []notifications.NotificationForm{{
Type: "text",
Title: "Incoming webhooker Url",
Placeholder: "Insert your slack Webhook URL here.",
@ -59,15 +65,14 @@ var Slacker = &slack{&Notification{
}}},
}
func parseSlackMessage(id int64, temp string, data interface{}) error {
func parseSlackMessage(id int64, temp string, data interface{}) string {
buf := new(bytes.Buffer)
slackTemp, _ := template.New("slack").Parse(temp)
err := slackTemp.Execute(buf, data)
if err != nil {
return err
return err.Error()
}
Slacker.AddQueue(fmt.Sprintf("service_%v", id), buf.String())
return nil
return buf.String()
}
type slackMessage struct {
@ -78,51 +83,44 @@ type slackMessage struct {
}
// Send will send a HTTP Post to the slack webhooker API. It accepts type: string
func (u *slack) Send(msg interface{}) error {
message := msg.(string)
_, _, err := utils.HttpRequest(u.Host, "POST", "application/json", nil, strings.NewReader(message), time.Duration(10*time.Second), true)
func (u *slack) sendSlack(msg string) error {
contents, resp, err := utils.HttpRequest(u.Host, "POST", "application/json", nil, strings.NewReader(msg), time.Duration(10*time.Second), true)
defer resp.Body.Close()
fmt.Println("CONTENTS: ", string(contents))
return err
}
func (u *slack) Select() *Notification {
return u.Notification
}
func (u *slack) OnTest() error {
contents, _, err := utils.HttpRequest(u.Host, "POST", "application/json", nil, bytes.NewBuffer([]byte(`{"text":"testing message"}`)), time.Duration(10*time.Second), true)
contents, resp, err := utils.HttpRequest(u.Host, "POST", "application/json", nil, bytes.NewBuffer([]byte(`{"text":"testing message"}`)), time.Duration(10*time.Second), true)
defer resp.Body.Close()
if string(contents) != "ok" {
return errors.New("The slack response was incorrect, check the URL")
return errors.New("the slack response was incorrect, check the URL")
}
return err
}
// OnFailure will trigger failing service
func (u *slack) OnFailure(s *services.Service, f *failures.Failure) {
func (u *slack) OnFailure(s *services.Service, f *failures.Failure) error {
message := slackMessage{
Service: s,
Template: failingTemplate,
Time: utils.Now().Unix(),
Issue: f.Issue,
}
parseSlackMessage(s.Id, failingTemplate, message)
msg := parseSlackMessage(s.Id, failingTemplate, message)
return u.sendSlack(msg)
}
// OnSuccess will trigger successful service
func (u *slack) OnSuccess(s *services.Service) {
if !s.Online {
u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id))
message := slackMessage{
Service: s,
Template: successTemplate,
Time: utils.Now().Unix(),
}
parseSlackMessage(s.Id, successTemplate, message)
func (u *slack) OnSuccess(s *services.Service) error {
message := slackMessage{
Service: s,
Template: successTemplate,
Time: utils.Now().Unix(),
}
}
msg := parseSlackMessage(s.Id, successTemplate, message)
// OnSave triggers when this notifier has been saved
func (u *slack) OnSave() error {
message := fmt.Sprintf("Notification %v is receiving updated information.", u.Method)
u.AddQueue("saved", message)
return nil
fmt.Println("Sending OnSuccess message!")
fmt.Println(msg)
fmt.Printf("%s\n", u.Host)
return u.sendSlack(msg)
}

View File

@ -1,273 +0,0 @@
// Statping
// Copyright (C) 2018. Hunter Long and the project contributors
// Written by Hunter Long <info@socialeck.com> and the project contributors
//
// https://github.com/statping/statping
//
// 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 notifiers
import (
"encoding/json"
"errors"
"fmt"
"github.com/statping/statping/types/null"
"github.com/statping/statping/types/services"
"github.com/statping/statping/utils"
"reflect"
"time"
)
var (
// db holds the Statping database connection
log = utils.Log.WithField("type", "notifier")
allNotifiers []Notifier
)
// Notification contains all the fields for a Statping Notifier.
type Notification struct {
Id int64 `gorm:"primary_key;column:id" json:"id"`
Method string `gorm:"column:method" json:"method"`
Host string `gorm:"not null;column:host" json:"host,omitempty"`
Port int `gorm:"not null;column:port" json:"port,omitempty"`
Username string `gorm:"not null;column:username" json:"username,omitempty"`
Password string `gorm:"not null;column:password" json:"password,omitempty"`
Var1 string `gorm:"not null;column:var1" json:"var1,omitempty"`
Var2 string `gorm:"not null;column:var2" json:"var2,omitempty"`
ApiKey string `gorm:"not null;column:api_key" json:"api_key,omitempty"`
ApiSecret string `gorm:"not null;column:api_secret" json:"api_secret,omitempty"`
Enabled null.NullBool `gorm:"column:enabled;type:boolean;default:false" json:"enabled"`
Limits int `gorm:"not null;column:limits" json:"limits"`
Removable bool `gorm:"column:removable" json:"removeable"`
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
Form []NotificationForm `gorm:"-" json:"form"`
logs []*NotificationLog `gorm:"-" json:"logs"`
Title string `gorm:"-" json:"title"`
Description string `gorm:"-" json:"description"`
Author string `gorm:"-" json:"author"`
AuthorUrl string `gorm:"-" json:"author_url"`
Icon string `gorm:"-" json:"icon"`
Delay time.Duration `gorm:"-" json:"delay,string"`
Queue []*QueueData `gorm:"-" json:"-"`
Running chan bool `gorm:"-" json:"-"`
testable bool `gorm:"-" json:"testable"`
Hits notificationHits
Notifier
}
type notificationHits struct {
OnSuccess int64 `gorm:"-" json:"-"`
OnFailure int64 `gorm:"-" json:"-"`
OnSave int64 `gorm:"-" json:"-"`
OnNewService int64 `gorm:"-" json:"-"`
OnUpdatedService int64 `gorm:"-" json:"-"`
OnDeletedService int64 `gorm:"-" json:"-"`
OnNewUser int64 `gorm:"-" json:"-"`
OnUpdatedUser int64 `gorm:"-" json:"-"`
OnDeletedUser int64 `gorm:"-" json:"-"`
OnNewNotifier int64 `gorm:"-" json:"-"`
OnUpdatedNotifier int64 `gorm:"-" json:"-"`
}
// QueueData is the struct for the messaging queue with service
type QueueData struct {
Id string
Data interface{}
}
// NotificationForm contains the HTML fields for each variable/input you want the notifier to accept.
type NotificationForm struct {
Type string `json:"type"` // the html input type (text, password, email)
Title string `json:"title"` // include a title for ease of use
Placeholder string `json:"placeholder"` // add a placeholder for the input
DbField string `json:"field"` // true variable key for input
SmallText string `json:"small_text"` // insert small text under a html input
Required bool `json:"required"` // require this input on the html form
IsHidden bool `json:"hidden"` // hide this form element from end user
IsList bool `json:"list"` // make this form element a comma separated list
IsSwitch bool `json:"switch"` // make the notifier a boolean true/false switch
}
// NotificationLog contains the normalized message from previously sent notifications
type NotificationLog struct {
Message string `json:"message"`
Time utils.Timestamp `json:"time"`
Timestamp time.Time `json:"timestamp"`
}
// normalizeType will accept multiple interfaces and converts it into a string for logging
func normalizeType(ty interface{}) string {
switch v := ty.(type) {
case int, int32, int64:
return fmt.Sprintf("%v", v)
case float32, float64:
return fmt.Sprintf("%v", v)
case string:
return v
case []byte:
return string(v)
case []string:
return fmt.Sprintf("%v", v)
case interface{}, map[string]interface{}:
j, _ := json.Marshal(v)
return string(j)
default:
return fmt.Sprintf("%v", v)
}
}
// Log will record a new notification into memory and will show the logs on the settings page
func (n *Notification) makeLog(msg interface{}) {
log := &NotificationLog{
Message: normalizeType(msg),
Time: utils.Timestamp(utils.Now()),
Timestamp: utils.Now(),
}
n.logs = append(n.logs, log)
}
// Logs returns an array of the notifiers logs
func (n *Notification) Logs() []*NotificationLog {
return reverseLogs(n.logs)
}
// reverseLogs will reverse the notifier's logs to be time desc
func reverseLogs(input []*NotificationLog) []*NotificationLog {
if len(input) == 0 {
return input
}
return append(reverseLogs(input[1:]), input[0])
}
// SelectNotification returns the Notification struct from the database
func SelectNotification(n Notifier) (*Notification, error) {
notifier := n.Select()
err := db.Where("method = ?", notifier.Method).Find(&notifier)
return notifier, err.Error()
}
// SelectNotifier returns the Notification struct from the database
func SelectNotifier(method string) (*Notification, Notifier, error) {
for _, comm := range allNotifiers {
n, ok := comm.(Notifier)
if !ok {
return nil, nil, fmt.Errorf("incorrect notification type: %v", reflect.TypeOf(n).String())
}
notifier := n.Select()
if notifier.Method == method {
return notifier, comm.(Notifier), nil
}
}
return nil, nil, errors.New("cannot find notifier")
}
// Queue is the FIFO go routine to send notifications when objects are triggered
func Queue(notifer Notifier) {
n := notifer.(*Notification)
rateLimit := n.Delay
CheckNotifier:
for {
select {
case <-n.Running:
break CheckNotifier
case <-time.After(rateLimit):
n := notifer.(*Notification)
fmt.Printf("checking %s %d\n", n.Method, len(n.Queue))
if len(n.Queue) > 0 {
ok, _ := n.WithinLimits()
if ok {
msg := n.Queue[0]
err := notifer.Send(msg.Data)
if err != nil {
log.WithFields(utils.ToFields(n, msg)).Error(fmt.Sprintf("Notifier '%v' had an error: %v", n.Method, err))
} else {
log.WithFields(utils.ToFields(n, msg)).Debug(fmt.Sprintf("Notifier '%v' sent outgoing message (%v) %v left in queue.", n.Method, msg.Id, len(n.Queue)))
}
n.makeLog(msg.Data)
if len(n.Queue) > 1 {
n.Queue = n.Queue[1:]
} else {
n.Queue = nil
}
rateLimit = n.Delay
}
}
}
continue
}
}
// install will check the database for the notification, if its not inserted it will insert a new record for it
//func install(n Notifier) error {
// log.WithFields(utils.ToFields(n)).
// Debugln(fmt.Sprintf("Checking if notifier '%v' is installed", n.Select().Method))
//
// if Exists(n.Select().Method) {
// AllCommunications = append(AllCommunications, n)
// } else {
// _, err := insertDatabase(n)
// if err != nil {
// log.Errorln(err)
// return err
// }
// AllCommunications = append(AllCommunications, n)
// }
// return nil
//}
// isEnabled returns true if the notifier is enabled
func isEnabled(n interface{}) bool {
notifier := n.(Notifier).Select()
return notifier.Enabled.Bool
}
// inLimits will return true if the notifier is within sending limits
func inLimits(n interface{}) bool {
notifier := n.(Notifier).Select()
ok, _ := notifier.WithinLimits()
return ok
}
// WithinLimits returns true if the notifier is within its sending limits
func (n *Notification) WithinLimits() (bool, error) {
if n.SentLastMinute() == 0 {
return true, nil
}
if n.SentLastMinute() >= n.Limits {
return false, fmt.Errorf("notifier sent %v out of %v in last minute", n.SentLastMinute(), n.Limits)
}
if n.LastSent().Seconds() == 0 {
return true, nil
}
if n.Delay.Seconds() >= n.LastSent().Seconds() {
return false, fmt.Errorf("notifiers delay (%v) is greater than last message sent (%v)", n.Delay.Seconds(), n.LastSent().Seconds())
}
return true, nil
}
// ExampleService can be used for the OnTest() method for notifiers
var ExampleService = &services.Service{
Id: 1,
Name: "Interpol - All The Rage Back Home",
Domain: "https://www.youtube.com/watch?v=-u6DvRyyKGU",
ExpectedStatus: 200,
Interval: 30,
Type: "http",
Method: "GET",
Timeout: 20,
LastStatusCode: 404,
Expected: null.NewNullString("test example"),
LastResponse: "<html>this is an example response</html>",
CreatedAt: utils.Now().Add(-24 * time.Hour),
}

View File

@ -20,6 +20,8 @@ import (
"errors"
"fmt"
"github.com/statping/statping/types/failures"
"github.com/statping/statping/types/notifications"
"github.com/statping/statping/types/notifier"
"github.com/statping/statping/types/services"
"github.com/statping/statping/utils"
"net/url"
@ -27,13 +29,17 @@ import (
"time"
)
var _ Notifier = (*telegram)(nil)
var _ notifier.Notifier = (*telegram)(nil)
type telegram struct {
*Notification
*notifications.Notification
}
var Telegram = &telegram{&Notification{
func (t *telegram) Select() *notifications.Notification {
return t.Notification
}
var Telegram = &telegram{&notifications.Notification{
Method: "telegram",
Title: "Telegram",
Description: "Receive notifications on your Telegram channel when a service has an issue. You must get a Telegram API token from the /botfather. Review the <a target=\"_blank\" href=\"http://techthoughts.info/how-to-create-a-telegram-bot-and-send-messages-via-api\">Telegram API Tutorial</a> to learn how to generate a new API Token.",
@ -41,7 +47,7 @@ var Telegram = &telegram{&Notification{
AuthorUrl: "https://github.com/hunterlong",
Icon: "fab fa-telegram-plane",
Delay: time.Duration(5 * time.Second),
Form: []NotificationForm{{
Form: []notifications.NotificationForm{{
Type: "text",
Title: "Telegram API Token",
Placeholder: "383810182:EEx829dtCeufeQYXG7CUdiQopqdmmxBPO7-s",
@ -58,13 +64,8 @@ var Telegram = &telegram{&Notification{
}}},
}
func (u *telegram) Select() *Notification {
return u.Notification
}
// Send will send a HTTP Post to the Telegram API. It accepts type: string
func (u *telegram) Send(msg interface{}) error {
message := msg.(string)
func (u *telegram) sendMessage(message string) error {
apiEndpoint := fmt.Sprintf("https://api.telegram.org/bot%v/sendMessage", u.ApiSecret)
v := url.Values{}
@ -84,35 +85,21 @@ func (u *telegram) Send(msg interface{}) error {
}
// OnFailure will trigger failing service
func (u *telegram) OnFailure(s *services.Service, f *failures.Failure) {
func (u *telegram) OnFailure(s *services.Service, f *failures.Failure) error {
msg := fmt.Sprintf("Your service '%v' is currently offline!", s.Name)
u.AddQueue(fmt.Sprintf("service_%v", s.Id), msg)
return u.sendMessage(msg)
}
// OnSuccess will trigger successful service
func (u *telegram) OnSuccess(s *services.Service) {
if !s.Online || !s.SuccessNotified {
u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id))
var msg interface{}
msg = s.DownText
u.AddQueue(fmt.Sprintf("service_%v", s.Id), msg)
}
}
// OnSave triggers when this notifier has been saved
func (u *telegram) OnSave() error {
utils.Log.Infoln(fmt.Sprintf("Notification %v is receiving updated information.", u.Method))
// Do updating stuff here
return nil
func (u *telegram) OnSuccess(s *services.Service) error {
msg := fmt.Sprintf("Your service '%v' is currently online!", s.Name)
return u.sendMessage(msg)
}
// OnTest will test the Twilio SMS messaging
func (u *telegram) OnTest() error {
msg := fmt.Sprintf("Testing the Twilio SMS Notifier on your Statping server")
return u.Send(msg)
return u.sendMessage(msg)
}
func telegramSuccess(res []byte) (bool, telegramResponse) {

View File

@ -20,6 +20,8 @@ import (
"errors"
"fmt"
"github.com/statping/statping/types/failures"
"github.com/statping/statping/types/notifications"
"github.com/statping/statping/types/notifier"
"github.com/statping/statping/types/services"
"github.com/statping/statping/utils"
"net/url"
@ -27,13 +29,17 @@ import (
"time"
)
var _ Notifier = (*twilio)(nil)
var _ notifier.Notifier = (*twilio)(nil)
type twilio struct {
*Notification
*notifications.Notification
}
var Twilio = &twilio{&Notification{
func (t *twilio) Select() *notifications.Notification {
return t.Notification
}
var Twilio = &twilio{&notifications.Notification{
Method: "twilio",
Title: "Twilio",
Description: "Receive SMS text messages directly to your cellphone when a service is offline. You can use a Twilio test account with limits. This notifier uses the <a href=\"https://www.twilio.com/docs/usage/api\">Twilio API</a>.",
@ -41,7 +47,7 @@ var Twilio = &twilio{&Notification{
AuthorUrl: "https://github.com/hunterlong",
Icon: "far fa-comment-alt",
Delay: time.Duration(10 * time.Second),
Form: []NotificationForm{{
Form: []notifications.NotificationForm{{
Type: "text",
Title: "Account SID",
Placeholder: "Insert your Twilio Account SID",
@ -68,13 +74,8 @@ var Twilio = &twilio{&Notification{
}}},
}
func (u *twilio) Select() *Notification {
return u.Notification
}
// Send will send a HTTP Post to the Twilio SMS API. It accepts type: string
func (u *twilio) Send(msg interface{}) error {
message := msg.(string)
func (u *twilio) sendMessage(message string) error {
twilioUrl := fmt.Sprintf("https://api.twilio.com/2010-04-01/Accounts/%v/Messages.json", u.GetValue("api_key"))
v := url.Values{}
@ -94,35 +95,21 @@ func (u *twilio) Send(msg interface{}) error {
}
// OnFailure will trigger failing service
func (u *twilio) OnFailure(s *services.Service, f *failures.Failure) {
func (u *twilio) OnFailure(s *services.Service, f *failures.Failure) error {
msg := fmt.Sprintf("Your service '%v' is currently offline!", s.Name)
u.AddQueue(fmt.Sprintf("service_%v", s.Id), msg)
return u.sendMessage(msg)
}
// OnSuccess will trigger successful service
func (u *twilio) OnSuccess(s *services.Service) {
if !s.Online || !s.SuccessNotified {
u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id))
var msg string
msg = s.DownText
u.AddQueue(fmt.Sprintf("service_%v", s.Id), msg)
}
}
// OnSave triggers when this notifier has been saved
func (u *twilio) OnSave() error {
utils.Log.Infoln(fmt.Sprintf("Notification %v is receiving updated information.", u.Method))
// Do updating stuff here
return nil
func (u *twilio) OnSuccess(s *services.Service) error {
msg := fmt.Sprintf("Your service '%v' is currently online!", s.Name)
return u.sendMessage(msg)
}
// OnTest will test the Twilio SMS messaging
func (u *twilio) OnTest() error {
msg := fmt.Sprintf("Testing the Twilio SMS Notifier")
return u.Send(msg)
return u.sendMessage(msg)
}
func twilioSuccess(res []byte) (bool, twilioResponse) {

View File

@ -19,6 +19,8 @@ import (
"bytes"
"fmt"
"github.com/statping/statping/types/failures"
"github.com/statping/statping/types/notifications"
"github.com/statping/statping/types/notifier"
"github.com/statping/statping/types/services"
"github.com/statping/statping/utils"
"io/ioutil"
@ -27,17 +29,17 @@ import (
"time"
)
var _ Notifier = (*webhooker)(nil)
var _ notifier.Notifier = (*webhooker)(nil)
const (
webhookMethod = "webhook"
)
type webhooker struct {
*Notification
*notifications.Notification
}
var Webhook = &webhooker{&Notification{
var Webhook = &webhooker{&notifications.Notification{
Method: webhookMethod,
Title: "HTTP webhooker",
Description: "Send a custom HTTP request to a specific URL with your own body, headers, and parameters.",
@ -45,7 +47,7 @@ var Webhook = &webhooker{&Notification{
AuthorUrl: "https://github.com/hunterlong",
Icon: "fas fa-code-branch",
Delay: time.Duration(1 * time.Second),
Form: []NotificationForm{{
Form: []notifications.NotificationForm{{
Type: "text",
Title: "HTTP Endpoint",
Placeholder: "http://webhookurl.com/JW2MCP4SKQP",
@ -89,7 +91,7 @@ func (w *webhooker) Send(msg interface{}) error {
return err
}
func (w *webhooker) Select() *Notification {
func (w *webhooker) Select() *notifications.Notification {
return w.Notification
}
@ -131,7 +133,7 @@ func (w *webhooker) sendHttpWebhook(body string) (*http.Response, error) {
}
func (w *webhooker) OnTest() error {
body := replaceBodyText(w.Var2, ExampleService, nil)
body := replaceBodyText(w.Var2, nil, nil)
resp, err := w.sendHttpWebhook(body)
if err != nil {
return err
@ -143,21 +145,17 @@ func (w *webhooker) OnTest() error {
}
// OnFailure will trigger failing service
func (w *webhooker) OnFailure(s *services.Service, f *failures.Failure) {
func (w *webhooker) OnFailure(s *services.Service, f *failures.Failure) error {
msg := replaceBodyText(w.Var2, s, f)
w.AddQueue(fmt.Sprintf("service_%v", s.Id), msg)
resp, err := w.sendHttpWebhook(msg)
defer resp.Body.Close()
return err
}
// OnSuccess will trigger successful service
func (w *webhooker) OnSuccess(s *services.Service) {
if !s.Online {
w.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id))
msg := replaceBodyText(w.Var2, s, nil)
w.AddQueue(fmt.Sprintf("service_%v", s.Id), msg)
}
}
// OnSave triggers when this notifier has been saved
func (w *webhooker) OnSave() error {
return nil
func (w *webhooker) OnSuccess(s *services.Service) error {
msg := replaceBodyText(w.Var2, s, nil)
resp, err := w.sendHttpWebhook(msg)
defer resp.Body.Close()
return err
}

View File

@ -5,7 +5,6 @@ import (
"github.com/jinzhu/gorm"
"github.com/pkg/errors"
"github.com/statping/statping/database"
"github.com/statping/statping/notifiers"
"github.com/statping/statping/types/checkins"
"github.com/statping/statping/types/core"
"github.com/statping/statping/types/failures"
@ -13,6 +12,7 @@ import (
"github.com/statping/statping/types/hits"
"github.com/statping/statping/types/incidents"
"github.com/statping/statping/types/messages"
"github.com/statping/statping/types/notifications"
"github.com/statping/statping/types/null"
"github.com/statping/statping/types/services"
"github.com/statping/statping/types/users"
@ -100,7 +100,7 @@ func initModels(db database.Database) {
hits.SetDB(db)
failures.SetDB(db)
checkins.SetDB(db)
notifiers.SetDB(db)
notifications.SetDB(db)
incidents.SetDB(db)
users.SetDB(db)
messages.SetDB(db)

View File

@ -2,7 +2,6 @@ package configs
import (
"github.com/statping/statping/database"
"github.com/statping/statping/notifiers"
"github.com/statping/statping/types/checkins"
"github.com/statping/statping/types/core"
"github.com/statping/statping/types/failures"
@ -10,6 +9,7 @@ import (
"github.com/statping/statping/types/hits"
"github.com/statping/statping/types/incidents"
"github.com/statping/statping/types/messages"
"github.com/statping/statping/types/notifications"
"github.com/statping/statping/types/services"
"github.com/statping/statping/types/users"
"github.com/statping/statping/utils"
@ -73,7 +73,7 @@ func (d *DbConfig) Delete() error {
// DropDatabase will DROP each table Statping created
func (d *DbConfig) DropDatabase() error {
var DbModels = []interface{}{&services.Service{}, &users.User{}, &hits.Hit{}, &failures.Failure{}, &messages.Message{}, &groups.Group{}, &checkins.Checkin{}, &checkins.CheckinHit{}, &notifiers.Notification{}, &incidents.Incident{}, &incidents.IncidentUpdate{}}
var DbModels = []interface{}{&services.Service{}, &users.User{}, &hits.Hit{}, &failures.Failure{}, &messages.Message{}, &groups.Group{}, &checkins.Checkin{}, &checkins.CheckinHit{}, &notifications.Notification{}, &incidents.Incident{}, &incidents.IncidentUpdate{}}
log.Infoln("Dropping Database Tables...")
for _, t := range DbModels {
if err := d.Db.DropTableIfExists(t); err != nil {
@ -94,7 +94,7 @@ func (d *DbConfig) Close() {
func (d *DbConfig) CreateDatabase() error {
var err error
var DbModels = []interface{}{&services.Service{}, &users.User{}, &hits.Hit{}, &failures.Failure{}, &messages.Message{}, &groups.Group{}, &checkins.Checkin{}, &checkins.CheckinHit{}, &notifiers.Notification{}, &incidents.Incident{}, &incidents.IncidentUpdate{}}
var DbModels = []interface{}{&services.Service{}, &users.User{}, &hits.Hit{}, &failures.Failure{}, &messages.Message{}, &groups.Group{}, &checkins.Checkin{}, &checkins.CheckinHit{}, &notifications.Notification{}, &incidents.Incident{}, &incidents.IncidentUpdate{}}
log.Infoln("Creating Database Tables...")
for _, table := range DbModels {

View File

@ -5,7 +5,8 @@ import (
_ "github.com/jinzhu/gorm/dialects/mysql"
_ "github.com/jinzhu/gorm/dialects/postgres"
_ "github.com/jinzhu/gorm/dialects/sqlite"
"github.com/statping/statping/notifiers"
"github.com/statping/statping/types/notifications"
"github.com/statping/statping/types/checkins"
"github.com/statping/statping/types/core"
"github.com/statping/statping/types/failures"
@ -52,7 +53,7 @@ func (c *DbConfig) DatabaseChanges() error {
//If this function has an issue, it will ROLLBACK to the previous state.
func (c *DbConfig) MigrateDatabase() error {
var DbModels = []interface{}{&services.Service{}, &users.User{}, &hits.Hit{}, &failures.Failure{}, &messages.Message{}, &groups.Group{}, &checkins.Checkin{}, &checkins.CheckinHit{}, &notifiers.Notification{}, &incidents.Incident{}, &incidents.IncidentUpdate{}}
var DbModels = []interface{}{&services.Service{}, &users.User{}, &hits.Hit{}, &failures.Failure{}, &messages.Message{}, &groups.Group{}, &checkins.Checkin{}, &checkins.CheckinHit{}, &notifications.Notification{}, &incidents.Incident{}, &incidents.IncidentUpdate{}}
log.Infoln("Migrating Database Tables...")
tx := c.Db.Begin()

View File

@ -1,32 +1,21 @@
package notifiers
package notifications
import (
"errors"
"github.com/statping/statping/database"
)
var db database.Database
var (
db database.Database
)
func SetDB(database database.Database) {
db = database.Model(&Notification{})
}
func appendList(n Notifier) {
allNotifiers = append(allNotifiers, n)
}
func Find(name string) (*Notification, error) {
for _, n := range allNotifiers {
notif := n.(*Notification)
if notif.Method == name {
return notif, nil
}
}
return nil, errors.New("notifier not found")
}
func All() []Notifier {
return allNotifiers
func Find(method string) (*Notification, error) {
var notification Notification
q := db.Where("method = ?", method).Find(&notification)
return &notification, q.Error()
}
func (n *Notification) Create() error {
@ -36,7 +25,6 @@ func (n *Notification) Create() error {
return err
}
}
appendList(n)
return nil
}
@ -45,7 +33,6 @@ func (n *Notification) Update() error {
if n.Enabled.Bool {
n.Close()
n.Start()
go Queue(n)
} else {
n.Close()
}
@ -53,6 +40,8 @@ func (n *Notification) Update() error {
return err.Error()
}
func (n *Notification) Delete() error {
return nil
func loadAll() []*Notification {
var notifications []*Notification
db.Find(&notifications)
return notifications
}

View File

@ -1,4 +1,4 @@
package notifiers
package notifications
import (
"fmt"
@ -21,49 +21,48 @@ func (n *Notification) AfterFind() (err error) {
}
// AddQueue will add any type of interface (json, string, struct, etc) into the Notifiers queue
func (n *Notification) AddQueue(uid string, msg interface{}) {
data := &QueueData{uid, msg}
n.Queue = append(n.Queue, data)
log.WithFields(utils.ToFields(data, n)).Debug(fmt.Sprintf("Notifier '%v' added new item (%v) to the queue. (%v queued)", n.Method, uid, len(n.Queue)))
}
//func (n *Notification) AddQueue(uid string, msg interface{}) {
// data := &QueueData{uid, msg}
// n.Queue = append(n.Queue, data)
// log.WithFields(utils.ToFields(data, n)).Debug(fmt.Sprintf("Notifier '%v' added new item (%v) to the queue. (%v queued)", n.Method, uid, len(n.Queue)))
//}
// CanTest returns true if the notifier implements the OnTest interface
func (n *Notification) CanTest() bool {
return n.testable
}
//func (n *Notification) CanTest() bool {
// return n.testable
//}
// LastSent returns a time.Duration of the last sent notification for the notifier
func (n *Notification) LastSent() time.Duration {
if len(n.logs) == 0 {
return time.Duration(0)
}
last := n.Logs()[0]
since := time.Since(last.Timestamp)
since := time.Since(n.lastSent)
return since
}
// SentLastHour returns the total amount of notifications sent in last 1 hour
func (n *Notification) SentLastHour() int {
since := utils.Now().Add(-1 * time.Hour)
return n.SentLast(since)
}
func (n *Notification) CanSend() bool {
if !n.Enabled.Bool {
return false
}
// SentLastMinute returns the total amount of notifications sent in last 1 minute
func (n *Notification) SentLastMinute() int {
since := utils.Now().Add(-1 * time.Minute)
return n.SentLast(since)
}
fmt.Println("Last sent: ", n.lastSent.String())
fmt.Println("Last count: ", n.lastSentCount)
fmt.Println("Last sent before now: ", n.lastSent.Add(60*time.Second).Before(utils.Now()))
// SentLast accept a time.Time and returns the amount of sent notifications within your time to current
func (n *Notification) SentLast(since time.Time) int {
sent := 0
for _, v := range n.Logs() {
lastTime := time.Time(v.Time).UTC()
if lastTime.After(since) {
sent++
// the last sent notification was past 1 minute (limit per minute)
if n.lastSent.Add(60 * time.Second).Before(utils.Now()) {
if n.lastSentCount != 0 {
n.lastSentCount--
}
}
return sent
// dont send if already beyond the notifier's limit
if n.lastSentCount >= n.Limits {
return false
}
// action to do since notifier is able to send
n.lastSentCount++
n.lastSent = utils.Now()
return true
}
// GetValue returns the database value of a accept DbField value.
@ -130,18 +129,6 @@ func (n *Notification) ResetQueue() {
n.Queue = nil
}
// ResetQueue will clear the notifiers Queue for a service
func (n *Notification) ResetUniqueQueue(uid string) []*QueueData {
var queue []*QueueData
for _, v := range n.Queue {
if v.Id != uid {
queue = append(queue, v)
}
}
n.Queue = queue
return queue
}
// start will start the go routine for the notifier queue
func (n *Notification) Start() {
n.Running = make(chan bool)

View File

@ -0,0 +1,74 @@
package notifications
import (
"github.com/statping/statping/types/null"
"github.com/statping/statping/utils"
"time"
)
var (
log = utils.Log
)
// Notification contains all the fields for a Statping Notifier.
type Notification struct {
Id int64 `gorm:"primary_key;column:id" json:"id"`
Method string `gorm:"column:method" json:"method"`
Host string `gorm:"not null;column:host" json:"host,omitempty"`
Port int `gorm:"not null;column:port" json:"port,omitempty"`
Username string `gorm:"not null;column:username" json:"username,omitempty"`
Password string `gorm:"not null;column:password" json:"password,omitempty"`
Var1 string `gorm:"not null;column:var1" json:"var1,omitempty"`
Var2 string `gorm:"not null;column:var2" json:"var2,omitempty"`
ApiKey string `gorm:"not null;column:api_key" json:"api_key,omitempty"`
ApiSecret string `gorm:"not null;column:api_secret" json:"api_secret,omitempty"`
Enabled null.NullBool `gorm:"column:enabled;type:boolean;default:false" json:"enabled"`
Limits int `gorm:"not null;column:limits" json:"limits"`
Removable bool `gorm:"column:removable" json:"removeable"`
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
Form []NotificationForm `gorm:"-" json:"form"`
Title string `gorm:"-" json:"title"`
Description string `gorm:"-" json:"description"`
Author string `gorm:"-" json:"author"`
AuthorUrl string `gorm:"-" json:"author_url"`
Icon string `gorm:"-" json:"icon"`
Delay time.Duration `gorm:"-" json:"delay,string"`
Running chan bool `gorm:"-" json:"-"`
Queue []RunFunc `gorm:"-" json:"-"`
lastSent time.Time `gorm:"-" json:"-"`
lastSentCount int `gorm:"-" json:"-"`
Hits notificationHits `gorm:"-" json:"-"`
}
type RunFunc func(interface{}) error
// NotificationForm contains the HTML fields for each variable/input you want the notifier to accept.
type NotificationForm struct {
Type string `json:"type"` // the html input type (text, password, email)
Title string `json:"title"` // include a title for ease of use
Placeholder string `json:"placeholder"` // add a placeholder for the input
DbField string `json:"field"` // true variable key for input
SmallText string `json:"small_text"` // insert small text under a html input
Required bool `json:"required"` // require this input on the html form
IsHidden bool `json:"hidden"` // hide this form element from end user
IsList bool `json:"list"` // make this form element a comma separated list
IsSwitch bool `json:"switch"` // make the notifier a boolean true/false switch
}
type notificationHits struct {
OnSuccess int64 `gorm:"-" json:"-"`
OnFailure int64 `gorm:"-" json:"-"`
OnSave int64 `gorm:"-" json:"-"`
OnNewService int64 `gorm:"-" json:"-"`
OnUpdatedService int64 `gorm:"-" json:"-"`
OnDeletedService int64 `gorm:"-" json:"-"`
OnNewUser int64 `gorm:"-" json:"-"`
OnUpdatedUser int64 `gorm:"-" json:"-"`
OnDeletedUser int64 `gorm:"-" json:"-"`
OnNewNotifier int64 `gorm:"-" json:"-"`
OnUpdatedNotifier int64 `gorm:"-" json:"-"`
}

View File

@ -0,0 +1,13 @@
package notifier
import (
"github.com/statping/statping/types/failures"
"github.com/statping/statping/types/services"
)
// Notifier interface is required to create a new Notifier
type Notifier interface {
OnSuccess(*services.Service) error // OnSuccess is triggered when a service is successful
OnFailure(*services.Service, *failures.Failure) error // OnFailure is triggered when a service is failing
OnTest() error // OnTest is triggered for testing
}

View File

@ -0,0 +1,21 @@
package services
import (
"github.com/statping/statping/types/failures"
"github.com/statping/statping/types/notifications"
)
var (
allNotifiers []ServiceNotifier
)
func AllNotifiers() []ServiceNotifier {
return allNotifiers
}
type ServiceNotifier interface {
OnSuccess(*Service) error // OnSuccess is triggered when a service is successful
OnFailure(*Service, *failures.Failure) error // OnFailure is triggered when a service is failing
OnTest() error // OnTest is triggered for testing
Select() *notifications.Notification // OnTest is triggered for testing
}

View File

@ -3,16 +3,17 @@ package services
import (
"bytes"
"fmt"
"github.com/statping/statping/types/failures"
"github.com/statping/statping/types/hits"
"github.com/statping/statping/utils"
"github.com/tatsushid/go-fastping"
"net"
"net/http"
"net/url"
"regexp"
"strings"
"time"
"github.com/statping/statping/types/failures"
"github.com/statping/statping/types/hits"
"github.com/statping/statping/utils"
"github.com/tatsushid/go-fastping"
)
// checkServices will start the checking go routine for each service
@ -242,10 +243,36 @@ func recordSuccess(s *Service) {
fmt.Sprintf("Service #%d '%v' Successful Response: %s | Lookup in: %s | Online: %v | Interval: %d seconds", s.Id, s.Name, humanMicro(hit.Latency), humanMicro(hit.PingTime), s.Online, s.Interval))
s.LastLookupTime = hit.PingTime
s.LastLatency = hit.Latency
//notifiers.OnSuccess(s)
sendSuccess(s)
s.SuccessNotified = true
}
func AddNotifier(n ServiceNotifier) {
allNotifiers = append(allNotifiers, n)
}
func sendSuccess(s *Service) {
if !s.AllowNotifications.Bool {
return
}
// dont send notification if server was already previous online
if s.SuccessNotified {
return
}
for _, n := range allNotifiers {
notif := n.Select()
if notif.CanSend() {
log.Infof("Sending notification to: %s!", notif.Method)
if err := n.OnSuccess(s); err != nil {
log.Errorln(err)
}
s.UserNotified = true
s.SuccessNotified = true
//s.UpdateNotify.Bool
}
}
}
// recordFailure will create a new 'Failure' record in the database for a offline service
func recordFailure(s *Service, issue string) {
s.LastOffline = utils.Now()
@ -266,7 +293,32 @@ func recordFailure(s *Service, issue string) {
s.Online = false
s.SuccessNotified = false
s.DownText = s.DowntimeText()
//notifiers.OnFailure(s, fail)
sendFailure(s, fail)
}
func sendFailure(s *Service, f *failures.Failure) {
if !s.AllowNotifications.Bool {
return
}
// ignore failure if user was already notified and
// they have "continuous notifications" switched off.
if s.UserNotified && !s.UpdateNotify.Bool {
return
}
for _, n := range allNotifiers {
notif := n.Select()
if notif.CanSend() {
log.Infof("Sending Failure notification to: %s!", notif.Method)
if err := n.OnFailure(s, f); err != nil {
log.Errorln(err)
}
s.UserNotified = true
s.SuccessNotified = true
//s.UpdateNotify.Bool
}
}
}
// Check will run checkHttp for HTTP services and checkTcp for TCP services