diff --git a/cmd/doc.go b/cmd/doc.go new file mode 100644 index 00000000..9ec0ac66 --- /dev/null +++ b/cmd/doc.go @@ -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 diff --git a/core/doc.go b/core/doc.go new file mode 100644 index 00000000..756f715a --- /dev/null +++ b/core/doc.go @@ -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 diff --git a/core/notifier/notifiers.go b/core/notifier/notifiers.go index 46f71b0e..42572652 100644 --- a/core/notifier/notifiers.go +++ b/core/notifier/notifiers.go @@ -262,7 +262,8 @@ CheckNotifier: case <-time.After(rateLimit): notification = n.Select() if len(notification.Queue) > 0 { - if notification.WithinLimits() { + ok, _ := notification.WithinLimits() + if ok { msg := notification.Queue[0] err := n.Send(msg) if err != nil { @@ -371,23 +372,27 @@ func isEnabled(n interface{}) bool { func inLimits(n interface{}) bool { 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 { - 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 { notify.Delay = time.Duration(500 * time.Millisecond) } if notify.LastSent().Seconds() == 0 { - return true + return true, nil } 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() { diff --git a/handlers/doc.go b/handlers/doc.go new file mode 100644 index 00000000..52de107d --- /dev/null +++ b/handlers/doc.go @@ -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 diff --git a/handlers/routes.go b/handlers/routes.go index 2b3927f5..f46aa8ec 100644 --- a/handlers/routes.go +++ b/handlers/routes.go @@ -51,19 +51,7 @@ func Router() *mux.Router { r.Handle("/charts/{id}.js", http.HandlerFunc(RenderServiceChartHandler)) r.Handle("/setup", http.HandlerFunc(SetupHandler)).Methods("GET") r.Handle("/setup", http.HandlerFunc(ProcessSetupHandler)).Methods("POST") - r.Handle("/dashboard", http.HandlerFunc(DashboardHandler)).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("/dashboard", http.HandlerFunc(DashboardHandler)).Methods("GET").HandlerFunc(UsersHandler).Methods("GET") r.Handle("/users", http.HandlerFunc(CreateUserHandler)).Methods("POST") r.Handle("/user/{id}", http.HandlerFunc(UsersEditHandler)).Methods("GET") r.Handle("/user/{id}", http.HandlerFunc(UpdateUserHandler)).Methods("POST") diff --git a/notifiers/discord.go b/notifiers/discord.go index db4552da..dadda8da 100644 --- a/notifiers/discord.go +++ b/notifiers/discord.go @@ -59,7 +59,6 @@ func init() { func (u *Discord) Send(msg interface{}) error { message := msg.([]byte) - fmt.Println("sending: ", message) req, _ := http.NewRequest("POST", discorder.GetValue("host"), bytes.NewBuffer(message)) req.Header.Set("Content-Type", "application/json") client := &http.Client{} @@ -76,7 +75,6 @@ func (u *Discord) Select() *notifier.Notification { 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) - fmt.Println(msg) u.AddQueue(msg) } diff --git a/notifiers/discord_test.go b/notifiers/discord_test.go new file mode 100644 index 00000000..21d25b8e --- /dev/null +++ b/notifiers/discord_test.go @@ -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)) + }) + +} diff --git a/notifiers/doc.go b/notifiers/doc.go new file mode 100644 index 00000000..eae5662a --- /dev/null +++ b/notifiers/doc.go @@ -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 diff --git a/notifiers/email.go b/notifiers/email.go index 8c4ace9c..50b1b482 100644 --- a/notifiers/email.go +++ b/notifiers/email.go @@ -17,6 +17,7 @@ package notifiers import ( "bytes" + "crypto/tls" "fmt" "github.com/hunterlong/statup/core/notifier" "github.com/hunterlong/statup/types" @@ -85,12 +86,12 @@ func init() { } func (u *Email) Send(msg interface{}) error { - //email := msg.(*EmailOutgoing) - //err := u.dialSend(email) - //if err != nil { - // utils.Log(3, fmt.Sprintf("Email Notifier could not send email: %v", err)) - // return err - //} + email := msg.(*EmailOutgoing) + err := u.dialSend(email) + if err != nil { + utils.Log(3, fmt.Sprintf("Email Notifier could not send email: %v", err)) + return err + } return nil } @@ -145,6 +146,9 @@ func (u *Email) OnSave() 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.SetHeader("From", email.From) m.SetHeader("To", email.To) @@ -157,7 +161,7 @@ func (u *Email) dialSend(email *EmailOutgoing) error { return nil } -func SendEmail(email *EmailOutgoing) { +func emailSource(email *EmailOutgoing) { source := EmailTemplate(email.Template, email.Data) email.Source = source } diff --git a/notifiers/email_test.go b/notifiers/email_test.go new file mode 100644 index 00000000..a13f0731 --- /dev/null +++ b/notifiers/email_test.go @@ -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)) + }) + +} diff --git a/notifiers/emails/failure.html b/notifiers/emails/failure.html deleted file mode 100644 index 2d11e1c4..00000000 --- a/notifiers/emails/failure.html +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - Sample Email - - - - - - - - -
- - - - - - -
- - \ No newline at end of file diff --git a/notifiers/emails/message.html b/notifiers/emails/message.html deleted file mode 100644 index 10d861e4..00000000 --- a/notifiers/emails/message.html +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - Sample Email - - - - - - - - -
- - - - - - -
- - \ No newline at end of file diff --git a/notifiers/notifiers_test.go b/notifiers/notifiers_test.go new file mode 100644 index 00000000..2e527b37 --- /dev/null +++ b/notifiers/notifiers_test.go @@ -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) +} diff --git a/notifiers/slack.go b/notifiers/slack.go index 85723c07..c84add49 100644 --- a/notifiers/slack.go +++ b/notifiers/slack.go @@ -21,6 +21,7 @@ import ( "github.com/hunterlong/statup/core/notifier" "github.com/hunterlong/statup/types" "github.com/hunterlong/statup/utils" + "io/ioutil" "net/http" "text/template" "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) slackTemp, _ := template.New("slack").Parse(temp) err := slackTemp.Execute(buf, data) @@ -67,7 +68,7 @@ func sendSlack(temp string, data interface{}) error { return nil } -type slackMessage struct { +type SlackMessage struct { Service *types.Service Template string Time int64 @@ -84,10 +85,13 @@ func init() { func (u *Slack) Send(msg interface{}) error { message := msg.(string) 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 { return err } + defer res.Body.Close() + contents, _ := ioutil.ReadAll(res.Body) + fmt.Println(string(contents)) return nil } @@ -98,18 +102,18 @@ func (u *Slack) Select() *notifier.Notification { func (u *Slack) OnTest(n notifier.Notification) (bool, error) { utils.Log(1, "Slack notifier loaded") msg := fmt.Sprintf("You're Statup Slack Notifier is working correctly!") - err := sendSlack(TEST_TEMPLATE, msg) + err := parseSlackMessage(TEST_TEMPLATE, msg) return true, err } // ON SERVICE FAILURE, DO YOUR OWN FUNCTIONS func (u *Slack) OnFailure(s *types.Service, f *types.Failure) { - message := slackMessage{ + message := SlackMessage{ Service: s, Template: FAILURE, Time: time.Now().Unix(), } - sendSlack(FAILING_TEMPLATE, message) + parseSlackMessage(FAILING_TEMPLATE, message) } // ON SERVICE SUCCESS, DO YOUR OWN FUNCTIONS diff --git a/notifiers/slack_test.go b/notifiers/slack_test.go new file mode 100644 index 00000000..fa2b8804 --- /dev/null +++ b/notifiers/slack_test.go @@ -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)) + }) + +} diff --git a/notifiers/twilio.go b/notifiers/twilio.go index 549df975..141228ce 100644 --- a/notifiers/twilio.go +++ b/notifiers/twilio.go @@ -16,10 +16,13 @@ package notifiers import ( + "encoding/json" + "errors" "fmt" "github.com/hunterlong/statup/core/notifier" "github.com/hunterlong/statup/types" "github.com/hunterlong/statup/utils" + "io/ioutil" "net/http" "net/url" "strings" @@ -27,14 +30,9 @@ import ( ) const ( - TWILIO_ID = 3 TWILIO_METHOD = "twilio" ) -var ( - twilioMessages []string -) - type Twilio struct { *notifier.Notification } @@ -59,12 +57,12 @@ var twilio = &Twilio{¬ifier.Notification{ }, { Type: "text", Title: "SMS to Phone Number", - Placeholder: "+18555555555", + Placeholder: "18555555555", DbField: "Var1", }, { Type: "text", Title: "From Phone Number", - Placeholder: "+18555555555", + Placeholder: "18555555555", 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")) client := &http.Client{} v := url.Values{} - v.Set("To", u.Var1) - v.Set("From", u.Var2) + v.Set("To", "+"+u.Var1) + v.Set("From", "+"+u.Var2) v.Set("Body", message) rb := *strings.NewReader(v.Encode()) req, err := http.NewRequest("POST", twilioUrl, &rb) req.SetBasicAuth(u.ApiKey, u.ApiSecret) req.Header.Add("Accept", "application/json") req.Header.Add("Content-Type", "application/x-www-form-urlencoded") - client.Do(req) + res, err := client.Do(req) if err != nil { - utils.Log(3, fmt.Sprintf("Issue sending Twilio notification: %v", 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 } @@ -121,3 +124,37 @@ func (u *Twilio) OnSave() error { 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"` +} diff --git a/notifiers/twilio_test.go b/notifiers/twilio_test.go new file mode 100644 index 00000000..8736e911 --- /dev/null +++ b/notifiers/twilio_test.go @@ -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)) + }) + +} diff --git a/source/doc.go b/source/doc.go new file mode 100644 index 00000000..b48bf193 --- /dev/null +++ b/source/doc.go @@ -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 diff --git a/source/tmpl/settings.html b/source/tmpl/settings.html index 9ca577d5..e88a8d36 100644 --- a/source/tmpl/settings.html +++ b/source/tmpl/settings.html @@ -32,7 +32,7 @@ Settings Theme Editor {{ range .Notifications }} - {{.Select.Method}} + {{.Select.Method}} {{ end }} Browse Plugins Backups diff --git a/types/core.go b/types/core.go index 7e6caccd..43daf799 100644 --- a/types/core.go +++ b/types/core.go @@ -21,6 +21,7 @@ type Core struct { 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"` + Timezone float32 `gorm:"column:timezone;default:-8.0" json:"timezone,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"` diff --git a/types/doc.go b/types/doc.go new file mode 100644 index 00000000..e968d183 --- /dev/null +++ b/types/doc.go @@ -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 diff --git a/utils/doc.go b/utils/doc.go new file mode 100644 index 00000000..6cf016a6 --- /dev/null +++ b/utils/doc.go @@ -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