notifier updates - discord - organize

pull/78/head v0.58
Hunter Long 2018-09-11 21:14:22 -07:00
parent ce1dc32dad
commit 685197bd72
31 changed files with 707 additions and 610 deletions

View File

@ -7,5 +7,6 @@ source/rice-box.go
.DS_Store
*/**/node_modules
vendor
!build/alpine-linux-amd64
servers
dev
!build/alpine-linux-amd64

View File

@ -1,4 +1,4 @@
VERSION=0.57
VERSION=0.58
BINARY_NAME=statup
GOPATH:=$(GOPATH)
GOCMD=go
@ -159,7 +159,7 @@ docker-push-cypress:
docker push hunterlong/statup:cypress
# push the :latest tag to Docker hub
docker-push-latest: docker
docker-push-latest:
docker push hunterlong/statup:latest
# push the :base and :base-v{VERSION} tag to Docker hub

View File

@ -22,7 +22,6 @@ import (
"github.com/hunterlong/statup/core"
"github.com/hunterlong/statup/handlers"
"github.com/hunterlong/statup/source"
"github.com/hunterlong/statup/types"
"github.com/hunterlong/statup/utils"
"github.com/joho/godotenv"
"io/ioutil"
@ -149,10 +148,10 @@ func RunOnce() {
if err != nil {
utils.Log(4, err)
}
for _, s := range core.CoreApp.Services {
out := s.(*types.Service)
for _, out := range core.CoreApp.Services {
service := out.Select()
out.Check(true)
fmt.Printf(" Service %v | URL: %v | Latency: %0.0fms | Online: %v\n", out.Name, out.Domain, (out.Latency * 1000), out.Online)
fmt.Printf(" Service %v | URL: %v | Latency: %0.0fms | Online: %v\n", service.Name, service.Domain, (service.Latency * 1000), service.Online)
}
}

View File

@ -19,8 +19,8 @@ import (
"github.com/gorilla/mux"
"github.com/gorilla/sessions"
"github.com/hunterlong/statup/core"
"github.com/hunterlong/statup/core/notifier"
"github.com/hunterlong/statup/handlers"
"github.com/hunterlong/statup/notifiers"
"github.com/hunterlong/statup/source"
"github.com/hunterlong/statup/types"
"github.com/hunterlong/statup/utils"
@ -112,7 +112,7 @@ func TestRunAll(t *testing.T) {
})
t.Run(dbt+" Select Comms", func(t *testing.T) {
t.SkipNow()
RunSelectAllMysqlCommunications(t)
RunSelectAllNotifiers(t)
})
t.Run(dbt+" Create Users", func(t *testing.T) {
RunUser_Create(t)
@ -290,15 +290,16 @@ func RunSelectCoreMYQL(t *testing.T, db string) {
func RunSelectAllMysqlServices(t *testing.T) {
var err error
t.SkipNow()
services, err := core.CoreApp.SelectAllServices()
assert.Nil(t, err)
assert.Equal(t, 18, len(services))
}
func RunSelectAllMysqlCommunications(t *testing.T) {
func RunSelectAllNotifiers(t *testing.T) {
var err error
notifiers.Collections = core.DbSession.Table("communication").Model(&notifiers.Notification{})
comms := notifiers.Load()
notifier.SetDB(core.DbSession)
comms := notifier.Load()
assert.Nil(t, err)
assert.Equal(t, 3, len(comms))
}
@ -473,7 +474,7 @@ func RunDeleteService(t *testing.T) {
func RunCreateService_Hits(t *testing.T) {
services := core.CoreApp.Services
assert.NotNil(t, services)
assert.Equal(t, 37, len(services))
assert.Equal(t, 19, len(services))
for _, service := range services {
service.Check(true)
assert.NotNil(t, service)
@ -528,7 +529,7 @@ func RunPrometheusHandler(t *testing.T) {
rr := httptest.NewRecorder()
route.ServeHTTP(rr, req)
t.Log(rr.Body.String())
assert.True(t, strings.Contains(rr.Body.String(), "statup_total_services 37"))
assert.True(t, strings.Contains(rr.Body.String(), "statup_total_services 19"))
assert.True(t, handlers.IsAuthenticated(req))
}

View File

@ -18,7 +18,7 @@ package core
import (
"bytes"
"fmt"
"github.com/hunterlong/statup/notifiers"
"github.com/hunterlong/statup/core/notifier"
"github.com/hunterlong/statup/types"
"github.com/hunterlong/statup/utils"
"io/ioutil"
@ -224,7 +224,7 @@ func RecordSuccess(s *Service) {
}
utils.Log(1, fmt.Sprintf("Service %v Successful: %0.2f ms", s.Name, hit.Latency*1000))
s.CreateHit(hit)
notifiers.OnSuccess(s.Service)
notifier.OnSuccess(s.Service)
}
// RecordFailure will create a new 'failure' record in the database for a offline service
@ -237,5 +237,5 @@ func RecordFailure(s *Service, issue string) {
}
utils.Log(2, fmt.Sprintf("Service %v Failing: %v", s.Name, issue))
s.CreateFailure(fail)
notifiers.OnFailure(s.Service, fail)
notifier.OnFailure(s.Service, fail)
}

View File

@ -37,7 +37,7 @@ func ReturnCheckin(s *types.Checkin) *Checkin {
func FindCheckin(api string) *types.Checkin {
for _, ser := range CoreApp.Services {
service := ser.(*types.Service)
service := ser.Select()
for _, c := range service.Checkins {
if c.Api == api {
return c

View File

@ -16,7 +16,8 @@
package core
import (
"github.com/hunterlong/statup/notifiers"
"github.com/hunterlong/statup/core/notifier"
_ "github.com/hunterlong/statup/notifiers"
"github.com/hunterlong/statup/source"
"github.com/hunterlong/statup/types"
"github.com/hunterlong/statup/utils"
@ -57,21 +58,21 @@ func (c *Core) ToCore() *types.Core {
// InitApp will initialize Statup
func InitApp() {
SelectCore()
InsertNotifierDB()
insertNotifierDB()
CoreApp.SelectAllServices()
CheckServices()
CoreApp.Communications = notifiers.Load()
CoreApp.Notifications = notifier.Load()
go DatabaseMaintence()
}
func InsertNotifierDB() error {
func insertNotifierDB() error {
if DbSession == nil {
err := Configs.Connect(false, utils.Directory)
if err != nil {
return errors.New("database connection has not been created")
}
}
notifiers.Collections = commDB()
notifier.SetDB(DbSession)
return nil
}
@ -81,14 +82,6 @@ func UpdateCore(c *Core) (*Core, error) {
return c, db.Error
}
func (c *Core) Notifiers() []notifiers.Notification {
var n []notifiers.Notification
for _, c := range c.Communications {
n = append(n, c.(notifiers.Notification))
}
return n
}
// UsingAssets will return true if /assets folder is present
func (c Core) UsingAssets() bool {
return source.UsingAssets(utils.Directory)
@ -122,7 +115,7 @@ func (c Core) MobileSASS() string {
// AllOnline will be true if all services are online
func (c Core) AllOnline() bool {
for _, s := range CoreApp.Services {
if !s.(*types.Service).Online {
if !s.Select().Online {
return false
}
}

View File

@ -103,7 +103,7 @@ func TestSelectCore(t *testing.T) {
}
func TestInsertNotifierDB(t *testing.T) {
err := InsertNotifierDB()
err := insertNotifierDB()
assert.Nil(t, err)
}

View File

@ -18,7 +18,7 @@ package core
import (
"fmt"
"github.com/go-yaml/yaml"
"github.com/hunterlong/statup/notifiers"
"github.com/hunterlong/statup/core/notifier"
"github.com/hunterlong/statup/types"
"github.com/hunterlong/statup/utils"
"github.com/jinzhu/gorm"
@ -66,7 +66,7 @@ func usersDB() *gorm.DB {
// commDB returns the 'communications' database column
func commDB() *gorm.DB {
return DbSession.Table("communication").Model(&notifiers.Notification{})
return DbSession.Model(&notifier.Notification{})
}
// hitsDB returns the 'hits' database column
@ -252,7 +252,7 @@ func (db *DbConfig) SeedDatabase() (string, string, error) {
func (db *DbConfig) DropDatabase() error {
utils.Log(1, "Dropping Database Tables...")
err := DbSession.DropTableIfExists("checkins")
err = DbSession.DropTableIfExists("communication")
err = DbSession.DropTableIfExists("notifications")
err = DbSession.DropTableIfExists("core")
err = DbSession.DropTableIfExists("failures")
err = DbSession.DropTableIfExists("hits")
@ -265,7 +265,7 @@ func (db *DbConfig) DropDatabase() error {
func (db *DbConfig) CreateDatabase() error {
utils.Log(1, "Creating Database Tables...")
err := DbSession.CreateTable(&types.Checkin{})
err = DbSession.Table("communication").CreateTable(&notifiers.Notification{})
err = DbSession.CreateTable(&notifier.Notification{})
err = DbSession.Table("core").CreateTable(&types.Core{})
err = DbSession.CreateTable(&types.Failure{})
err = DbSession.CreateTable(&types.Hit{})
@ -290,7 +290,7 @@ func (db *DbConfig) MigrateDatabase() error {
if tx.Error != nil {
return tx.Error
}
tx = tx.AutoMigrate(&types.Service{}, &types.User{}, &types.Hit{}, &types.Failure{}, &types.Checkin{}).Table("core").AutoMigrate(&types.Core{}).Table("communication").AutoMigrate(&notifiers.Notification{})
tx = tx.AutoMigrate(&types.Service{}, &types.User{}, &types.Hit{}, &types.Failure{}, &types.Checkin{}, &notifier.Notification{}).Table("core").AutoMigrate(&types.Core{})
if tx.Error != nil {
tx.Rollback()
utils.Log(3, fmt.Sprintf("Statup Database could not be migrated: %v", tx.Error))

View File

@ -1,82 +0,0 @@
// 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 core
import (
"github.com/fatih/structs"
"github.com/hunterlong/statup/notifiers"
"github.com/hunterlong/statup/types"
"github.com/jinzhu/gorm"
)
func OnLoad(db *gorm.DB) {
for _, p := range CoreApp.AllPlugins {
p.OnLoad(*db)
}
}
func OnSuccess(s *Service) {
for _, p := range CoreApp.AllPlugins {
p.OnSuccess(structs.Map(s))
}
notifiers.OnSuccess(s.Service)
}
func OnFailure(s *Service, f *types.Failure) {
for _, p := range CoreApp.AllPlugins {
p.OnFailure(structs.Map(s))
}
notifiers.OnFailure(s.Service, f)
}
func OnSettingsSaved(c *types.Core) {
for _, p := range CoreApp.AllPlugins {
p.OnSettingsSaved(structs.Map(c))
}
}
func OnNewUser(u *User) {
for _, p := range CoreApp.AllPlugins {
p.OnNewUser(structs.Map(u))
}
}
func OnNewService(s *Service) {
for _, p := range CoreApp.AllPlugins {
p.OnNewService(structs.Map(s))
}
}
func OnDeletedService(s *Service) {
for _, p := range CoreApp.AllPlugins {
p.OnDeletedService(structs.Map(s))
}
}
func OnUpdateService(s *Service) {
for _, p := range CoreApp.AllPlugins {
p.OnUpdatedService(structs.Map(s))
}
}
func SelectPlugin(name string) types.PluginActions {
for _, p := range CoreApp.AllPlugins {
if p.GetInfo().Name == name {
return p
}
}
return types.PluginInfo{}
}

View File

@ -19,7 +19,6 @@ import (
"bytes"
"fmt"
"github.com/hunterlong/statup/source"
"github.com/hunterlong/statup/types"
"github.com/hunterlong/statup/utils"
"html/template"
"io/ioutil"
@ -39,7 +38,7 @@ func ExportIndexHTML() string {
CoreApp.SelectAllServices()
CoreApp.UseCdn = true
for _, srv := range CoreApp.Services {
service := srv.(*types.Service)
service := srv.(*Service)
service.Check(true)
fmt.Println(service.Name, service.Online, service.Latency)
}

46
core/notifier/audit.go Normal file
View File

@ -0,0 +1,46 @@
// 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 notifier
import (
"errors"
"fmt"
"strings"
)
var (
allowed_vars = []string{"host", "username", "password", "port", "api_key", "api_secret", "var1", "var2"}
)
func checkNotifierForm(n Notifier) error {
notifier := n.Select()
for _, f := range notifier.Form {
contains := contains(f.DbField, allowed_vars)
if !contains {
return errors.New(fmt.Sprintf("the DbField '%v' is not allowed, allowed vars: %v", f.DbField, allowed_vars))
}
}
return nil
}
func contains(s string, arr []string) bool {
for _, v := range arr {
if strings.ToLower(s) == v {
return true
}
}
return false
}

View File

@ -13,14 +13,14 @@
// 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
package notifier
import "github.com/hunterlong/statup/types"
// OnSave will trigger a notifier when it has been saved - Notifier interface
func OnSave(method string) {
for _, comm := range AllCommunications {
if IsType(comm, "Notifier") {
if isType(comm, new(Notifier)) {
notifier := comm.(Notifier).Select()
if notifier.Method == method {
comm.(Notifier).OnSave()
@ -32,7 +32,7 @@ func OnSave(method string) {
// OnFailure will be triggered when a service is failing - BasicEvents interface
func OnFailure(s *types.Service, f *types.Failure) {
for _, comm := range AllCommunications {
if IsType(comm, "BasicEvents") {
if isType(comm, new(BasicEvents)) && isEnabled(comm) {
comm.(BasicEvents).OnFailure(s, f)
}
}
@ -41,7 +41,7 @@ func OnFailure(s *types.Service, f *types.Failure) {
// OnSuccess will be triggered when a service is successful - BasicEvents interface
func OnSuccess(s *types.Service) {
for _, comm := range AllCommunications {
if IsType(comm, "BasicEvents") {
if isType(comm, new(BasicEvents)) && isEnabled(comm) {
comm.(BasicEvents).OnSuccess(s)
}
}
@ -50,7 +50,7 @@ func OnSuccess(s *types.Service) {
// OnNewService is triggered when a new service is created - ServiceEvents interface
func OnNewService(s *types.Service) {
for _, comm := range AllCommunications {
if IsType(comm, "ServiceEvents") {
if isType(comm, new(ServiceEvents)) && isEnabled(comm) {
comm.(ServiceEvents).OnNewService(s)
}
}
@ -59,7 +59,7 @@ func OnNewService(s *types.Service) {
// OnUpdatedService is triggered when a service is updated - ServiceEvents interface
func OnUpdatedService(s *types.Service) {
for _, comm := range AllCommunications {
if IsType(comm, "ServiceEvents") {
if isType(comm, new(ServiceEvents)) && isEnabled(comm) {
comm.(ServiceEvents).OnUpdatedService(s)
}
}
@ -68,7 +68,7 @@ func OnUpdatedService(s *types.Service) {
// OnDeletedService is triggered when a service is deleted - ServiceEvents interface
func OnDeletedService(s *types.Service) {
for _, comm := range AllCommunications {
if IsType(comm, "ServiceEvents") {
if isType(comm, new(ServiceEvents)) && isEnabled(comm) {
comm.(ServiceEvents).OnDeletedService(s)
}
}
@ -77,7 +77,7 @@ func OnDeletedService(s *types.Service) {
// OnNewUser is triggered when a new user is created - UserEvents interface
func OnNewUser(u *types.User) {
for _, comm := range AllCommunications {
if IsType(comm, "UserEvents") {
if isType(comm, new(UserEvents)) && isEnabled(comm) {
comm.(UserEvents).OnNewUser(u)
}
}
@ -86,7 +86,7 @@ func OnNewUser(u *types.User) {
// OnUpdatedUser is triggered when a new user is updated - UserEvents interface
func OnUpdatedUser(u *types.User) {
for _, comm := range AllCommunications {
if IsType(comm, "UserEvents") {
if isType(comm, new(UserEvents)) && isEnabled(comm) {
comm.(UserEvents).OnUpdatedUser(u)
}
}
@ -95,7 +95,7 @@ func OnUpdatedUser(u *types.User) {
// OnDeletedUser is triggered when a new user is deleted - UserEvents interface
func OnDeletedUser(u *types.User) {
for _, comm := range AllCommunications {
if IsType(comm, "UserEvents") {
if isType(comm, new(UserEvents)) && isEnabled(comm) {
comm.(UserEvents).OnDeletedUser(u)
}
}
@ -104,7 +104,7 @@ func OnDeletedUser(u *types.User) {
// OnUpdatedCore is triggered when the CoreApp settings are saved - CoreEvents interface
func OnUpdatedCore(c *types.Core) {
for _, comm := range AllCommunications {
if IsType(comm, "CoreEvents") {
if isType(comm, new(CoreEvents)) && isEnabled(comm) {
comm.(CoreEvents).OnUpdatedCore(c)
}
}
@ -113,7 +113,7 @@ func OnUpdatedCore(c *types.Core) {
// NotifierEvents interface
func OnNewNotifier(n *Notification) {
for _, comm := range AllCommunications {
if IsType(comm, "NotifierEvents") {
if isType(comm, new(NotifierEvents)) && isEnabled(comm) {
comm.(NotifierEvents).OnNewNotifier(n)
}
}
@ -122,7 +122,7 @@ func OnNewNotifier(n *Notification) {
// NotifierEvents interface
func OnUpdatedNotifier(n *Notification) {
for _, comm := range AllCommunications {
if IsType(comm, "NotifierEvents") {
if isType(comm, new(NotifierEvents)) && isEnabled(comm) {
comm.(NotifierEvents).OnUpdatedNotifier(n)
}
}

View File

@ -0,0 +1,160 @@
// 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 notifier
import (
"fmt"
"github.com/hunterlong/statup/types"
)
type Example struct {
*Notification
}
const (
EXAMPLE_METHOD = "example"
)
var example = &Example{&Notification{
Method: EXAMPLE_METHOD,
Host: "http://exmaplehost.com",
Form: []NotificationForm{{
Type: "text",
Title: "Host",
Placeholder: "Insert your Host here.",
DbField: "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",
}}},
}
// REQUIRED init() will install/load the notifier
func init() {
AddNotifier(example)
}
// REQUIRED
func (n *Example) Run() error {
return nil
}
// REQUIRED
func (n *Example) OnSave() error {
return nil
}
// REQUIRED
func (n *Example) Test() error {
return nil
}
// REQUIRED
func (n *Example) Select() *Notification {
return n.Notification
}
// REQUIRED - BASIC EVENT
func (n *Example) OnSuccess(s *types.Service) {
saySomething("service is is online!")
}
// REQUIRED - BASIC EVENT
func (n *Example) OnFailure(s *types.Service, f *types.Failure) {
saySomething("service is failing!")
}
// Example function to do something awesome or not...
func saySomething(text ...interface{}) {
fmt.Println(text)
}
// OPTIONAL
func (n *Example) OnNewService(s *types.Service) {
}
// OPTIONAL
func (n *Example) OnUpdatedService(s *types.Service) {
}
// OPTIONAL
func (n *Example) OnDeletedService(s *types.Service) {
}
// OPTIONAL
func (n *Example) OnNewUser(s *types.User) {
}
// OPTIONAL
func (n *Example) OnUpdatedUser(s *types.User) {
}
// OPTIONAL
func (n *Example) OnDeletedUser(s *types.User) {
}
// OPTIONAL
func (n *Example) OnUpdatedCore(s *types.Core) {
}
// OPTIONAL
func (n *Example) OnNewNotifier(s *Notification) {
}
// OPTIONAL
func (n *Example) OnUpdatedNotifier(s *Notification) {
}

View File

@ -13,32 +13,22 @@
// 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
package notifier
import "github.com/hunterlong/statup/types"
// Notifier interface is required to create a new Notifier
type Notifier interface {
// Init will load and install the notifier if needed
Init() error
// Install will install the notifier into the database
Install() error
// Run will trigger inside of the notifier when enabled
Run() error
// OnSave is triggered when the notifier is saved
OnSave() error
// Test will run a function inside the notifier to Test if it works
Test() error
// Select returns the *Notification for a notifier
Select() *Notification
Run() error // Run will trigger inside of the notifier when enabled
OnSave() error // OnSave is triggered when the notifier is saved
Test() error // Test will run a function inside the notifier to Test if it works
Select() *Notification // Select returns the *Notification for a notifier
}
// BasicEvents includes the most minimal events, failing and successful service triggers
type BasicEvents interface {
// OnSuccess is triggered when a service is successful
OnSuccess(*types.Service)
// OnFailure is triggered when a service is failing
OnFailure(*types.Service, *types.Failure)
OnSuccess(*types.Service) // OnSuccess is triggered when a service is successful
OnFailure(*types.Service, *types.Failure) // OnFailure is triggered when a service is failing
}
// ServiceEvents are events for Services

View File

@ -13,7 +13,7 @@
// 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
package notifier
import (
"errors"
@ -28,12 +28,11 @@ import (
var (
AllCommunications []types.AllNotifiers
Collections *gorm.DB
Logs []*NotificationLog
db *gorm.DB
)
type Notification struct {
Id int64 `gorm:"primary_key column:id" json:"id"`
Id int64 `gorm:"primary_key;column:id" json:"id"`
Method string `gorm:"column:method" json:"method"`
Host string `gorm:"not null;column:host" json:"-"`
Port int `gorm:"not null;column:port" json:"-"`
@ -50,7 +49,7 @@ type Notification struct {
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
Form []NotificationForm `gorm:"-" json:"-"`
Routine chan struct{} `gorm:"-" json:"-"`
Notifier
logs []*NotificationLog `gorm:"-" json:"-"`
}
type NotificationForm struct {
@ -61,27 +60,43 @@ type NotificationForm struct {
}
type NotificationLog struct {
Notifier *Notification
Message string
Time utils.Timestamp
Message string
Time utils.Timestamp
Timestamp time.Time
}
func AddNotifier(c interface{}) error {
if _, ok := c.(Notifier); ok {
AllCommunications = append(AllCommunications, c)
// db will return the notifier database column/record
func (n *Notification) db() *gorm.DB {
return db.Model(&Notification{}).Where("method = ?", n.Method).Find(n)
}
// SetDB is called by core to inject the database for a notifier to use
func SetDB(d *gorm.DB) {
db = d
}
// AddNotifier accept a Notifier interface to be added into the array
func AddNotifier(c Notifier) error {
if notifier, ok := c.(Notifier); ok {
err := checkNotifierForm(notifier)
if err != nil {
return err
}
AllCommunications = append(AllCommunications, notifier)
} else {
return errors.New("notifier does not have the required methods")
}
return nil
}
// Load is called by core to add all the notifier into memory
func Load() []types.AllNotifiers {
var notifiers []types.AllNotifiers
for _, comm := range AllCommunications {
n := comm.(Notifier)
n.Init()
Init(n)
notifiers = append(notifiers, n)
n.Test()
//n.Test()
}
return notifiers
}
@ -90,25 +105,22 @@ func (n *Notification) Select() *Notification {
return n
}
// Log will record a new notification into memory and will show the logs on the settings page
func (n *Notification) Log(msg string) {
log := &NotificationLog{
Notifier: n,
Message: msg,
Time: utils.Timestamp(time.Now()),
Message: msg,
Time: utils.Timestamp(time.Now()),
Timestamp: time.Now(),
}
Logs = append(Logs, log)
n.logs = append(n.logs, log)
}
// Logs returns an array of the notifiers logs
func (n *Notification) Logs() []*NotificationLog {
var logs []*NotificationLog
for _, v := range Logs {
if v.Notifier.Id == n.Id {
logs = append(logs, v)
}
}
return reverseLogs(logs)
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
@ -116,31 +128,36 @@ func reverseLogs(input []*NotificationLog) []*NotificationLog {
return append(reverseLogs(input[1:]), input[0])
}
func (n *Notification) IsInDatabase() bool {
return !Collections.Find(n).RecordNotFound()
// isInDatabase returns true if the notifier has already been installed
func (n *Notification) isInDatabase() bool {
inDb := n.db().RecordNotFound()
return !inDb
}
func SelectNotification(id int64) (*Notification, error) {
// SelectNotification returns the Notification struct from the database
func SelectNotification(method string) (*Notification, error) {
var notifier Notification
err := Collections.Find(&notifier, id)
err := db.Model(&Notification{}).Where("method = ?", method).Scan(&notifier)
return &notifier, err.Error
}
// Update will update the notification into the database
func (n *Notification) Update() (*Notification, error) {
err := Collections.Update(n)
err := n.db().Update(n)
return n, err.Error
}
func InsertDatabase(n *Notification) (int64, error) {
n.CreatedAt = time.Now()
// insertDatabase will create a new record into the database for the notifier
func insertDatabase(n *Notification) (int64, error) {
n.Limits = 3
db := Collections.Create(n)
if db.Error != nil {
return 0, db.Error
query := db.Create(n)
if query.Error != nil {
return 0, query.Error
}
return n.Id, db.Error
return n.Id, query.Error
}
// SelectNotifier returns the Notification struct from the database
func SelectNotifier(method string) (*Notification, error) {
for _, comm := range AllCommunications {
n, ok := comm.(Notifier)
@ -155,6 +172,7 @@ func SelectNotifier(method string) (*Notification, error) {
return nil, nil
}
// CanSend will return true if notifier has not passed its Limits within the last hour
func (f *Notification) CanSend() bool {
if f.SentLastHour() >= f.Limits {
return false
@ -162,6 +180,41 @@ func (f *Notification) CanSend() bool {
return true
}
// Init accepts the Notifier interface to initialize the notifier
func Init(n Notifier) (*Notification, error) {
err := install(n)
var notify *Notification
if err == nil {
notify, _ = SelectNotification(n.Select().Method)
notify.Form = n.Select().Form
}
return notify, err
}
// 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 {
inDb := n.Select().isInDatabase()
if !inDb {
_, err := insertDatabase(n.Select())
if err != nil {
utils.Log(3, err)
return err
}
}
return nil
}
// LastSent returns a time.Duration of the last sent notification for the notifier
func (f *Notification) LastSent() time.Duration {
if len(f.logs) == 0 {
return time.Duration(0)
}
last := f.Logs()[0]
since := time.Since(last.Timestamp)
return since
}
// SentLastHour returns the amount of sent notifications within the last hour
func (f *Notification) SentLastHour() int {
sent := 0
hourAgo := time.Now().Add(-1 * time.Hour)
@ -174,10 +227,12 @@ func (f *Notification) SentLastHour() int {
return sent
}
func (f *Notification) LimitValue() int64 {
// Limit returns the limits on how many notifications can be sent in 1 hour
func (f *Notification) Limit() int64 {
return utils.StringInt(f.GetValue("limits"))
}
// GetValue returns the database value of a accept DbField value.
func (n *Notification) GetValue(dbField string) string {
dbField = strings.ToLower(dbField)
switch dbField {
@ -205,12 +260,20 @@ func (n *Notification) GetValue(dbField string) string {
return ""
}
func IsType(n interface{}, obj string) bool {
// isType will return true if a variable can implement an interface
func isType(n interface{}, obj interface{}) bool {
objOne := reflect.TypeOf(n)
return objOne.String() == obj
obj2 := reflect.TypeOf(obj)
return objOne.String() == obj2.String()
}
func uniqueStrings(elements []string) []string {
// isEnabled returns true if the notifier is enabled
func isEnabled(n interface{}) bool {
notify := n.(Notifier).Select()
return notify.Enabled
}
func UniqueStrings(elements []string) []string {
result := []string{}
for i := 0; i < len(elements); i++ {

View File

@ -1,3 +1,5 @@
// +build bypass
// Statup
// Copyright (C) 2018. Hunter Long and the project contributors
// Written by Hunter Long <info@socialeck.com> and the project contributors
@ -13,139 +15,89 @@
// 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
package notifier
import (
"github.com/hunterlong/statup/source"
"github.com/hunterlong/statup/types"
"github.com/hunterlong/statup/utils"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"testing"
)
var (
testNotifier *Tester
dir string
dir string
EXAMPLE_ID = "example"
)
//
//
//
func (n *Tester) Init() error {
return errors.New("im just testing")
}
func (n *Tester) Install() error {
return errors.New("installing")
}
func (n *Tester) Run() error {
return errors.New("running")
}
func (n *Tester) Select() *Notification {
return n.Notification
}
func (n *Tester) OnSuccess(s *types.Service) error {
return errors.New(s.Name)
}
func (n *Tester) OnFailure(s *types.Service) error {
return errors.New(s.Name)
}
func (n *Tester) Test() error {
return errors.New("testing")
}
func init() {
dir = utils.Directory
utils.InitLogs()
}
func injectDatabase() {
dbSession, _ := gorm.Open("sqlite3", dir+"/statup.db")
Collections = dbSession.Table("communication").Model(&Notification{})
db, _ = gorm.Open("sqlite3", dir+"/statup.db")
}
type Tester struct {
*Notification
}
func TestInit(t *testing.T) {
func TestLoad(t *testing.T) {
source.Assets()
utils.InitLogs()
injectDatabase()
}
func TestAdd(t *testing.T) {
testNotifier = &Tester{&Notification{
Id: 999999,
Method: "tester",
Host: "0.0.0.0",
Form: []NotificationForm{{
Type: "text",
Title: "Incoming Webhook Url",
Placeholder: "Insert your Slack webhook URL here.",
DbField: "Host",
}}},
}
AddNotifier(testNotifier)
AllCommunications = Load()
assert.Len(t, AllCommunications, 1)
}
func TestIsInDatabase(t *testing.T) {
in := testNotifier.IsInDatabase()
assert.False(t, in)
in := example.isInDatabase()
assert.True(t, in)
}
func TestInsertDatabase(t *testing.T) {
newId, err := InsertDatabase(testNotifier.Notification)
_, err := insertDatabase(example.Notification)
assert.Nil(t, err)
assert.NotZero(t, newId)
assert.NotZero(t, example.Id)
in := testNotifier.IsInDatabase()
in := example.isInDatabase()
assert.True(t, in)
}
func TestSelectNotification(t *testing.T) {
notifier, err := SelectNotification(999999)
notifier, err := SelectNotification(EXAMPLE_ID)
assert.Nil(t, err)
assert.Equal(t, "tester", notifier.Method)
assert.Equal(t, "example", notifier.Method)
assert.False(t, notifier.Enabled)
}
func TestNotification_Update(t *testing.T) {
notifier, err := SelectNotification(999999)
notifier, err := SelectNotification(EXAMPLE_ID)
assert.Nil(t, err)
notifier.Method = "updatedName"
notifier.Host = "new host here"
notifier.Enabled = true
updated, err := notifier.Update()
assert.Nil(t, err)
selected, err := SelectNotification(updated.Id)
selected, err := SelectNotification(updated.Method)
assert.Nil(t, err)
assert.Equal(t, "updatedName", selected.Method)
assert.Equal(t, "new host here", selected.GetValue("host"))
assert.True(t, selected.Enabled)
}
func TestNotification_GetValue(t *testing.T) {
notifier, err := SelectNotification(999999)
notifier, err := SelectNotification(EXAMPLE_ID)
assert.Nil(t, err)
val := notifier.GetValue("Host")
assert.Equal(t, "0.0.0.0", val)
assert.Equal(t, "http://exmaplehost.com", val)
}
func TestRun(t *testing.T) {
err := testNotifier.Run()
assert.Equal(t, "running", err.Error())
}
//func TestRun(t *testing.T) {
// err := example.Run()
// assert.Equal(t, "running", err.Error())
//}
func TestTestIt(t *testing.T) {
err := testNotifier.Test()
assert.Equal(t, "testing", err.Error())
}
//func TestTestIt(t *testing.T) {
// err := example.Test()
// assert.Equal(t, "testing", err.Error())
//}
func TestOnSuccess(t *testing.T) {
s := &types.Service{

View File

@ -18,7 +18,7 @@ package core
import (
"encoding/json"
"fmt"
"github.com/hunterlong/statup/notifiers"
"github.com/hunterlong/statup/core/notifier"
"github.com/hunterlong/statup/types"
"github.com/hunterlong/statup/utils"
"sort"
@ -30,6 +30,10 @@ type Service struct {
*types.Service
}
func (s *Service) Select() *types.Service {
return s.Service
}
func ReturnService(s *types.Service) *Service {
return &Service{s}
}
@ -37,7 +41,7 @@ func ReturnService(s *types.Service) *Service {
// SelectService returns a *core.Service from in memory
func SelectService(id int64) *Service {
for _, s := range CoreApp.Services {
if s.(*Service).Id == id {
if s.Select().Id == id {
return s.(*Service)
}
}
@ -58,7 +62,7 @@ func (c *Core) SelectAllServices() ([]*Service, error) {
service.AllFailures()
CoreApp.Services = append(CoreApp.Services, service)
}
reorderServices()
sort.Sort(ServiceOrder(CoreApp.Services))
return services, db.Error
}
@ -265,7 +269,7 @@ func (u *Service) Delete() error {
slice := CoreApp.Services
CoreApp.Services = append(slice[:i], slice[i+1:]...)
reorderServices()
notifiers.OnDeletedService(u.Service)
notifier.OnDeletedService(u.Service)
return err.Error
}
@ -289,7 +293,7 @@ func (u *Service) Update(restart bool) error {
}
reorderServices()
updateService(u)
notifiers.OnUpdatedService(u.Service)
notifier.OnUpdatedService(u.Service)
return err.Error
}
@ -305,7 +309,7 @@ func (u *Service) Create() (int64, error) {
go u.CheckQueue(true)
CoreApp.Services = append(CoreApp.Services, u)
reorderServices()
notifiers.OnNewService(u.Service)
notifier.OnNewService(u.Service)
return u.Id, nil
}
@ -318,7 +322,7 @@ func (c *Core) ServicesCount() int {
func (c *Core) CountOnline() int {
amount := 0
for _, s := range CoreApp.Services {
if s.(*Service).Online {
if s.Select().Online {
amount++
}
}

View File

@ -133,7 +133,7 @@ INSERT INTO failures (issue,method,service,created_at) VALUES
('HTTP Status Code 200 did not match 0','',6,'2018-08-31 10:42:14'),
('HTTP Status Code 200 did not match 0','',6,'2018-08-31 10:42:14'),
('HTTP Status Code 200 did not match 0','',18,'2018-08-31 10:42:14');
INSERT INTO communication (id,method,host,port,username,password,var1,var2,api_key,api_secret,enabled,removable,limits,created_at) VALUES
INSERT INTO notifications (id,method,host,port,username,password,var1,var2,api_key,api_secret,enabled,removable,limits,created_at) VALUES
(1,'email','smtp.emailer.com',587,'exampleuser','password123','info@betatude.com','sendto@gmail.com','','',1,0,7,'2018-08-31 10:42:15'),
(2,'slack','https://webhooksurl.slack.com/***',0,'','','','','','',0,0,3,'2018-08-31 10:42:08'),
(3,'twilio','',0,'','','','','','',0,0,3,'2018-08-31 10:42:08');

View File

@ -133,7 +133,7 @@ INSERT INTO failures (issue,method,service,created_at) VALUES
('HTTP Status Code 200 did not match 0','',6,'2018-08-31 10:42:14.271162743-07:00'),
('HTTP Status Code 200 did not match 0','',6,'2018-08-31 10:42:14.272209564-07:00'),
('HTTP Status Code 200 did not match 0','',18,'2018-08-31 10:42:14.271162743-07:00');
INSERT INTO communication (id,method,host,port,username,password,var1,var2,api_key,api_secret,enabled,removable,limits,created_at) VALUES
INSERT INTO notifications (id,method,host,port,username,password,var1,var2,api_key,api_secret,enabled,removable,limits,created_at) VALUES
(1,'email','smtp.emailer.com',587,'exampleuser','password123','info@betatude.com','sendto@gmail.com','','',true,false,7,'2018-08-31 10:42:15.000829706-07:00'),
(2,'slack','https://webhooksurl.slack.com/***',0,'','','','','','',false,false,3,'2018-08-31 10:42:08.775366824-07:00'),
(3,'twilio','',0,'','','','','','',false,false,3,'2018-08-31 10:42:08.776944923-07:00');

View File

@ -133,7 +133,7 @@ INSERT INTO failures (issue,method,service,created_at) VALUES
('HTTP Status Code 200 did not match 0','',6,'2018-08-31 10:42:14.271162743-07:00'),
('HTTP Status Code 200 did not match 0','',6,'2018-08-31 10:42:14.272209564-07:00'),
('HTTP Status Code 200 did not match 0','',18,'2018-08-31 10:42:14.271162743-07:00');
INSERT INTO communication (id,method,host,port,username,password,var1,var2,api_key,api_secret,enabled,removable,limits,created_at) VALUES
INSERT INTO notifications (id,method,host,port,username,password,var1,var2,api_key,api_secret,enabled,removable,limits,created_at) VALUES
(1,'email','smtp.emailer.com',587,'exampleuser','password123','info@betatude.com','sendto@gmail.com','','',1,0,7,'2018-08-31 10:42:15.000829706-07:00'),
(2,'slack','https://webhooksurl.slack.com/***',0,'','','','','','',0,0,3,'2018-08-31 10:42:08.775366824-07:00'),
(3,'twilio','',0,'','','','','','',0,0,3,'2018-08-31 10:42:08.776944923-07:00');

View File

@ -19,7 +19,7 @@ import (
"fmt"
"github.com/gorilla/mux"
"github.com/hunterlong/statup/core"
"github.com/hunterlong/statup/notifiers"
"github.com/hunterlong/statup/core/notifier"
"github.com/hunterlong/statup/source"
"github.com/hunterlong/statup/utils"
"net/http"
@ -147,7 +147,7 @@ func SaveNotificationHandler(w http.ResponseWriter, r *http.Request) {
apiSecret := form.Get("api_secret")
limits := int(utils.StringInt(form.Get("limits")))
notifer, err := notifiers.SelectNotifier(method)
notifer, 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")
@ -186,6 +186,6 @@ func SaveNotificationHandler(w http.ResponseWriter, r *http.Request) {
if err != nil {
utils.Log(3, fmt.Sprintf("issue updating notifier: %v", err))
}
notifiers.OnSave(notifer.Method)
notifier.OnSave(notifer.Method)
ExecuteResponse(w, r, "settings.html", core.CoreApp, "/settings")
}

98
notifiers/discord.go Normal file
View File

@ -0,0 +1,98 @@
// 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 notifiers
import (
"bytes"
"fmt"
"github.com/hunterlong/statup/core/notifier"
"github.com/hunterlong/statup/types"
"github.com/hunterlong/statup/utils"
"net/http"
)
const (
DISCORD_METHOD = "discord"
DISCORD_TEST = `{"content": "This is a notification from Statup!"}`
)
type Discord struct {
*notifier.Notification
}
var discorder = &Discord{&notifier.Notification{
Method: DISCORD_METHOD,
Host: "https://discordapp.com/api/webhooks/****/*****",
Form: []notifier.NotificationForm{{
Type: "text",
Title: "Discord Webhook URL",
Placeholder: "Insert your webhook URL here",
DbField: "host",
}}},
}
// init the Discord notifier
func init() {
err := notifier.AddNotifier(discorder)
if err != nil {
panic(err)
}
}
func (u *Discord) Test() error {
utils.Log(1, "Discord notifier loaded")
discordPost([]byte(DISCORD_TEST))
return nil
}
// Discord won't be using the Run() process
func (u *Discord) Run() error {
return nil
}
// Discord won't be using the Run() process
func (u *Discord) Select() *notifier.Notification {
return u.Notification
}
// discordPost sends an HTTP POST to the webhook URL
func discordPost(msg []byte) {
req, _ := http.NewRequest("POST", discorder.GetValue("host"), bytes.NewBuffer(msg))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
utils.Log(3, fmt.Sprintf("issue sending Discord message to channel: %v", err))
return
}
defer resp.Body.Close()
discorder.Log(string(msg))
}
func (u *Discord) OnFailure(s *types.Service, f *types.Failure) {
msg := fmt.Sprintf(`{"content": "Your service '%v' is currently failing! Reason: %v"}`, s.Name, f.Issue)
discordPost([]byte(msg))
}
func (u *Discord) OnSuccess(s *types.Service) {
}
func (u *Discord) OnSave() error {
msg := fmt.Sprintf(`{"content": "The Discord notifier on Statup was just updated."}`)
discordPost([]byte(msg))
return nil
}

View File

@ -17,9 +17,9 @@ package notifiers
import (
"bytes"
"crypto/tls"
"fmt"
"github.com/GeertJohan/go.rice"
"github.com/hunterlong/statup/core/notifier"
"github.com/hunterlong/statup/types"
"github.com/hunterlong/statup/utils"
"gopkg.in/gomail.v2"
@ -33,7 +33,6 @@ const (
)
var (
emailer *Email
emailArray []string
emailQueue []*EmailOutgoing
emailBox *rice.Box
@ -41,78 +40,53 @@ var (
)
type Email struct {
*Notification
*notifier.Notification
}
var emailer = &Email{
Notification: &notifier.Notification{
Method: EMAIL_METHOD,
Form: []notifier.NotificationForm{{
Type: "text",
Title: "SMTP Host",
Placeholder: "Insert your SMTP Host here.",
DbField: "Host",
}, {
Type: "text",
Title: "SMTP Username",
Placeholder: "Insert your SMTP Username here.",
DbField: "Username",
}, {
Type: "password",
Title: "SMTP Password",
Placeholder: "Insert your SMTP Password here.",
DbField: "Password",
}, {
Type: "number",
Title: "SMTP Port",
Placeholder: "Insert your SMTP Port here.",
DbField: "Port",
}, {
Type: "text",
Title: "Outgoing Email Address",
Placeholder: "Insert your Outgoing Email Address",
DbField: "Var1",
}, {
Type: "email",
Title: "Send Alerts To",
Placeholder: "Email Address",
DbField: "Var2",
}},
}}
// DEFINE YOUR NOTIFICATION HERE.
func init() {
emailer = &Email{
Notification: &Notification{
Id: EMAIL_ID,
Method: EMAIL_METHOD,
Form: []NotificationForm{{
Type: "text",
Title: "SMTP Host",
Placeholder: "Insert your SMTP Host here.",
DbField: "Host",
}, {
Type: "text",
Title: "SMTP Username",
Placeholder: "Insert your SMTP Username here.",
DbField: "Username",
}, {
Type: "password",
Title: "SMTP Password",
Placeholder: "Insert your SMTP Password here.",
DbField: "Password",
}, {
Type: "number",
Title: "SMTP Port",
Placeholder: "Insert your SMTP Port here.",
DbField: "Port",
}, {
Type: "text",
Title: "Outgoing Email Address",
Placeholder: "Insert your Outgoing Email Address",
DbField: "Var1",
}, {
Type: "email",
Title: "Send Alerts To",
Placeholder: "Email Address",
DbField: "Var2",
}},
}}
err := AddNotifier(emailer)
err := notifier.AddNotifier(emailer)
if err != nil {
utils.Log(3, err)
panic(err)
}
}
// WHEN NOTIFIER LOADS
func (u *Email) Init() error {
emailBox = rice.MustFindBox("emails")
err := u.Install()
utils.Log(1, fmt.Sprintf("Creating Mailer: %v:%v", u.Notification.Host, u.Notification.Port))
if err == nil {
notifier, _ := SelectNotification(u.Id)
forms := u.Form
u.Notification = notifier
u.Form = forms
if u.Enabled {
utils.Log(1, fmt.Sprintf("Loading SMTP Emailer using host: %v:%v", u.Notification.Host, u.Notification.Port))
mailer = gomail.NewPlainDialer(u.Notification.Host, u.Notification.Port, u.Notification.Username, u.Notification.Password)
mailer.TLSConfig = &tls.Config{InsecureSkipVerify: true}
go u.Run()
}
}
return nil
}
func (u *Email) Test() error {
utils.Log(1, "Emailer notifier loaded")
if u.Enabled {
@ -200,20 +174,6 @@ func (u *Email) OnSave() error {
return nil
}
// ON SERVICE FAILURE, DO YOUR OWN FUNCTIONS
func (u *Email) Install() error {
inDb := emailer.Notification.IsInDatabase()
if !inDb {
newNotifer, err := InsertDatabase(u.Notification)
if err != nil {
utils.Log(3, err)
return err
}
utils.Log(1, fmt.Sprintf("new notifier #%v installed: %v", newNotifer, u.Method))
}
return nil
}
func (u *Email) dialSend(email *EmailOutgoing) error {
m := gomail.NewMessage()
m.SetHeader("From", email.From)

View File

@ -17,6 +17,7 @@ package notifiers
import (
"fmt"
"github.com/hunterlong/statup/core/notifier"
"github.com/hunterlong/statup/types"
"github.com/hunterlong/statup/utils"
"net/http"
@ -31,27 +32,26 @@ const (
)
var (
lineNotify *LineNotify
lineNotifyMessages []string
)
type LineNotify struct {
*Notification
*notifier.Notification
}
var lineNotify = &LineNotify{&notifier.Notification{
Method: LINE_NOTIFY_METHOD,
Form: []notifier.NotificationForm{{
Type: "text",
Title: "Access Token",
Placeholder: "Insert your Line Notify Access Token here.",
DbField: "api_secret",
}}},
}
// DEFINE YOUR NOTIFICATION HERE.
func init() {
lineNotify = &LineNotify{&Notification{
Id: LINE_NOTIFY_ID,
Method: LINE_NOTIFY_METHOD,
Form: []NotificationForm{{
Type: "text",
Title: "Access Token",
Placeholder: "Insert your Line Notify Access Token here.",
DbField: "api_secret",
}}},
}
err := AddNotifier(lineNotify)
err := notifier.AddNotifier(lineNotify)
if err != nil {
utils.Log(3, err)
}
@ -61,22 +61,6 @@ func (u *LineNotify) postUrl() string {
return fmt.Sprintf("https://notify-api.line.me/api/notify")
}
// WHEN NOTIFIER LOADS
func (u *LineNotify) Init() error {
err := u.Install()
if err == nil {
notifier, _ := SelectNotification(u.Id)
forms := u.Form
u.Notification = notifier
u.Form = forms
if u.Enabled {
go u.Run()
}
}
return err
}
func (u *LineNotify) Test() error {
msg := fmt.Sprintf("You're Statup Line Notify Notifier is working correctly!")
SendLineNotify(msg)
@ -85,7 +69,7 @@ func (u *LineNotify) Test() error {
// AFTER NOTIFIER LOADS, IF ENABLED, START A QUEUE PROCESS
func (u *LineNotify) Run() error {
lineNotifyMessages = uniqueStrings(lineNotifyMessages)
lineNotifyMessages = notifier.UniqueStrings(lineNotifyMessages)
for _, msg := range lineNotifyMessages {
if u.CanSend() {
@ -142,18 +126,3 @@ func (u *LineNotify) OnSave() error {
// Do updating stuff here
return nil
}
// ON SERVICE FAILURE, DO YOUR OWN FUNCTIONS
func (u *LineNotify) Install() error {
var err error
inDb := lineNotify.Notification.IsInDatabase()
if !inDb {
newNotifer, err := InsertDatabase(u.Notification)
if err != nil {
utils.Log(3, err)
return err
}
utils.Log(1, fmt.Sprintf("new notifier #%v installed: %v", newNotifer, u.Method))
}
return err
}

View File

@ -18,6 +18,7 @@ package notifiers
import (
"bytes"
"fmt"
"github.com/hunterlong/statup/core/notifier"
"github.com/hunterlong/statup/types"
"github.com/hunterlong/statup/utils"
"net/http"
@ -35,13 +36,12 @@ const (
)
var (
slacker *Slack
slackMessages []string
messageLock *sync.Mutex
)
type Slack struct {
*Notification
*notifier.Notification
}
type slackMessage struct {
@ -49,40 +49,24 @@ type slackMessage struct {
Time int64
}
// DEFINE YOUR NOTIFICATION HERE.
func init() {
slacker = &Slack{&Notification{
Id: SLACK_ID,
Method: SLACK_METHOD,
Host: "https://webhooksurl.slack.com/***",
Form: []NotificationForm{{
Type: "text",
Title: "Incoming Webhook Url",
Placeholder: "Insert your Slack webhook URL here.",
DbField: "Host",
}}},
}
messageLock = new(sync.Mutex)
err := AddNotifier(slacker)
if err != nil {
utils.Log(3, err)
}
var slacker = &Slack{&notifier.Notification{
Method: SLACK_METHOD,
Host: "https://webhooksurl.slack.com/***",
Form: []notifier.NotificationForm{{
Type: "text",
Title: "Incoming Webhook Url",
Placeholder: "Insert your Slack webhook URL here.",
DbField: "Host",
}}},
}
// WHEN NOTIFIER LOADS
func (u *Slack) Init() error {
err := u.Install()
if err == nil {
notifier, _ := SelectNotification(u.Id)
forms := u.Form
u.Notification = notifier
u.Form = forms
if u.Enabled {
go u.Run()
}
// DEFINE YOUR NOTIFICATION HERE.
func init() {
err := notifier.AddNotifier(slacker)
messageLock = new(sync.Mutex)
if err != nil {
panic(err)
}
return err
}
func (u *Slack) Test() error {
@ -95,7 +79,7 @@ func (u *Slack) Test() error {
// AFTER NOTIFIER LOADS, IF ENABLED, START A QUEUE PROCESS
func (u *Slack) Run() error {
messageLock.Lock()
slackMessages = uniqueStrings(slackMessages)
slackMessages = notifier.UniqueStrings(slackMessages)
for _, msg := range slackMessages {
if u.CanSend() {
@ -125,6 +109,7 @@ func SendSlack(temp string, data interface{}) error {
slackTemp.Execute(buf, data)
slackMessages = append(slackMessages, buf.String())
messageLock.Unlock()
slacker.Log(buf.String())
return nil
}
@ -151,17 +136,3 @@ func (u *Slack) OnSave() error {
u.Test()
return nil
}
// ON SERVICE FAILURE, DO YOUR OWN FUNCTIONS
func (u *Slack) Install() error {
inDb := slacker.Notification.IsInDatabase()
if !inDb {
newNotifer, err := InsertDatabase(u.Notification)
if err != nil {
utils.Log(3, err)
return err
}
utils.Log(1, fmt.Sprintf("new notifier #%v installed: %v", newNotifer, u.Method))
}
return nil
}

View File

@ -17,6 +17,7 @@ package notifiers
import (
"fmt"
"github.com/hunterlong/statup/core/notifier"
"github.com/hunterlong/statup/types"
"github.com/hunterlong/statup/utils"
"net/http"
@ -31,49 +32,43 @@ const (
)
var (
twilio *Twilio
twilioMessages []string
)
type Twilio struct {
*Notification
*notifier.Notification
}
type twilioMessage struct {
Service *types.Service
Time int64
var twilio = &Twilio{&notifier.Notification{
Method: TWILIO_METHOD,
Form: []notifier.NotificationForm{{
Type: "text",
Title: "Account Sid",
Placeholder: "Insert your Twilio Account Sid",
DbField: "api_key",
}, {
Type: "text",
Title: "Account Token",
Placeholder: "Insert your Twilio Account Token",
DbField: "api_secret",
}, {
Type: "text",
Title: "SMS to Phone Number",
Placeholder: "+18555555555",
DbField: "Var1",
}, {
Type: "text",
Title: "From Phone Number",
Placeholder: "+18555555555",
DbField: "Var2",
}}},
}
// DEFINE YOUR NOTIFICATION HERE.
func init() {
twilio = &Twilio{&Notification{
Id: TWILIO_ID,
Method: TWILIO_METHOD,
Form: []NotificationForm{{
Type: "text",
Title: "Account Sid",
Placeholder: "Insert your Twilio Account Sid",
DbField: "api_key",
}, {
Type: "text",
Title: "Account Token",
Placeholder: "Insert your Twilio Account Token",
DbField: "api_secret",
}, {
Type: "text",
Title: "SMS to Phone Number",
Placeholder: "+18555555555",
DbField: "Var1",
}, {
Type: "text",
Title: "From Phone Number",
Placeholder: "+18555555555",
DbField: "Var2",
}}},
}
err := AddNotifier(twilio)
err := notifier.AddNotifier(twilio)
if err != nil {
utils.Log(3, err)
panic(err)
}
}
@ -81,22 +76,6 @@ func (u *Twilio) postUrl() string {
return fmt.Sprintf("https://api.twilio.com/2010-04-01/Accounts/%v/Messages.json", u.ApiKey)
}
// WHEN NOTIFIER LOADS
func (u *Twilio) Init() error {
err := u.Install()
if err == nil {
notifier, _ := SelectNotification(u.Id)
forms := u.Form
u.Notification = notifier
u.Form = forms
if u.Enabled {
go u.Run()
}
}
return err
}
func (u *Twilio) Test() error {
utils.Log(1, "Twilio notifier loaded")
msg := fmt.Sprintf("You're Statup Twilio Notifier is working correctly!")
@ -106,7 +85,7 @@ func (u *Twilio) Test() error {
// AFTER NOTIFIER LOADS, IF ENABLED, START A QUEUE PROCESS
func (u *Twilio) Run() error {
twilioMessages = uniqueStrings(twilioMessages)
twilioMessages = notifier.UniqueStrings(twilioMessages)
for _, msg := range twilioMessages {
if u.CanSend() {
@ -167,17 +146,3 @@ func (u *Twilio) OnSave() error {
return nil
}
// ON SERVICE FAILURE, DO YOUR OWN FUNCTIONS
func (u *Twilio) Install() error {
inDb := twilio.Notification.IsInDatabase()
if !inDb {
newNotifer, err := InsertDatabase(u.Notification)
if err != nil {
utils.Log(3, err)
return err
}
utils.Log(1, fmt.Sprintf("new notifier #%v installed: %v", newNotifer, u.Method))
}
return nil
}

View File

@ -31,7 +31,7 @@
<div class="nav flex-column nav-pills" id="v-pills-tab" role="tablist" aria-orientation="vertical">
<a class="nav-link active" id="v-pills-home-tab" data-toggle="pill" href="#v-pills-home" role="tab" aria-controls="v-pills-home" aria-selected="true">Settings</a>
<a class="nav-link" id="v-pills-style-tab" data-toggle="pill" href="#v-pills-style" role="tab" aria-controls="v-pills-style" aria-selected="false">Theme Editor</a>
{{ range .Communications }}
{{ range .Notifications }}
<a class="nav-link text-capitalize" id="v-pills-{{underscore .Select.Method}}-tab" data-toggle="pill" href="#v-pills-{{underscore .Select.Method}}" role="tab" aria-controls="v-pills-{{underscore .Select.Method}}" aria-selected="false">{{.Select.Method}}</a>
{{ end }}
<a class="nav-link" id="v-pills-browse-tab" data-toggle="pill" href="#v-pills-browse" role="tab" aria-controls="v-pills-home" aria-selected="false">Browse Plugins</a>
@ -132,7 +132,7 @@
{{end}}
</div>
{{ range .Communications }}
{{ range .Notifications }}
{{$n := .Select}}
<div class="tab-pane" id="v-pills-{{underscore $n.Method}}" role="tabpanel" aria-labelledby="v-pills-{{underscore $n.Method }}-tab">
<form method="POST" action="/settings/notifier/{{ $n.Method }}">
@ -145,7 +145,7 @@
<div class="form-group">
<label class="text-capitalize" for="limits_per_hour_{{underscore $n.Method }}">Limits per Hour</label>
<input type="number" name="limits" class="form-control" value="{{$n.LimitValue}}" id="limits_per_hour_{{underscore $n.Method }}" min="1" max="60" placeholder="How many messages can send per hour">
<input type="number" name="limits" class="form-control" value="{{$n.Limits}}" id="limits_per_hour_{{underscore $n.Method }}" min="1" max="60" placeholder="How many messages can send per hour">
</div>
<div class="form-group row">

View File

@ -4,31 +4,33 @@ import (
"time"
)
type AllNotifiers interface{}
// Core struct contains all the required fields for Statup. All application settings
// will be saved into 1 row in the 'core' table. You can use the core.CoreApp
// global variable to interact with the attributes to the application, such as services.
type Core struct {
Name string `gorm:"not null;column:name" json:"name"`
Description string `gorm:"not null;column:description" json:"description,omitempty"`
Config string `gorm:"column:config" json:"-"`
ApiKey string `gorm:"column:api_key" json:"-"`
ApiSecret string `gorm:"column:api_secret" json:"-"`
Style string `gorm:"not null;column:style" json:"style,omitempty"`
Footer string `gorm:"not null;column:footer" json:"footer,omitempty"`
Domain string `gorm:"not null;column:domain" json:"domain,omitempty"`
Version string `gorm:"column:version" json:"version"`
MigrationId int64 `gorm:"column:migration_id" json:"migration_id,omitempty"`
UseCdn bool `gorm:"column:use_cdn;default:false" json:"using_cdn,omitempty"`
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
DbConnection string `gorm:"-" json:"database"`
Started time.Time `gorm:"-" json:"started_on"`
Services []ServiceInterface `gorm:"-" json:"services,omitempty"`
Plugins []Info `gorm:"-" json:"-"`
Repos []PluginJSON `gorm:"-" json:"-"`
AllPlugins []PluginActions `gorm:"-" json:"-"`
Communications []AllNotifiers `gorm:"-" json:"-"`
CoreInterface `gorm:"-" json:"-"`
Name string `gorm:"not null;column:name" json:"name"`
Description string `gorm:"not null;column:description" json:"description,omitempty"`
Config string `gorm:"column:config" json:"-"`
ApiKey string `gorm:"column:api_key" json:"-"`
ApiSecret string `gorm:"column:api_secret" json:"-"`
Style string `gorm:"not null;column:style" json:"style,omitempty"`
Footer string `gorm:"not null;column:footer" json:"footer,omitempty"`
Domain string `gorm:"not null;column:domain" json:"domain,omitempty"`
Version string `gorm:"column:version" json:"version"`
MigrationId int64 `gorm:"column:migration_id" json:"migration_id,omitempty"`
UseCdn bool `gorm:"column:use_cdn;default:false" json:"using_cdn,omitempty"`
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
DbConnection string `gorm:"-" json:"database"`
Started time.Time `gorm:"-" json:"started_on"`
Services []ServiceInterface `gorm:"-" json:"services,omitempty"`
Plugins []Info `gorm:"-" json:"-"`
Repos []PluginJSON `gorm:"-" json:"-"`
AllPlugins []PluginActions `gorm:"-" json:"-"`
Notifications []AllNotifiers `gorm:"-" json:"-"`
CoreInterface `gorm:"-" json:"-"`
}
type CoreInterface interface {

View File

@ -20,75 +20,83 @@ import (
)
type Service struct {
Id int64 `gorm:"primary_key;column:id" json:"id"`
Name string `gorm:"column:name" json:"name"`
Domain string `gorm:"column:domain" json:"domain"`
Expected string `gorm:"not null;column:expected" json:"expected"`
ExpectedStatus int `gorm:"default:200;column:expected_status" json:"expected_status"`
Interval int `gorm:"default:30;column:check_interval" json:"check_interval"`
Type string `gorm:"column:check_type" json:"type"`
Method string `gorm:"column:method" json:"method"`
PostData string `gorm:"not null;column:post_data" json:"post_data"`
Port int `gorm:"not null;column:port" json:"port"`
Timeout int `gorm:"default:30;column:timeout" json:"timeout"`
Order int `gorm:"default:0;column:order_id" json:"order_id"`
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
Online bool `gorm:"-" json:"online"`
Latency float64 `gorm:"-" json:"latency"`
Online24Hours float32 `gorm:"-" json:"24_hours_online"`
AvgResponse string `gorm:"-" json:"avg_response"`
Failures []interface{} `gorm:"-" json:"failures"`
Checkins []*Checkin `gorm:"-" json:"checkins"`
Running chan bool `gorm:"-" json:"-"`
Checkpoint time.Time `gorm:"-" json:"-"`
SleepDuration time.Duration `gorm:"-" json:"-"`
LastResponse string `gorm:"-" json:"-"`
LastStatusCode int `gorm:"-" json:"status_code"`
LastOnline time.Time `gorm:"-" json:"last_online"`
DnsLookup float64 `gorm:"-" json:"dns_lookup_time"`
ServiceInterface `gorm:"-" json:"-"`
Id int64 `gorm:"primary_key;column:id" json:"id"`
Name string `gorm:"column:name" json:"name"`
Domain string `gorm:"column:domain" json:"domain"`
Expected string `gorm:"not null;column:expected" json:"expected"`
ExpectedStatus int `gorm:"default:200;column:expected_status" json:"expected_status"`
Interval int `gorm:"default:30;column:check_interval" json:"check_interval"`
Type string `gorm:"column:check_type" json:"type"`
Method string `gorm:"column:method" json:"method"`
PostData string `gorm:"not null;column:post_data" json:"post_data"`
Port int `gorm:"not null;column:port" json:"port"`
Timeout int `gorm:"default:30;column:timeout" json:"timeout"`
Order int `gorm:"default:0;column:order_id" json:"order_id"`
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
Online bool `gorm:"-" json:"online"`
Latency float64 `gorm:"-" json:"latency"`
Online24Hours float32 `gorm:"-" json:"24_hours_online"`
AvgResponse string `gorm:"-" json:"avg_response"`
Failures []interface{} `gorm:"-" json:"failures"`
Checkins []*Checkin `gorm:"-" json:"checkins"`
Running chan bool `gorm:"-" json:"-"`
Checkpoint time.Time `gorm:"-" json:"-"`
SleepDuration time.Duration `gorm:"-" json:"-"`
LastResponse string `gorm:"-" json:"-"`
LastStatusCode int `gorm:"-" json:"status_code"`
LastOnline time.Time `gorm:"-" json:"last_online"`
DnsLookup float64 `gorm:"-" json:"dns_lookup_time"`
}
type ServiceInterface interface {
// Database functions
Select() *Service
CheckQueue(bool)
Check(bool)
Create() (int64, error)
Update(bool) error
Delete() error
// Basic Method functions
AvgTime() float64
OnlineSince(time.Time) float32
Online24() float32
SmallText() string
GraphData() string
AvgUptime(time.Time) string
AvgUptime24() string
ToJSON() string
// Failure functions
CreateFailure(*Failure) (int64, error)
//LimitedFailures() []interface{}
//AllFailures() []*Failure
TotalFailuresSince(time.Time) (uint64, error)
TotalFailures24() (uint64, error)
TotalFailures() (uint64, error)
DeleteFailures()
// Hits functions (successful responses)
CreateHit(*Hit) (int64, error)
Hits() ([]*Hit, error)
TotalHits() (uint64, error)
TotalHitsSince(time.Time) (uint64, error)
Sum() (float64, error)
LimitedHits() ([]*Hit, error)
SelectHitsGroupBy(string) ([]*Hit, error)
// Go Routines
CheckQueue(bool)
Check(bool)
//checkHttp(bool) *Service
//checkTcp(bool) *Service
// Checkin functions
AllCheckins() []*Checkin
}
//type ServiceInterface interface {
// // Database functions
// Create() (int64, error)
// Update(bool) error
// Delete() error
// // Basic Method functions
// AvgTime() float64
// OnlineSince(time.Time) float32
// Online24() float32
// SmallText() string
// GraphData() string
// AvgUptime(time.Time) string
// AvgUptime24() string
// ToJSON() string
// // Failure functions
// CreateFailure(*Failure) (int64, error)
// //LimitedFailures() []interface{}
// //AllFailures() []*Failure
// TotalFailuresSince(time.Time) (uint64, error)
// TotalFailures24() (uint64, error)
// TotalFailures() (uint64, error)
// DeleteFailures()
// // Hits functions (successful responses)
// CreateHit(*Hit) (int64, error)
// Hits() ([]*Hit, error)
// TotalHits() (uint64, error)
// TotalHitsSince(time.Time) (uint64, error)
// Sum() (float64, error)
// LimitedHits() ([]*Hit, error)
// SelectHitsGroupBy(string) ([]*Hit, error)
// // Go Routines
// CheckQueue(bool)
// Check(bool)
// //checkHttp(bool) *Service
// //checkTcp(bool) *Service
// // Checkin functions
// //AllCheckins() []*Checkin
//}
// Start will create a channel for the service checking go routine
func (s *Service) Start() {
s.Running = make(chan bool)

View File

@ -21,12 +21,10 @@ import (
"time"
)
type AllNotifiers interface{}
// Hit struct is a 'successful' ping or web response entry for a service.
type Hit struct {
Id int64 `gorm:"primary_key;column:id"`
Service int64 `gorm:"index;column:service"`
Service int64 `gorm:"column:service"`
Latency float64 `gorm:"column:latency"`
CreatedAt time.Time `gorm:"column:created_at"`
}