mirror of https://github.com/statping/statping
notifier testing - doc.go added - timezone column
parent
e39bf6ef0a
commit
f144b8cec1
|
@ -0,0 +1,12 @@
|
||||||
|
// Package main for building the Statup CLI binary application. This package
|
||||||
|
// connects to all the other packages to make a runnable binary for multiple
|
||||||
|
// operating system.
|
||||||
|
//
|
||||||
|
// To build Statup from source, run the follow command in the root directory:
|
||||||
|
// // go build -o statup ./cmd
|
||||||
|
//
|
||||||
|
// Remember that you'll need to compile the static assets using Rice:
|
||||||
|
// // cd source && rice embed-go
|
||||||
|
//
|
||||||
|
// by Hunter Long
|
||||||
|
package main
|
|
@ -0,0 +1,6 @@
|
||||||
|
// Package core contains the main functionality of Statup. This includes everything for
|
||||||
|
// Services, Hits, Failures, Users, service checking mechanisms, databases, and notifiers
|
||||||
|
// in the notifier package
|
||||||
|
//
|
||||||
|
// by Hunter Long
|
||||||
|
package core
|
|
@ -262,7 +262,8 @@ CheckNotifier:
|
||||||
case <-time.After(rateLimit):
|
case <-time.After(rateLimit):
|
||||||
notification = n.Select()
|
notification = n.Select()
|
||||||
if len(notification.Queue) > 0 {
|
if len(notification.Queue) > 0 {
|
||||||
if notification.WithinLimits() {
|
ok, _ := notification.WithinLimits()
|
||||||
|
if ok {
|
||||||
msg := notification.Queue[0]
|
msg := notification.Queue[0]
|
||||||
err := n.Send(msg)
|
err := n.Send(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -371,23 +372,27 @@ func isEnabled(n interface{}) bool {
|
||||||
|
|
||||||
func inLimits(n interface{}) bool {
|
func inLimits(n interface{}) bool {
|
||||||
notifier := n.(Notifier).Select()
|
notifier := n.(Notifier).Select()
|
||||||
return notifier.WithinLimits()
|
ok, _ := notifier.WithinLimits()
|
||||||
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (notify *Notification) WithinLimits() bool {
|
func (notify *Notification) WithinLimits() (bool, error) {
|
||||||
|
if notify.SentLastMinute() == 0 {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
if notify.SentLastMinute() >= notify.Limits {
|
if notify.SentLastMinute() >= notify.Limits {
|
||||||
return false
|
return false, errors.New(fmt.Sprintf("notifier sent %v out of %v in last minute", notify.SentLastMinute(), notify.Limits))
|
||||||
}
|
}
|
||||||
if notify.Delay.Seconds() == 0 {
|
if notify.Delay.Seconds() == 0 {
|
||||||
notify.Delay = time.Duration(500 * time.Millisecond)
|
notify.Delay = time.Duration(500 * time.Millisecond)
|
||||||
}
|
}
|
||||||
if notify.LastSent().Seconds() == 0 {
|
if notify.LastSent().Seconds() == 0 {
|
||||||
return true
|
return true, nil
|
||||||
}
|
}
|
||||||
if notify.Delay.Seconds() >= notify.LastSent().Seconds() {
|
if notify.Delay.Seconds() >= notify.LastSent().Seconds() {
|
||||||
return false
|
return false, errors.New(fmt.Sprintf("notifiers delay (%v) is greater than last message sent (%v)", notify.Delay.Seconds(), notify.LastSent().Seconds()))
|
||||||
}
|
}
|
||||||
return true
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Notification) ResetQueue() {
|
func (n *Notification) ResetQueue() {
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
// Package handlers holds all the HTTP requests and routes. All HTTP related
|
||||||
|
// functions are in this package.
|
||||||
|
//
|
||||||
|
// by Hunter Long
|
||||||
|
package handlers
|
|
@ -51,19 +51,7 @@ func Router() *mux.Router {
|
||||||
r.Handle("/charts/{id}.js", http.HandlerFunc(RenderServiceChartHandler))
|
r.Handle("/charts/{id}.js", http.HandlerFunc(RenderServiceChartHandler))
|
||||||
r.Handle("/setup", http.HandlerFunc(SetupHandler)).Methods("GET")
|
r.Handle("/setup", http.HandlerFunc(SetupHandler)).Methods("GET")
|
||||||
r.Handle("/setup", http.HandlerFunc(ProcessSetupHandler)).Methods("POST")
|
r.Handle("/setup", http.HandlerFunc(ProcessSetupHandler)).Methods("POST")
|
||||||
r.Handle("/dashboard", http.HandlerFunc(DashboardHandler)).Methods("GET")
|
r.Handle("/dashboard", http.HandlerFunc(DashboardHandler)).Methods("GET").HandlerFunc(UsersHandler).Methods("GET")
|
||||||
r.Handle("/dashboard", http.HandlerFunc(LoginHandler)).Methods("POST")
|
|
||||||
r.Handle("/logout", http.HandlerFunc(LogoutHandler))
|
|
||||||
r.Handle("/services", http.HandlerFunc(ServicesHandler)).Methods("GET")
|
|
||||||
r.Handle("/services", http.HandlerFunc(CreateServiceHandler)).Methods("POST")
|
|
||||||
r.Handle("/services/reorder", http.HandlerFunc(ReorderServiceHandler)).Methods("POST")
|
|
||||||
r.Handle("/service/{id}", http.HandlerFunc(ServicesViewHandler)).Methods("GET")
|
|
||||||
r.Handle("/service/{id}", http.HandlerFunc(ServicesUpdateHandler)).Methods("POST")
|
|
||||||
r.Handle("/service/{id}/edit", http.HandlerFunc(ServicesViewHandler))
|
|
||||||
r.Handle("/service/{id}/delete", http.HandlerFunc(ServicesDeleteHandler))
|
|
||||||
r.Handle("/service/{id}/delete_failures", http.HandlerFunc(ServicesDeleteFailuresHandler)).Methods("GET")
|
|
||||||
r.Handle("/service/{id}/checkin", http.HandlerFunc(CheckinCreateUpdateHandler)).Methods("POST")
|
|
||||||
r.Handle("/users", http.HandlerFunc(UsersHandler)).Methods("GET")
|
|
||||||
r.Handle("/users", http.HandlerFunc(CreateUserHandler)).Methods("POST")
|
r.Handle("/users", http.HandlerFunc(CreateUserHandler)).Methods("POST")
|
||||||
r.Handle("/user/{id}", http.HandlerFunc(UsersEditHandler)).Methods("GET")
|
r.Handle("/user/{id}", http.HandlerFunc(UsersEditHandler)).Methods("GET")
|
||||||
r.Handle("/user/{id}", http.HandlerFunc(UpdateUserHandler)).Methods("POST")
|
r.Handle("/user/{id}", http.HandlerFunc(UpdateUserHandler)).Methods("POST")
|
||||||
|
|
|
@ -59,7 +59,6 @@ func init() {
|
||||||
|
|
||||||
func (u *Discord) Send(msg interface{}) error {
|
func (u *Discord) Send(msg interface{}) error {
|
||||||
message := msg.([]byte)
|
message := msg.([]byte)
|
||||||
fmt.Println("sending: ", message)
|
|
||||||
req, _ := http.NewRequest("POST", discorder.GetValue("host"), bytes.NewBuffer(message))
|
req, _ := http.NewRequest("POST", discorder.GetValue("host"), bytes.NewBuffer(message))
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
|
@ -76,7 +75,6 @@ func (u *Discord) Select() *notifier.Notification {
|
||||||
|
|
||||||
func (u *Discord) OnFailure(s *types.Service, f *types.Failure) {
|
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)
|
msg := fmt.Sprintf(`{"content": "Your service '%v' is currently failing! Reason: %v"}`, s.Name, f.Issue)
|
||||||
fmt.Println(msg)
|
|
||||||
u.AddQueue(msg)
|
u.AddQueue(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
package notifiers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hunterlong/statup/core/notifier"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DISCORD_URL = os.Getenv("DISCORD_URL")
|
||||||
|
discordMessage = `{"content": "The Discord notifier on Statup has been tested!"}`
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
discorder.Host = DISCORD_URL
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDiscordNotifier(t *testing.T) {
|
||||||
|
if DISCORD_URL == "" {
|
||||||
|
t.Log("Discord notifier testing skipped, missing DISCORD_URL environment variable")
|
||||||
|
t.SkipNow()
|
||||||
|
}
|
||||||
|
currentCount = CountNotifiers()
|
||||||
|
|
||||||
|
t.Run("Load Discord", func(t *testing.T) {
|
||||||
|
discorder.Host = DISCORD_URL
|
||||||
|
discorder.Delay = time.Duration(100 * time.Millisecond)
|
||||||
|
err := notifier.AddNotifier(discorder)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "Hunter Long", discorder.Author)
|
||||||
|
assert.Equal(t, DISCORD_URL, discorder.Host)
|
||||||
|
assert.Equal(t, currentCount+1, CountNotifiers())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Load Discord Notifier", func(t *testing.T) {
|
||||||
|
count := notifier.Load()
|
||||||
|
assert.Equal(t, currentCount+1, len(count))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Discord Within Limits", func(t *testing.T) {
|
||||||
|
ok, err := discorder.WithinLimits()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.True(t, ok)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Discord Send", func(t *testing.T) {
|
||||||
|
err := discorder.Send([]byte(discordMessage))
|
||||||
|
assert.Nil(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Discord Queue", func(t *testing.T) {
|
||||||
|
go notifier.Queue(discorder)
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
assert.Equal(t, DISCORD_URL, discorder.Host)
|
||||||
|
assert.Equal(t, 0, len(discorder.Queue))
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
// Package notifiers holds all the notifiers for Statup, which also includes
|
||||||
|
// user created notifiers that have been accepted in a Push Request. Read the
|
||||||
|
// Statup notifier wiki at: https://github.com/hunterlong/statup/wiki
|
||||||
|
//
|
||||||
|
// To see a full example of a notifier with all events, visit Statup's
|
||||||
|
// notifier example code: https://github.com/hunterlong/statup/wiki/Notifier-Example
|
||||||
|
//
|
||||||
|
// by Hunter Long
|
||||||
|
package notifiers
|
|
@ -17,6 +17,7 @@ package notifiers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/hunterlong/statup/core/notifier"
|
"github.com/hunterlong/statup/core/notifier"
|
||||||
"github.com/hunterlong/statup/types"
|
"github.com/hunterlong/statup/types"
|
||||||
|
@ -85,12 +86,12 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Email) Send(msg interface{}) error {
|
func (u *Email) Send(msg interface{}) error {
|
||||||
//email := msg.(*EmailOutgoing)
|
email := msg.(*EmailOutgoing)
|
||||||
//err := u.dialSend(email)
|
err := u.dialSend(email)
|
||||||
//if err != nil {
|
if err != nil {
|
||||||
// utils.Log(3, fmt.Sprintf("Email Notifier could not send email: %v", err))
|
utils.Log(3, fmt.Sprintf("Email Notifier could not send email: %v", err))
|
||||||
// return err
|
return err
|
||||||
//}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,6 +146,9 @@ func (u *Email) OnSave() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Email) dialSend(email *EmailOutgoing) error {
|
func (u *Email) dialSend(email *EmailOutgoing) error {
|
||||||
|
mailer = gomail.NewDialer(emailer.Host, emailer.Port, emailer.Username, emailer.Password)
|
||||||
|
mailer.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
||||||
|
emailSource(email)
|
||||||
m := gomail.NewMessage()
|
m := gomail.NewMessage()
|
||||||
m.SetHeader("From", email.From)
|
m.SetHeader("From", email.From)
|
||||||
m.SetHeader("To", email.To)
|
m.SetHeader("To", email.To)
|
||||||
|
@ -157,7 +161,7 @@ func (u *Email) dialSend(email *EmailOutgoing) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SendEmail(email *EmailOutgoing) {
|
func emailSource(email *EmailOutgoing) {
|
||||||
source := EmailTemplate(email.Template, email.Data)
|
source := EmailTemplate(email.Template, email.Data)
|
||||||
email.Source = source
|
email.Source = source
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
package notifiers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/hunterlong/statup/core/notifier"
|
||||||
|
"github.com/hunterlong/statup/utils"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
EMAIL_HOST = os.Getenv("EMAIL_HOST")
|
||||||
|
EMAIL_USER = os.Getenv("EMAIL_USER")
|
||||||
|
EMAIL_PASS = os.Getenv("EMAIL_PASS")
|
||||||
|
EMAIL_OUTGOING = os.Getenv("EMAIL_OUTGOING")
|
||||||
|
EMAIL_SEND_TO = os.Getenv("EMAIL_SEND_TO")
|
||||||
|
EMAIL_PORT = utils.StringInt(os.Getenv("EMAIL_PORT"))
|
||||||
|
)
|
||||||
|
|
||||||
|
var testEmail *EmailOutgoing
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
emailer.Host = EMAIL_HOST
|
||||||
|
emailer.Username = EMAIL_USER
|
||||||
|
emailer.Password = EMAIL_PASS
|
||||||
|
emailer.Var1 = EMAIL_OUTGOING
|
||||||
|
emailer.Var2 = EMAIL_SEND_TO
|
||||||
|
emailer.Port = int(EMAIL_PORT)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmailNotifier(t *testing.T) {
|
||||||
|
if EMAIL_HOST == "" || EMAIL_USER == "" || EMAIL_PASS == "" {
|
||||||
|
t.Log("Email notifier testing skipped, missing EMAIL_ environment variables")
|
||||||
|
t.SkipNow()
|
||||||
|
}
|
||||||
|
currentCount = CountNotifiers()
|
||||||
|
|
||||||
|
t.Run("New Emailer", func(t *testing.T) {
|
||||||
|
emailer.Host = EMAIL_HOST
|
||||||
|
emailer.Username = EMAIL_USER
|
||||||
|
emailer.Password = EMAIL_PASS
|
||||||
|
emailer.Var1 = EMAIL_OUTGOING
|
||||||
|
emailer.Var2 = EMAIL_SEND_TO
|
||||||
|
emailer.Port = int(EMAIL_PORT)
|
||||||
|
emailer.Delay = time.Duration(100 * time.Millisecond)
|
||||||
|
|
||||||
|
message := "this is a test email!"
|
||||||
|
|
||||||
|
testEmail = &EmailOutgoing{
|
||||||
|
To: emailer.GetValue("var2"),
|
||||||
|
Subject: fmt.Sprintf("Service %v is Failing", TestService.Name),
|
||||||
|
Template: MESSAGE,
|
||||||
|
Data: interface{}(message),
|
||||||
|
From: emailer.GetValue("var1"),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Add Email Notifier", func(t *testing.T) {
|
||||||
|
err := notifier.AddNotifier(emailer)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "Hunter Long", emailer.Author)
|
||||||
|
assert.Equal(t, EMAIL_HOST, emailer.Host)
|
||||||
|
assert.Equal(t, currentCount+1, CountNotifiers())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Emailer Load", func(t *testing.T) {
|
||||||
|
count := notifier.Load()
|
||||||
|
assert.Equal(t, currentCount+1, len(count))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Email Within Limits", func(t *testing.T) {
|
||||||
|
ok, err := emailer.WithinLimits()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.True(t, ok)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Emailer Test Source", func(t *testing.T) {
|
||||||
|
emailSource(testEmail)
|
||||||
|
assert.NotEmpty(t, testEmail.Source)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Email Send", func(t *testing.T) {
|
||||||
|
err := emailer.Send(testEmail)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Email Run Queue", func(t *testing.T) {
|
||||||
|
go notifier.Queue(emailer)
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
assert.Equal(t, EMAIL_HOST, emailer.Host)
|
||||||
|
assert.Equal(t, 0, len(emailer.Queue))
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
|
@ -1,69 +0,0 @@
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/1999/xhtml">
|
|
||||||
<head>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
|
||||||
<title>Sample Email</title>
|
|
||||||
|
|
||||||
|
|
||||||
</head>
|
|
||||||
<body style="-webkit-text-size-adjust: none; box-sizing: border-box; color: #74787E; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; height: 100%; line-height: 1.4; margin: 0; width: 100% !important;" bgcolor="#F2F4F6"><style type="text/css">
|
|
||||||
body {
|
|
||||||
width: 100% !important; height: 100%; margin: 0; line-height: 1.4; background-color: #F2F4F6; color: #74787E; -webkit-text-size-adjust: none;
|
|
||||||
}
|
|
||||||
@media only screen and (max-width: 600px) {
|
|
||||||
.email-body_inner {
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
.email-footer {
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media only screen and (max-width: 500px) {
|
|
||||||
.button {
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0" style="box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin: 0; padding: 0; width: 100%;" bgcolor="#F2F4F6">
|
|
||||||
<tr>
|
|
||||||
<td align="center" style="box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; word-break: break-word;">
|
|
||||||
<table class="email-content" width="100%" cellpadding="0" cellspacing="0" style="box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin: 0; padding: 0; width: 100%;">
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td class="email-body" width="100%" cellpadding="0" cellspacing="0" style="-premailer-cellpadding: 0; -premailer-cellspacing: 0; border-bottom-color: #EDEFF2; border-bottom-style: solid; border-bottom-width: 1px; border-top-color: #EDEFF2; border-top-style: solid; border-top-width: 1px; box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin: 0; padding: 0; width: 100%; word-break: break-word;" bgcolor="#FFFFFF">
|
|
||||||
<table class="email-body_inner" align="center" width="570" cellpadding="0" cellspacing="0" style="box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin: 0 auto; padding: 0; width: 570px;" bgcolor="#FFFFFF">
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td class="content-cell" style="box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; padding: 35px; word-break: break-word;">
|
|
||||||
<h1 style="box-sizing: border-box; color: #2F3133; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 19px; font-weight: bold; margin-top: 0;" align="left">{{ .Service.Name }} is Offline!</h1>
|
|
||||||
<p style="box-sizing: border-box; color: #74787E; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 16px; line-height: 1.5em; margin-top: 0;" align="left">
|
|
||||||
Your Statup service '<a target="_blank" href="{{.Service.Domain}}">{{.Service.Name}}</a>' has been triggered with a HTTP status code of '{{.Service.LastStatusCode}}' and is currently offline based on your requirements. This failure was created on {{.Service.CreatedAt}}.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{{if .Service.LastResponse }}
|
|
||||||
<h1 style="box-sizing: border-box; color: #2F3133; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 19px; font-weight: bold; margin-top: 0;" align="left">Last Response</h1>
|
|
||||||
<p style="box-sizing: border-box; color: #74787E; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 16px; line-height: 1.5em; margin-top: 0;" align="left">
|
|
||||||
{{ .Service.LastResponse }}
|
|
||||||
</p>
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
<table class="body-sub" style="border-top-color: #EDEFF2; border-top-style: solid; border-top-width: 1px; box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin-top: 25px; padding-top: 25px;">
|
|
||||||
<td style="box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; word-break: break-word;">
|
|
||||||
<a href="/service/{{.Service.Id}}" class="button button--blue" target="_blank" style="-webkit-text-size-adjust: none; background: #3869D4; border-color: #3869d4; border-radius: 3px; border-style: solid; border-width: 10px 18px; box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16); box-sizing: border-box; color: #FFF; display: inline-block; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; text-decoration: none;">View Service</a>
|
|
||||||
</td>
|
|
||||||
<td style="box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; word-break: break-word;">
|
|
||||||
<a href="/dashboard" class="button button--blue" target="_blank" style="-webkit-text-size-adjust: none; background: #3869D4; border-color: #3869d4; border-radius: 3px; border-style: solid; border-width: 10px 18px; box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16); box-sizing: border-box; color: #FFF; display: inline-block; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; text-decoration: none;">Statup Dashboard</a>
|
|
||||||
</td>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,61 +0,0 @@
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/1999/xhtml">
|
|
||||||
<head>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
|
||||||
<title>Sample Email</title>
|
|
||||||
|
|
||||||
|
|
||||||
</head>
|
|
||||||
<body style="-webkit-text-size-adjust: none; box-sizing: border-box; color: #74787E; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; height: 100%; line-height: 1.4; margin: 0; width: 100% !important;" bgcolor="#F2F4F6"><style type="text/css">
|
|
||||||
body {
|
|
||||||
width: 100% !important; height: 100%; margin: 0; line-height: 1.4; background-color: #F2F4F6; color: #74787E; -webkit-text-size-adjust: none;
|
|
||||||
}
|
|
||||||
@media only screen and (max-width: 600px) {
|
|
||||||
.email-body_inner {
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
.email-footer {
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media only screen and (max-width: 500px) {
|
|
||||||
.button {
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0" style="box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin: 0; padding: 0; width: 100%;" bgcolor="#F2F4F6">
|
|
||||||
<tr>
|
|
||||||
<td align="center" style="box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; word-break: break-word;">
|
|
||||||
<table class="email-content" width="100%" cellpadding="0" cellspacing="0" style="box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin: 0; padding: 0; width: 100%;">
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td class="email-body" width="100%" cellpadding="0" cellspacing="0" style="-premailer-cellpadding: 0; -premailer-cellspacing: 0; border-bottom-color: #EDEFF2; border-bottom-style: solid; border-bottom-width: 1px; border-top-color: #EDEFF2; border-top-style: solid; border-top-width: 1px; box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin: 0; padding: 0; width: 100%; word-break: break-word;" bgcolor="#FFFFFF">
|
|
||||||
<table class="email-body_inner" align="center" width="570" cellpadding="0" cellspacing="0" style="box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin: 0 auto; padding: 0; width: 570px;" bgcolor="#FFFFFF">
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td class="content-cell" style="box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; padding: 35px; word-break: break-word;">
|
|
||||||
<h1 style="box-sizing: border-box; color: #2F3133; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 19px; font-weight: bold; margin-top: 0;" align="left">Looks Like Emails Work!</h1>
|
|
||||||
<p style="box-sizing: border-box; color: #74787E; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 16px; line-height: 1.5em; margin-top: 0;" align="left">
|
|
||||||
Since you got this email, it confirms that your Statup Status Page email system is working correctly.
|
|
||||||
</p>
|
|
||||||
</p>
|
|
||||||
<p style="box-sizing: border-box; color: #74787E; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 16px; line-height: 1.5em; margin-top: 0;" align="left">
|
|
||||||
Enjoy using Statup!
|
|
||||||
<br />Statup.io Team</p>
|
|
||||||
|
|
||||||
<table class="body-sub" style="border-top-color: #EDEFF2; border-top-style: solid; border-top-width: 1px; box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin-top: 25px; padding-top: 25px;">
|
|
||||||
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
package notifiers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hunterlong/statup/core/notifier"
|
||||||
|
"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"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
dir string
|
||||||
|
db *gorm.DB
|
||||||
|
currentCount int
|
||||||
|
)
|
||||||
|
|
||||||
|
var TestService = &types.Service{
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
var TestFailure = &types.Failure{
|
||||||
|
Issue: "testing",
|
||||||
|
}
|
||||||
|
|
||||||
|
var TestUser = &types.User{
|
||||||
|
Username: "admin",
|
||||||
|
Email: "info@email.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
var TestCore = &types.Core{
|
||||||
|
Name: "testing notifiers",
|
||||||
|
}
|
||||||
|
|
||||||
|
func CountNotifiers() int {
|
||||||
|
return len(notifier.AllCommunications)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
dir = utils.Directory
|
||||||
|
source.Assets()
|
||||||
|
utils.InitLogs()
|
||||||
|
injectDatabase()
|
||||||
|
}
|
||||||
|
|
||||||
|
func injectDatabase() {
|
||||||
|
utils.DeleteFile(dir + "/statup.db")
|
||||||
|
db, err := gorm.Open("sqlite3", dir+"/statup.db")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
db.CreateTable(¬ifier.Notification{})
|
||||||
|
notifier.SetDB(db)
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"github.com/hunterlong/statup/core/notifier"
|
"github.com/hunterlong/statup/core/notifier"
|
||||||
"github.com/hunterlong/statup/types"
|
"github.com/hunterlong/statup/types"
|
||||||
"github.com/hunterlong/statup/utils"
|
"github.com/hunterlong/statup/utils"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
@ -56,7 +57,7 @@ var slacker = &Slack{¬ifier.Notification{
|
||||||
}}},
|
}}},
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendSlack(temp string, data interface{}) error {
|
func parseSlackMessage(temp string, data interface{}) error {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
slackTemp, _ := template.New("slack").Parse(temp)
|
slackTemp, _ := template.New("slack").Parse(temp)
|
||||||
err := slackTemp.Execute(buf, data)
|
err := slackTemp.Execute(buf, data)
|
||||||
|
@ -67,7 +68,7 @@ func sendSlack(temp string, data interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type slackMessage struct {
|
type SlackMessage struct {
|
||||||
Service *types.Service
|
Service *types.Service
|
||||||
Template string
|
Template string
|
||||||
Time int64
|
Time int64
|
||||||
|
@ -84,10 +85,13 @@ func init() {
|
||||||
func (u *Slack) Send(msg interface{}) error {
|
func (u *Slack) Send(msg interface{}) error {
|
||||||
message := msg.(string)
|
message := msg.(string)
|
||||||
client := new(http.Client)
|
client := new(http.Client)
|
||||||
_, err := client.Post(u.Host, "application/json", bytes.NewBuffer([]byte(message)))
|
res, err := client.Post(u.Host, "application/json", bytes.NewBuffer([]byte(message)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
contents, _ := ioutil.ReadAll(res.Body)
|
||||||
|
fmt.Println(string(contents))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,18 +102,18 @@ func (u *Slack) Select() *notifier.Notification {
|
||||||
func (u *Slack) OnTest(n notifier.Notification) (bool, error) {
|
func (u *Slack) OnTest(n notifier.Notification) (bool, error) {
|
||||||
utils.Log(1, "Slack notifier loaded")
|
utils.Log(1, "Slack notifier loaded")
|
||||||
msg := fmt.Sprintf("You're Statup Slack Notifier is working correctly!")
|
msg := fmt.Sprintf("You're Statup Slack Notifier is working correctly!")
|
||||||
err := sendSlack(TEST_TEMPLATE, msg)
|
err := parseSlackMessage(TEST_TEMPLATE, msg)
|
||||||
return true, err
|
return true, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ON SERVICE FAILURE, DO YOUR OWN FUNCTIONS
|
// ON SERVICE FAILURE, DO YOUR OWN FUNCTIONS
|
||||||
func (u *Slack) OnFailure(s *types.Service, f *types.Failure) {
|
func (u *Slack) OnFailure(s *types.Service, f *types.Failure) {
|
||||||
message := slackMessage{
|
message := SlackMessage{
|
||||||
Service: s,
|
Service: s,
|
||||||
Template: FAILURE,
|
Template: FAILURE,
|
||||||
Time: time.Now().Unix(),
|
Time: time.Now().Unix(),
|
||||||
}
|
}
|
||||||
sendSlack(FAILING_TEMPLATE, message)
|
parseSlackMessage(FAILING_TEMPLATE, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ON SERVICE SUCCESS, DO YOUR OWN FUNCTIONS
|
// ON SERVICE SUCCESS, DO YOUR OWN FUNCTIONS
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
package notifiers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hunterlong/statup/core/notifier"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
SLACK_URL = os.Getenv("SLACK_URL")
|
||||||
|
slackMessage = `{"text":"this is a test from the Slack notifier!"}`
|
||||||
|
slackTestMessage = SlackMessage{
|
||||||
|
Service: TestService,
|
||||||
|
Template: FAILURE,
|
||||||
|
Time: time.Now().Unix(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
slacker.Host = SLACK_URL
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlackNotifier(t *testing.T) {
|
||||||
|
if SLACK_URL == "" {
|
||||||
|
t.Log("Slack notifier testing skipped, missing SLACK_URL environment variable")
|
||||||
|
t.SkipNow()
|
||||||
|
}
|
||||||
|
currentCount = CountNotifiers()
|
||||||
|
|
||||||
|
t.Run("Load Slack", func(t *testing.T) {
|
||||||
|
slacker.Host = SLACK_URL
|
||||||
|
slacker.Delay = time.Duration(100 * time.Millisecond)
|
||||||
|
err := notifier.AddNotifier(slacker)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "Hunter Long", slacker.Author)
|
||||||
|
assert.Equal(t, SLACK_URL, slacker.Host)
|
||||||
|
assert.Equal(t, currentCount+1, CountNotifiers())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Load Slack Notifier", func(t *testing.T) {
|
||||||
|
count := notifier.Load()
|
||||||
|
assert.Equal(t, currentCount+1, len(count))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Slack parse message", func(t *testing.T) {
|
||||||
|
err := parseSlackMessage("this is a test message!", slackTestMessage)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, 1, len(slacker.Queue))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Slack Within Limits", func(t *testing.T) {
|
||||||
|
ok, err := slacker.WithinLimits()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.True(t, ok)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Slack Send", func(t *testing.T) {
|
||||||
|
err := slacker.Send(slackMessage)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Slack Queue", func(t *testing.T) {
|
||||||
|
go notifier.Queue(slacker)
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
assert.Equal(t, SLACK_URL, slacker.Host)
|
||||||
|
assert.Equal(t, 0, len(slacker.Queue))
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
|
@ -16,10 +16,13 @@
|
||||||
package notifiers
|
package notifiers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/hunterlong/statup/core/notifier"
|
"github.com/hunterlong/statup/core/notifier"
|
||||||
"github.com/hunterlong/statup/types"
|
"github.com/hunterlong/statup/types"
|
||||||
"github.com/hunterlong/statup/utils"
|
"github.com/hunterlong/statup/utils"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -27,14 +30,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TWILIO_ID = 3
|
|
||||||
TWILIO_METHOD = "twilio"
|
TWILIO_METHOD = "twilio"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
twilioMessages []string
|
|
||||||
)
|
|
||||||
|
|
||||||
type Twilio struct {
|
type Twilio struct {
|
||||||
*notifier.Notification
|
*notifier.Notification
|
||||||
}
|
}
|
||||||
|
@ -59,12 +57,12 @@ var twilio = &Twilio{¬ifier.Notification{
|
||||||
}, {
|
}, {
|
||||||
Type: "text",
|
Type: "text",
|
||||||
Title: "SMS to Phone Number",
|
Title: "SMS to Phone Number",
|
||||||
Placeholder: "+18555555555",
|
Placeholder: "18555555555",
|
||||||
DbField: "Var1",
|
DbField: "Var1",
|
||||||
}, {
|
}, {
|
||||||
Type: "text",
|
Type: "text",
|
||||||
Title: "From Phone Number",
|
Title: "From Phone Number",
|
||||||
Placeholder: "+18555555555",
|
Placeholder: "18555555555",
|
||||||
DbField: "Var2",
|
DbField: "Var2",
|
||||||
}}},
|
}}},
|
||||||
}
|
}
|
||||||
|
@ -86,19 +84,24 @@ func (u *Twilio) Send(msg interface{}) error {
|
||||||
twilioUrl := fmt.Sprintf("https://api.twilio.com/2010-04-01/Accounts/%v/Messages.json", u.GetValue("api_key"))
|
twilioUrl := fmt.Sprintf("https://api.twilio.com/2010-04-01/Accounts/%v/Messages.json", u.GetValue("api_key"))
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
v := url.Values{}
|
v := url.Values{}
|
||||||
v.Set("To", u.Var1)
|
v.Set("To", "+"+u.Var1)
|
||||||
v.Set("From", u.Var2)
|
v.Set("From", "+"+u.Var2)
|
||||||
v.Set("Body", message)
|
v.Set("Body", message)
|
||||||
rb := *strings.NewReader(v.Encode())
|
rb := *strings.NewReader(v.Encode())
|
||||||
req, err := http.NewRequest("POST", twilioUrl, &rb)
|
req, err := http.NewRequest("POST", twilioUrl, &rb)
|
||||||
req.SetBasicAuth(u.ApiKey, u.ApiSecret)
|
req.SetBasicAuth(u.ApiKey, u.ApiSecret)
|
||||||
req.Header.Add("Accept", "application/json")
|
req.Header.Add("Accept", "application/json")
|
||||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||||
client.Do(req)
|
res, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(3, fmt.Sprintf("Issue sending Twilio notification: %v", err))
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
contents, _ := ioutil.ReadAll(res.Body)
|
||||||
|
success, twilioRes := twilioSuccess(contents)
|
||||||
|
if !success {
|
||||||
|
return errors.New(fmt.Sprintf("twilio didn't receive the expected status of 'enque' from API got: %v", twilioRes))
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,3 +124,37 @@ func (u *Twilio) OnSave() error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func twilioSuccess(res []byte) (bool, TwilioResponse) {
|
||||||
|
var obj TwilioResponse
|
||||||
|
json.Unmarshal(res, &obj)
|
||||||
|
if obj.Status == "queued" {
|
||||||
|
return true, obj
|
||||||
|
}
|
||||||
|
return false, obj
|
||||||
|
}
|
||||||
|
|
||||||
|
type TwilioResponse struct {
|
||||||
|
Sid string `json:"sid"`
|
||||||
|
DateCreated string `json:"date_created"`
|
||||||
|
DateUpdated string `json:"date_updated"`
|
||||||
|
DateSent interface{} `json:"date_sent"`
|
||||||
|
AccountSid string `json:"account_sid"`
|
||||||
|
To string `json:"to"`
|
||||||
|
From string `json:"from"`
|
||||||
|
MessagingServiceSid interface{} `json:"messaging_service_sid"`
|
||||||
|
Body string `json:"body"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
NumSegments string `json:"num_segments"`
|
||||||
|
NumMedia string `json:"num_media"`
|
||||||
|
Direction string `json:"direction"`
|
||||||
|
APIVersion string `json:"api_version"`
|
||||||
|
Price interface{} `json:"price"`
|
||||||
|
PriceUnit string `json:"price_unit"`
|
||||||
|
ErrorCode interface{} `json:"error_code"`
|
||||||
|
ErrorMessage interface{} `json:"error_message"`
|
||||||
|
URI string `json:"uri"`
|
||||||
|
SubresourceUris struct {
|
||||||
|
Media string `json:"media"`
|
||||||
|
} `json:"subresource_uris"`
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
package notifiers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hunterlong/statup/core/notifier"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
TWILIO_SID = os.Getenv("TWILIO_SID")
|
||||||
|
TWILIO_SECRET = os.Getenv("TWILIO_SECRET")
|
||||||
|
TWILIO_FROM = os.Getenv("TWILIO_FROM")
|
||||||
|
TWILIO_TO = os.Getenv("TWILIO_TO")
|
||||||
|
twilioMessage = "The Twilio notifier on Statup has been tested!"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
twilio.ApiKey = TWILIO_SID
|
||||||
|
twilio.ApiSecret = TWILIO_SECRET
|
||||||
|
twilio.Var1 = TWILIO_TO
|
||||||
|
twilio.Var2 = TWILIO_FROM
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTwilioNotifier(t *testing.T) {
|
||||||
|
if TWILIO_SID == "" || TWILIO_SECRET == "" || TWILIO_FROM == "" {
|
||||||
|
t.Log("Twilio notifier testing skipped, missing TWILIO_SID environment variable")
|
||||||
|
t.SkipNow()
|
||||||
|
}
|
||||||
|
currentCount = CountNotifiers()
|
||||||
|
|
||||||
|
t.Run("Load Twilio", func(t *testing.T) {
|
||||||
|
twilio.ApiKey = TWILIO_SID
|
||||||
|
twilio.Delay = time.Duration(100 * time.Millisecond)
|
||||||
|
err := notifier.AddNotifier(twilio)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "Hunter Long", twilio.Author)
|
||||||
|
assert.Equal(t, TWILIO_SID, twilio.ApiKey)
|
||||||
|
assert.Equal(t, currentCount+1, CountNotifiers())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Load Twilio Notifier", func(t *testing.T) {
|
||||||
|
count := notifier.Load()
|
||||||
|
assert.Equal(t, currentCount+1, len(count))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Twilio Within Limits", func(t *testing.T) {
|
||||||
|
ok, err := twilio.WithinLimits()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.True(t, ok)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Twilio Send", func(t *testing.T) {
|
||||||
|
err := twilio.Send(twilioMessage)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Twilio Queue", func(t *testing.T) {
|
||||||
|
go notifier.Queue(twilio)
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
assert.Equal(t, TWILIO_SID, twilio.ApiKey)
|
||||||
|
assert.Equal(t, 0, len(twilio.Queue))
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
// Package source holds all the assets for Statup. This includes
|
||||||
|
// CSS, JS, SCSS, HTML and other website related content.
|
||||||
|
// This package uses Rice to compile all assets into a single 'rice-box.go' file.
|
||||||
|
//
|
||||||
|
// To compile all the assets run `rice embed-go` in the source directory.
|
||||||
|
//
|
||||||
|
// by Hunter Long
|
||||||
|
package source
|
|
@ -32,7 +32,7 @@
|
||||||
<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 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>
|
<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 .Notifications }}
|
{{ 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>
|
<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}} <span class="badge badge-pill badge-secondary"></span></a>
|
||||||
{{ end }}
|
{{ 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>
|
<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>
|
||||||
<a class="nav-link d-none" id="v-pills-backups-tab" data-toggle="pill" href="#v-pills-backups" role="tab" aria-controls="v-pills-backups" aria-selected="false">Backups</a>
|
<a class="nav-link d-none" id="v-pills-backups-tab" data-toggle="pill" href="#v-pills-backups" role="tab" aria-controls="v-pills-backups" aria-selected="false">Backups</a>
|
||||||
|
|
|
@ -21,6 +21,7 @@ type Core struct {
|
||||||
Version string `gorm:"column:version" json:"version"`
|
Version string `gorm:"column:version" json:"version"`
|
||||||
MigrationId int64 `gorm:"column:migration_id" json:"migration_id,omitempty"`
|
MigrationId int64 `gorm:"column:migration_id" json:"migration_id,omitempty"`
|
||||||
UseCdn bool `gorm:"column:use_cdn;default:false" json:"using_cdn,omitempty"`
|
UseCdn bool `gorm:"column:use_cdn;default:false" json:"using_cdn,omitempty"`
|
||||||
|
Timezone float32 `gorm:"column:timezone;default:-8.0" json:"timezone,omitempty"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
|
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
|
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
|
||||||
DbConnection string `gorm:"-" json:"database"`
|
DbConnection string `gorm:"-" json:"database"`
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
// Package type contains all of the structs for objects in Statup including
|
||||||
|
// services, hits, failures, Core, and others.
|
||||||
|
//
|
||||||
|
// by Hunter Long
|
||||||
|
package types
|
|
@ -0,0 +1,10 @@
|
||||||
|
// Package utils contains common methods used in most packages in Statup.
|
||||||
|
// This package contains multiple function like:
|
||||||
|
// Logging, encryption, type conversions, setting utils.Directory as the current directory,
|
||||||
|
// running local CMD commands, and creaing/deleting files/folder.
|
||||||
|
//
|
||||||
|
// You can overwrite the utils.Directory global variable by including
|
||||||
|
// STATUP_DIR environment variable to be an absolute path.
|
||||||
|
//
|
||||||
|
// by Hunter Long
|
||||||
|
package utils
|
Loading…
Reference in New Issue