diff --git a/.travis.yml b/.travis.yml index 19e032d0..2a2f92d2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ services: env: global: - - VERSION=0.28.5 + - VERSION=0.28.6 - DB_HOST=localhost - DB_USER=travis - DB_PASS= diff --git a/.travis/Dockerfile.dev b/.travis/Dockerfile.dev new file mode 100644 index 00000000..de8a5191 --- /dev/null +++ b/.travis/Dockerfile.dev @@ -0,0 +1,27 @@ +FROM golang:1.10.3-alpine + +RUN apk update && apk add git g++ libstdc++ ca-certificates + +WORKDIR $GOPATH/src/github.com/hunterlong/statup/ + +COPY . $GOPATH/src/github.com/hunterlong/statup/ +RUN go get github.com/GeertJohan/go.rice/rice +RUN go get -d -v +RUN rice embed-go +RUN go install + +RUN wget -q https://assets.statup.io/sass && \ + chmod +x sass && \ + mv sass /usr/local/bin/sass + +ENV IS_DOCKER=true +ENV SASS=/usr/local/bin/sass +ENV CMD_FILE=/usr/bin/cmd + +RUN printf "#!/usr/bin/env sh\n\$1\n" > $CMD_FILE && \ + chmod +x $CMD_FILE + +WORKDIR /app +VOLUME /app +EXPOSE 8080 +CMD ["/go/bin/statup"] \ No newline at end of file diff --git a/.travis/docker.sh b/.travis/docker.sh new file mode 100755 index 00000000..d4162e45 --- /dev/null +++ b/.travis/docker.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +cd .travis +cp Dockerfile.dev ../ +cd ../ +docker build -t hunterlong/statup:dev -f Dockerfile.dev . + +rm -rf Dockerfile.dev \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 54d3f1c9..585a5780 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM alpine:latest -ENV VERSION=v0.28.5 +ENV VERSION=v0.28.6 RUN apk --no-cache add libstdc++ ca-certificates RUN wget -q https://github.com/hunterlong/statup/releases/download/$VERSION/statup-linux-alpine.tar.gz && \ diff --git a/core/assets.go b/core/assets.go index 6269f586..fa7d1c99 100644 --- a/core/assets.go +++ b/core/assets.go @@ -95,7 +95,7 @@ func CreateAllAssets() { utils.Log(1, "Inserting scss, css, emails, and javascript files into assets..") CopyToPublic(ScssBox, "scss", "base.scss") CopyToPublic(ScssBox, "scss", "variables.scss") - CopyToPublic(EmailBox, "emails", "error.html") + CopyToPublic(EmailBox, "emails", "message.html") CopyToPublic(EmailBox, "emails", "failure.html") CopyToPublic(CssBox, "css", "bootstrap.min.css") CopyToPublic(JsBox, "js", "bootstrap.min.js") @@ -106,7 +106,6 @@ func CreateAllAssets() { CopyToPublic(JsBox, "js", "setup.js") CopyToPublic(TmplBox, "", "robots.txt") CopyToPublic(TmplBox, "", "favicon.ico") - utils.Log(1, "Compiling CSS from SCSS style...") err := CompileSASS() if err != nil { diff --git a/core/checker.go b/core/checker.go index 50b4d50d..668d5f34 100644 --- a/core/checker.go +++ b/core/checker.go @@ -17,10 +17,10 @@ type FailureData types.FailureData func CheckServices() { CoreApp.Services, _ = SelectAllServices() - utils.Log(1, fmt.Sprintf("Loaded %v Services", len(CoreApp.Services))) + utils.Log(1, fmt.Sprintf("Starting monitoring process for %v Services", len(CoreApp.Services))) for _, v := range CoreApp.Services { obj := v - go obj.StartCheckins() + //go obj.StartCheckins() go obj.CheckQueue() } } diff --git a/core/communication.go b/core/communication.go index 0460bcd8..044d4a4e 100644 --- a/core/communication.go +++ b/core/communication.go @@ -1,21 +1,36 @@ package core import ( - "bytes" "fmt" + "github.com/hunterlong/statup/notifications" "github.com/hunterlong/statup/types" "github.com/hunterlong/statup/utils" - "net/http" "time" ) -type Communication types.Communication - func LoadDefaultCommunications() { - emailer := SelectCommunication(1) + notifications.EmailComm = SelectCommunication(1) + emailer := notifications.EmailComm if emailer.Enabled { - //LoadMailer(emailer) - //go EmailerQueue() + admin, _ := SelectUser(1) + notifications.LoadEmailer(emailer) + email := &types.Email{ + To: admin.Email, + Subject: "Test Email", + Template: "message.html", + Data: nil, + From: emailer.Var1, + } + notifications.SendEmail(EmailBox, email) + go notifications.EmailRoutine() + } + notifications.SlackComm = SelectCommunication(2) + slack := notifications.SlackComm + if slack.Enabled { + notifications.LoadSlack(slack.Host) + msg := fmt.Sprintf("Slack loaded on your Statup Status Page!") + notifications.SendSlack(msg) + go notifications.SlackRoutine() } } @@ -27,25 +42,15 @@ func LoadComms() { } } -func Run(c *Communication) { - - //sample := &Email{ - // To: "info@socialeck.com", - // Subject: "Test Email from Statup", - //} - - //AddEmail(sample) -} - -func SelectAllCommunications() ([]*Communication, error) { - var c []*Communication +func SelectAllCommunications() ([]*types.Communication, error) { + var c []*types.Communication col := DbSession.Collection("communication").Find() - err := col.All(&c) + err := col.OrderBy("id").All(&c) CoreApp.Communications = c return c, err } -func Create(c *Communication) (int64, error) { +func Create(c *types.Communication) (int64, error) { c.CreatedAt = time.Now() uuid, err := DbSession.Collection("communication").Insert(c) if err != nil { @@ -62,45 +67,30 @@ func Create(c *Communication) (int64, error) { return uuid.(int64), err } -func Disable(c *Communication) { +func Disable(c *types.Communication) { c.Enabled = false Update(c) } -func Enable(c *Communication) { +func Enable(c *types.Communication) { c.Enabled = true Update(c) } -func Update(c *Communication) *Communication { +func Update(c *types.Communication) *types.Communication { col := DbSession.Collection("communication").Find("id", c.Id) col.Update(c) + SelectAllCommunications() return c } -func SelectCommunication(id int64) *Communication { - for _, c := range CoreApp.Communications { - if c.Id == id { - return c - } - } - return nil -} - -func SendSlackMessage(msg string) error { - fullMessage := fmt.Sprintf("{\"text\":\"%v\"}", msg) - utils.Log(1, fmt.Sprintf("Sending JSON to Slack Webhook: %v", fullMessage)) - slack := SelectCommunication(2) - if slack == nil { - utils.Log(3, fmt.Sprintf("Slack communication database entry was not found.")) +func SelectCommunication(id int64) *types.Communication { + var comm *types.Communication + col := DbSession.Collection("communication").Find("id", id) + err := col.One(&comm) + if err != nil { + utils.Log(2, err) return nil } - client := http.Client{ - Timeout: 15 * time.Second, - } - _, err := client.Post(slack.Host, "application/json", bytes.NewBuffer([]byte(fullMessage))) - if err != nil { - utils.Log(3, err) - } - return err + return comm } diff --git a/core/core.go b/core/core.go index 540ba08b..9f8ba209 100644 --- a/core/core.go +++ b/core/core.go @@ -23,7 +23,7 @@ type Core struct { Plugins []plugin.Info Repos []PluginJSON //PluginFields []PluginSelect - Communications []*Communication + Communications []*types.Communication OfflineAssets bool } @@ -45,6 +45,16 @@ func init() { CoreApp = new(Core) } +func InitApp() { + SelectCore() + SelectAllCommunications() + InsertDefaultComms() + LoadDefaultCommunications() + SelectAllServices() + CheckServices() + go DatabaseMaintence() +} + func (c *Core) Update() (*Core, error) { res := DbSession.Collection("core").Find().Limit(1) res.Update(c) diff --git a/core/events.go b/core/events.go index 48bd15c6..b9dea112 100644 --- a/core/events.go +++ b/core/events.go @@ -3,7 +3,9 @@ package core import ( "fmt" "github.com/fatih/structs" + "github.com/hunterlong/statup/notifications" "github.com/hunterlong/statup/plugin" + "github.com/hunterlong/statup/types" "upper.io/db.v3/lib/sqlbuilder" ) @@ -23,13 +25,38 @@ func OnFailure(s *Service, f FailureData) { for _, p := range AllPlugins { p.OnFailure(structs.Map(s)) } + + onFailureEmail(s, f) + onFailureSlack(s, f) +} + +func onFailureSlack(s *Service, f FailureData) { slack := SelectCommunication(2) - if slack == nil { - return - } if slack.Enabled { msg := fmt.Sprintf("Service %v is currently offline! Issue: %v", s.Name, f.Issue) - SendSlackMessage(msg) + notifications.SendSlack(msg) + } +} + +type failedEmail struct { + Service *Service + FailureData FailureData + Domain string +} + +func onFailureEmail(s *Service, f FailureData) { + email := SelectCommunication(1) + if email.Enabled { + data := failedEmail{s, f, CoreApp.Domain} + admin, _ := SelectUser(1) + email := &types.Email{ + To: admin.Email, + Subject: fmt.Sprintf("Service %v is Down", s.Name), + Template: "failure.html", + Data: data, + From: email.Var1, + } + notifications.SendEmail(EmailBox, email) } } diff --git a/core/setup.go b/core/setup.go index 65835378..7ae5a5d8 100644 --- a/core/setup.go +++ b/core/setup.go @@ -1,23 +1,30 @@ package core import ( + "github.com/hunterlong/statup/types" "github.com/hunterlong/statup/utils" "os" ) func InsertDefaultComms() { - emailer := &Communication{ - Method: "email", - Removable: false, - Enabled: false, + emailer := SelectCommunication(1) + if emailer == nil { + emailer := &types.Communication{ + Method: "email", + Removable: false, + Enabled: false, + } + Create(emailer) } - Create(emailer) - slack := &Communication{ - Method: "slack", - Removable: false, - Enabled: false, + slack := SelectCommunication(2) + if slack == nil { + slack := &types.Communication{ + Method: "slack", + Removable: false, + Enabled: false, + } + Create(slack) } - Create(slack) } func DeleteConfig() { @@ -82,12 +89,14 @@ func LoadSampleData() error { } checkin.Create() - for i := 0; i < 20; i++ { - s1.Check() - s2.Check() - s3.Check() - s4.Check() - } + //for i := 0; i < 3; i++ { + // s1.Check() + // s2.Check() + // s3.Check() + // s4.Check() + //} + + utils.Log(1, "Sample data has finished importing") return nil } diff --git a/emailer.go b/emailer.go deleted file mode 100644 index 545e64e8..00000000 --- a/emailer.go +++ /dev/null @@ -1,114 +0,0 @@ -package main - -import ( - "bytes" - "crypto/tls" - "fmt" - "github.com/hunterlong/statup/core" - "github.com/hunterlong/statup/types" - "github.com/hunterlong/statup/utils" - "gopkg.in/gomail.v2" - "html/template" - "time" -) - -var ( - emailQue *Que -) - -type Email types.Email - -type Que struct { - Mailer *gomail.Dialer - Outgoing []*Email - LastSent int - LastSentTime time.Time -} - -func AddEmail(email *Email) { - if emailQue == nil { - return - } - emailQue.Outgoing = append(emailQue.Outgoing, email) -} - -func EmailerQueue() { - if emailQue == nil { - return - } - uniques := []*Email{} - for _, out := range emailQue.Outgoing { - if isUnique(uniques, out) { - msg := fmt.Sprintf("sending email to: %v \n", out.To) - Send(out) - utils.Log(0, msg) - uniques = append(uniques, out) - } - } - emailQue.Outgoing = nil - fmt.Println("running emailer queue") - time.Sleep(60 * time.Second) - EmailerQueue() -} - -func isUnique(arr []*Email, obj *Email) bool { - for _, v := range arr { - if v.To == obj.To && v.Subject == obj.Subject { - return false - } - } - return true -} - -func Send(em *Email) { - source := EmailTemplate(em.Template, em.Data) - m := gomail.NewMessage() - m.SetHeader("From", "info@betatude.com") - m.SetHeader("To", em.To) - m.SetHeader("Subject", em.Subject) - m.SetBody("text/html", source) - if err := emailQue.Mailer.DialAndSend(m); err != nil { - utils.Log(2, err) - } - emailQue.LastSent++ - emailQue.LastSentTime = time.Now() -} - -func SendFailureEmail(service *core.Service) { - email := &Email{ - To: "info@socialeck.com", - Subject: fmt.Sprintf("Service %v is Failing", service.Name), - Template: "failure.html", - Data: service, - } - AddEmail(email) -} - -func LoadMailer(config *core.Communication) *gomail.Dialer { - if config.Host == "" || config.Username == "" || config.Password == "" { - return nil - } - emailQue = new(Que) - emailQue.Outgoing = []*Email{} - emailQue.Mailer = gomail.NewDialer(config.Host, config.Port, config.Username, config.Password) - emailQue.Mailer.TLSConfig = &tls.Config{InsecureSkipVerify: true} - return emailQue.Mailer -} - -func EmailTemplate(tmpl string, data interface{}) string { - emailTpl, err := core.EmailBox.String(tmpl) - if err != nil { - utils.Log(3, err) - } - t := template.New("email") - t, err = t.Parse(emailTpl) - if err != nil { - utils.Log(3, err) - } - var tpl bytes.Buffer - if err := t.Execute(&tpl, data); err != nil { - utils.Log(2, err) - } - result := tpl.String() - return result -} diff --git a/handlers/misc.go b/handlers/misc.go index 03ec423c..99ead482 100644 --- a/handlers/misc.go +++ b/handlers/misc.go @@ -7,4 +7,4 @@ import ( func Error404Handler(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotFound) ExecuteResponse(w, r, "error_404.html", nil) -} +} \ No newline at end of file diff --git a/handlers/settings.go b/handlers/settings.go index f444c406..daec9cd2 100644 --- a/handlers/settings.go +++ b/handlers/settings.go @@ -3,17 +3,17 @@ package handlers import ( "fmt" "github.com/hunterlong/statup/core" + "github.com/hunterlong/statup/notifications" + "github.com/hunterlong/statup/types" "github.com/hunterlong/statup/utils" "net/http" ) func PluginsHandler(w http.ResponseWriter, r *http.Request) { - auth := IsAuthenticated(r) - if !auth { + if !IsAuthenticated(r) { http.Redirect(w, r, "/", http.StatusSeeOther) return } - //CoreApp.FetchPluginRepo() //var pluginFields []PluginSelect @@ -31,8 +31,7 @@ func PluginsHandler(w http.ResponseWriter, r *http.Request) { } func SaveSettingsHandler(w http.ResponseWriter, r *http.Request) { - auth := IsAuthenticated(r) - if !auth { + if !IsAuthenticated(r) { http.Redirect(w, r, "/", http.StatusSeeOther) return } @@ -63,8 +62,7 @@ func SaveSettingsHandler(w http.ResponseWriter, r *http.Request) { } func SaveSASSHandler(w http.ResponseWriter, r *http.Request) { - auth := IsAuthenticated(r) - if !auth { + if !IsAuthenticated(r) { http.Redirect(w, r, "/", http.StatusSeeOther) return } @@ -78,8 +76,7 @@ func SaveSASSHandler(w http.ResponseWriter, r *http.Request) { } func SaveAssetsHandler(w http.ResponseWriter, r *http.Request) { - auth := IsAuthenticated(r) - if !auth { + if !IsAuthenticated(r) { http.Redirect(w, r, "/", http.StatusSeeOther) return } @@ -89,45 +86,64 @@ func SaveAssetsHandler(w http.ResponseWriter, r *http.Request) { } func SaveEmailSettingsHandler(w http.ResponseWriter, r *http.Request) { - auth := IsAuthenticated(r) - if !auth { + if !IsAuthenticated(r) { http.Redirect(w, r, "/", http.StatusSeeOther) return } emailer := core.SelectCommunication(1) - r.ParseForm() - emailer.Host = r.PostForm.Get("host") - emailer.Username = r.PostForm.Get("username") - emailer.Password = r.PostForm.Get("password") - emailer.Port = int(utils.StringInt(r.PostForm.Get("port"))) - emailer.Var1 = r.PostForm.Get("address") + smtpHost := r.PostForm.Get("host") + smtpUser := r.PostForm.Get("username") + smtpPass := r.PostForm.Get("password") + smtpPort := int(utils.StringInt(r.PostForm.Get("port"))) + smtpOutgoing := r.PostForm.Get("address") + enabled := r.PostForm.Get("enable_email") + + emailer.Host = smtpHost + emailer.Username = smtpUser + if smtpPass != "#######################" { + emailer.Password = smtpPass + } + emailer.Port = smtpPort + emailer.Var1 = smtpOutgoing + emailer.Enabled = false + if enabled == "on" { + emailer.Enabled = true + } core.Update(emailer) - //sample := &Email{ - // To: SessionUser(r).Email, - // Subject: "Sample Email", - // Template: "error.html", - //} - //AddEmail(sample) + sample := &types.Email{ + To: SessionUser(r).Email, + Subject: "Test Email", + Template: "message.html", + From: emailer.Var1, + } + notifications.LoadEmailer(emailer) + notifications.SendEmail(core.EmailBox, sample) + notifications.EmailComm = emailer + if emailer.Enabled { + utils.Log(1, "Starting Email Routine, 1 unique email per 60 seconds") + go notifications.EmailRoutine() + } http.Redirect(w, r, "/settings", http.StatusSeeOther) } func SaveSlackSettingsHandler(w http.ResponseWriter, r *http.Request) { - auth := IsAuthenticated(r) - if !auth { + if !IsAuthenticated(r) { http.Redirect(w, r, "/", http.StatusSeeOther) return } slack := core.SelectCommunication(2) r.ParseForm() - slack.Host = r.PostForm.Get("host") - slack.Enabled = true - if slack.Host == "" { - slack.Enabled = false + slackWebhook := r.PostForm.Get("slack_url") + enable := r.PostForm.Get("enable_slack") + slack.Enabled = false + if enable == "on" && slackWebhook != "" { + slack.Enabled = true + go notifications.SlackRoutine() } + slack.Host = slackWebhook core.Update(slack) - core.SendSlackMessage("This is a test from Statup!") http.Redirect(w, r, "/settings", http.StatusSeeOther) } diff --git a/handlers/setup.go b/handlers/setup.go index 05cdc011..ac8b4904 100644 --- a/handlers/setup.go +++ b/handlers/setup.go @@ -7,6 +7,7 @@ import ( "net/http" "os" "strconv" + "time" ) func SetupHandler(w http.ResponseWriter, r *http.Request) { @@ -14,6 +15,7 @@ func SetupHandler(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/", http.StatusSeeOther) return } + w.WriteHeader(http.StatusOK) port := 5432 if os.Getenv("DB_CONN") == "mysql" { port = 3306 @@ -109,15 +111,13 @@ func ProcessSetupHandler(w http.ResponseWriter, r *http.Request) { } admin.Create() - core.InsertDefaultComms() - if sample == "on" { - go core.LoadSampleData() + core.LoadSampleData() } - core.SelectCore() + core.InitApp() + time.Sleep(2 * time.Second) http.Redirect(w, r, "/", http.StatusSeeOther) - //mainProcess() } func SetupResponseError(w http.ResponseWriter, r *http.Request, a interface{}) { diff --git a/handlers/users.go b/handlers/users.go index b7c69218..806b5a90 100644 --- a/handlers/users.go +++ b/handlers/users.go @@ -1,7 +1,6 @@ package handlers import ( - "fmt" "github.com/gorilla/mux" "github.com/hunterlong/statup/core" "github.com/hunterlong/statup/types" @@ -39,15 +38,17 @@ func CreateUserHandler(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/", http.StatusSeeOther) return } - fmt.Println("creating user") r.ParseForm() username := r.PostForm.Get("username") password := r.PostForm.Get("password") email := r.PostForm.Get("email") + admin := r.PostForm.Get("admin") + user := &core.User{ Username: username, Password: password, Email: email, + Admin: (admin == "on"), } _, err := user.Create() if err != nil { diff --git a/main.go b/main.go index 73391311..81830347 100644 --- a/main.go +++ b/main.go @@ -65,20 +65,7 @@ func mainProcess() { utils.Log(3, err) } core.RunDatabaseUpgrades() - core.CoreApp, err = core.SelectCore() - if err != nil { - utils.Log(2, "Core database was not found, Statup is not setup yet.") - handlers.RunHTTPServer() - } - - core.CheckServices() - core.CoreApp.Communications, err = core.SelectAllCommunications() - if err != nil { - utils.Log(2, err) - } - core.LoadDefaultCommunications() - - go core.DatabaseMaintence() + core.InitApp() if !core.SetupMode { LoadPlugins() diff --git a/notifications/email.go b/notifications/email.go new file mode 100644 index 00000000..49001ee5 --- /dev/null +++ b/notifications/email.go @@ -0,0 +1,105 @@ +package notifications + +import ( + "bytes" + "crypto/tls" + "fmt" + "github.com/GeertJohan/go.rice" + "github.com/hunterlong/statup/types" + "github.com/hunterlong/statup/utils" + "gopkg.in/gomail.v2" + "html/template" + "time" +) + +var ( + mailer *gomail.Dialer + emailQueue []*types.Email + EmailComm *types.Communication +) + +func EmailRoutine() { + var sentAddresses []string + for _, email := range emailQueue { + if inArray(sentAddresses, email.To) || email.Sent { + emailQueue = removeEmail(emailQueue, email) + continue + } + e := email + go func(email *types.Email) { + err := dialSend(email) + if err == nil { + email.Sent = true + sentAddresses = append(sentAddresses, email.To) + utils.Log(1, fmt.Sprintf("Email '%v' sent to: %v using the %v template (size: %v)", email.Subject, email.To, email.Template, len([]byte(email.Source)))) + emailQueue = removeEmail(emailQueue, email) + } + }(e) + } + time.Sleep(60 * time.Second) + if EmailComm.Enabled { + EmailRoutine() + } +} + +func dialSend(email *types.Email) error { + m := gomail.NewMessage() + m.SetHeader("From", email.From) + m.SetHeader("To", email.To) + m.SetHeader("Subject", email.Subject) + m.SetBody("text/html", email.Source) + if err := mailer.DialAndSend(m); err != nil { + utils.Log(3, fmt.Sprintf("Email '%v' sent to: %v using the %v template (size: %v) %v", email.Subject, email.To, email.Template, len([]byte(email.Source)), err)) + return err + } + return nil +} + +func LoadEmailer(mail *types.Communication) { + utils.Log(1, fmt.Sprintf("Loading SMTP Emailer using host: %v:%v", mail.Host, mail.Port)) + mailer = gomail.NewDialer(mail.Host, mail.Port, mail.Username, mail.Password) + mailer.TLSConfig = &tls.Config{InsecureSkipVerify: true} +} + +func SendEmail(box *rice.Box, email *types.Email) { + source := EmailTemplate(box, email.Template, email.Data) + email.Source = source + emailQueue = append(emailQueue, email) +} + +func EmailTemplate(box *rice.Box, tmpl string, data interface{}) string { + emailTpl, err := box.String(tmpl) + if err != nil { + utils.Log(3, err) + } + t := template.New("email") + t, err = t.Parse(emailTpl) + if err != nil { + utils.Log(3, err) + } + var tpl bytes.Buffer + if err := t.Execute(&tpl, data); err != nil { + utils.Log(2, err) + } + result := tpl.String() + return result +} + +func removeEmail(emails []*types.Email, em *types.Email) []*types.Email { + var newArr []*types.Email + for _, e := range emails { + if e != em { + newArr = append(newArr, e) + } + } + return newArr +} + +func inArray(a []string, v string) bool { + for _, i := range a { + if i == v { + return true + } + } + return false +} diff --git a/notifications/slack.go b/notifications/slack.go new file mode 100644 index 00000000..f6e78480 --- /dev/null +++ b/notifications/slack.go @@ -0,0 +1,61 @@ +package notifications + +import ( + "bytes" + "fmt" + "github.com/hunterlong/statup/types" + "github.com/hunterlong/statup/utils" + "github.com/pkg/errors" + "net/http" + "time" +) + +var ( + slackUrl string + sentLastMin int + slackMessages []string + SlackComm *types.Communication +) + +func LoadSlack(url string) { + if url == "" { + utils.Log(1, "Slack Webhook URL is empty") + return + } + slackUrl = url +} + +func SlackRoutine() { + for _, msg := range slackMessages { + utils.Log(1, fmt.Sprintf("Sending JSON to Slack Webhook: %v", msg)) + client := http.Client{Timeout: 15 * time.Second} + _, err := client.Post(slackUrl, "application/json", bytes.NewBuffer([]byte(msg))) + if err != nil { + utils.Log(3, fmt.Sprintf("Issue sending Slack notification: %v", err)) + } + slackMessages = removeStrArray(slackMessages, msg) + } + time.Sleep(60 * time.Second) + if SlackComm.Enabled { + SlackRoutine() + } +} + +func removeStrArray(arr []string, v string) []string { + var newArray []string + for _, i := range arr { + if i != v { + newArray = append(newArray, v) + } + } + return newArray +} + +func SendSlack(msg string) error { + if slackUrl == "" { + return errors.New("Slack Webhook URL has not been set in settings") + } + fullMessage := fmt.Sprintf("{\"text\":\"%v\"}", msg) + slackMessages = append(slackMessages, fullMessage) + return nil +} diff --git a/source/css/base.css b/source/css/base.css index 0475078d..2d70f354 100644 --- a/source/css/base.css +++ b/source/css/base.css @@ -189,6 +189,117 @@ HTML, BODY { box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; } +.switch { + font-size: 1rem; + position: relative; } + +.switch input { + position: absolute; + height: 1px; + width: 1px; + background: none; + border: 0; + clip: rect(0 0 0 0); + clip-path: inset(50%); + overflow: hidden; + padding: 0; } + +.switch input + label { + position: relative; + min-width: calc(calc(2.375rem * .8) * 2); + border-radius: calc(2.375rem * .8); + height: calc(2.375rem * .8); + line-height: calc(2.375rem * .8); + display: inline-block; + cursor: pointer; + outline: none; + user-select: none; + vertical-align: middle; + text-indent: calc(calc(calc(2.375rem * .8) * 2) + .5rem); } + +.switch input + label::before, +.switch input + label::after { + content: ''; + position: absolute; + top: 0; + left: 0; + width: calc(calc(2.375rem * .8) * 2); + bottom: 0; + display: block; } + +.switch input + label::before { + right: 0; + background-color: #dee2e6; + border-radius: calc(2.375rem * .8); + transition: 0.2s all; } + +.switch input + label::after { + top: 2px; + left: 2px; + width: calc(calc(2.375rem * .8) - calc(2px * 2)); + height: calc(calc(2.375rem * .8) - calc(2px * 2)); + border-radius: 50%; + background-color: white; + transition: 0.2s all; } + +.switch input:checked + label::before { + background-color: #08d; } + +.switch input:checked + label::after { + margin-left: calc(2.375rem * .8); } + +.switch input:focus + label::before { + outline: none; + box-shadow: 0 0 0 0.2rem rgba(0, 136, 221, 0.25); } + +.switch input:disabled + label { + color: #868e96; + cursor: not-allowed; } + +.switch input:disabled + label::before { + background-color: #e9ecef; } + +.switch.switch-sm { + font-size: 0.875rem; } + +.switch.switch-sm input + label { + min-width: calc(calc(1.9375rem * .8) * 2); + height: calc(1.9375rem * .8); + line-height: calc(1.9375rem * .8); + text-indent: calc(calc(calc(1.9375rem * .8) * 2) + .5rem); } + +.switch.switch-sm input + label::before { + width: calc(calc(1.9375rem * .8) * 2); } + +.switch.switch-sm input + label::after { + width: calc(calc(1.9375rem * .8) - calc(2px * 2)); + height: calc(calc(1.9375rem * .8) - calc(2px * 2)); } + +.switch.switch-sm input:checked + label::after { + margin-left: calc(1.9375rem * .8); } + +.switch.switch-lg { + font-size: 1.25rem; } + +.switch.switch-lg input + label { + min-width: calc(calc(3rem * .8) * 2); + height: calc(3rem * .8); + line-height: calc(3rem * .8); + text-indent: calc(calc(calc(3rem * .8) * 2) + .5rem); } + +.switch.switch-lg input + label::before { + width: calc(calc(3rem * .8) * 2); } + +.switch.switch-lg input + label::after { + width: calc(calc(3rem * .8) - calc(2px * 2)); + height: calc(calc(3rem * .8) - calc(2px * 2)); } + +.switch.switch-lg input:checked + label::after { + margin-left: calc(3rem * .8); } + +.switch + .switch { + margin-left: 1rem; } + @keyframes pulse_animation { 0% { transform: scale(1); } diff --git a/source/css/base.css.map b/source/css/base.css.map index ca50c1a5..9729d73d 100644 --- a/source/css/base.css.map +++ b/source/css/base.css.map @@ -1,6 +1,6 @@ { "version": 3, -"mappings": "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", +"mappings": "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", "sources": ["../scss/variables.scss","../scss/base.scss","../scss/mobile.scss"], "names": [], "file": "base.css" diff --git a/source/emails/failure.html b/source/emails/failure.html index f3abd13e..ae964e3c 100644 --- a/source/emails/failure.html +++ b/source/emails/failure.html @@ -36,19 +36,24 @@ -

{{ .Name }} is Offline!

+

{{ .Service.Name }} is Offline!

- Your Statup service named '{{.Name}}' has been triggered with a HTTP status code of '{{.LastStatusCode}}' and is currently offline based on your requirements. + Your Statup service '{{.Service.Name}}' 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}}.

+ {{if .Service.LastResponse }}

Last Response

- {{ .LastResponse }} + {{ .Service.LastResponse }}

+ {{end}} +
- View Service + View Service + + Statup Dashboard
diff --git a/source/emails/error.html b/source/emails/message.html similarity index 98% rename from source/emails/error.html rename to source/emails/message.html index 51e79466..10d861e4 100644 --- a/source/emails/error.html +++ b/source/emails/message.html @@ -36,7 +36,7 @@ -

Looks like emails work!

+

Looks Like Emails Work!

Since you got this email, it confirms that your Statup Status Page email system is working correctly.

diff --git a/source/scss/base.scss b/source/scss/base.scss index 1f86653e..0030370b 100644 --- a/source/scss/base.scss +++ b/source/scss/base.scss @@ -242,6 +242,119 @@ HTML,BODY { transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; } +.switch { + font-size: 1rem; + position: relative; +} +.switch input { + position: absolute; + height: 1px; + width: 1px; + background: none; + border: 0; + clip: rect(0 0 0 0); + clip-path: inset(50%); + overflow: hidden; + padding: 0; +} +.switch input + label { + position: relative; + min-width: calc(calc(2.375rem * .8) * 2); + border-radius: calc(2.375rem * .8); + height: calc(2.375rem * .8); + line-height: calc(2.375rem * .8); + display: inline-block; + cursor: pointer; + outline: none; + user-select: none; + vertical-align: middle; + text-indent: calc(calc(calc(2.375rem * .8) * 2) + .5rem); +} +.switch input + label::before, +.switch input + label::after { + content: ''; + position: absolute; + top: 0; + left: 0; + width: calc(calc(2.375rem * .8) * 2); + bottom: 0; + display: block; +} +.switch input + label::before { + right: 0; + background-color: #dee2e6; + border-radius: calc(2.375rem * .8); + transition: 0.2s all; +} +.switch input + label::after { + top: 2px; + left: 2px; + width: calc(calc(2.375rem * .8) - calc(2px * 2)); + height: calc(calc(2.375rem * .8) - calc(2px * 2)); + border-radius: 50%; + background-color: white; + transition: 0.2s all; +} +.switch input:checked + label::before { + background-color: #08d; +} +.switch input:checked + label::after { + margin-left: calc(2.375rem * .8); +} +.switch input:focus + label::before { + outline: none; + box-shadow: 0 0 0 0.2rem rgba(0, 136, 221, 0.25); +} +.switch input:disabled + label { + color: #868e96; + cursor: not-allowed; +} +.switch input:disabled + label::before { + background-color: #e9ecef; +} +.switch.switch-sm { + font-size: 0.875rem; +} +.switch.switch-sm input + label { + min-width: calc(calc(1.9375rem * .8) * 2); + height: calc(1.9375rem * .8); + line-height: calc(1.9375rem * .8); + text-indent: calc(calc(calc(1.9375rem * .8) * 2) + .5rem); +} +.switch.switch-sm input + label::before { + width: calc(calc(1.9375rem * .8) * 2); +} +.switch.switch-sm input + label::after { + width: calc(calc(1.9375rem * .8) - calc(2px * 2)); + height: calc(calc(1.9375rem * .8) - calc(2px * 2)); +} +.switch.switch-sm input:checked + label::after { + margin-left: calc(1.9375rem * .8); +} +.switch.switch-lg { + font-size: 1.25rem; +} +.switch.switch-lg input + label { + min-width: calc(calc(3rem * .8) * 2); + height: calc(3rem * .8); + line-height: calc(3rem * .8); + text-indent: calc(calc(calc(3rem * .8) * 2) + .5rem); +} +.switch.switch-lg input + label::before { + width: calc(calc(3rem * .8) * 2); +} +.switch.switch-lg input + label::after { + width: calc(calc(3rem * .8) - calc(2px * 2)); + height: calc(calc(3rem * .8) - calc(2px * 2)); +} +.switch.switch-lg input:checked + label::after { + margin-left: calc(3rem * .8); +} +.switch + .switch { + margin-left: 1rem; +} + + @keyframes pulse_animation { 0% { transform: scale(1); } 30% { transform: scale(1); } diff --git a/source/tmpl/service.html b/source/tmpl/service.html index c31398d6..74f1c945 100644 --- a/source/tmpl/service.html +++ b/source/tmpl/service.html @@ -79,22 +79,22 @@
- +
- +
@@ -121,7 +121,7 @@
- +
@@ -133,7 +133,7 @@
- +
diff --git a/source/tmpl/services.html b/source/tmpl/services.html index ced73e8b..f9523f41 100644 --- a/source/tmpl/services.html +++ b/source/tmpl/services.html @@ -50,7 +50,7 @@
- +
@@ -65,7 +65,7 @@
- +
@@ -104,7 +104,7 @@
- +
diff --git a/source/tmpl/settings.html b/source/tmpl/settings.html index 36ffe08b..4de6ebee 100644 --- a/source/tmpl/settings.html +++ b/source/tmpl/settings.html @@ -105,7 +105,7 @@
- {{end}} + {{end}}
{{ with $c := index .Communications 0 }} @@ -125,7 +125,7 @@
- +
@@ -143,7 +143,19 @@
- +
+
+ + + + +
+ +
+ +
+
+ @@ -151,22 +163,34 @@ {{ end }} -{{ with $c := index .Communications 1 }} -
+ {{ with $c := index .Communications 1 }} +
-
+ -
- - -
+
+ + +
- +
+
+ + + + +
- +
+ +
+
-
-{{ end }} + + + +
+ {{ end }}
{{ range .Repos }} diff --git a/source/tmpl/users.html b/source/tmpl/users.html index ce5cb220..adf4b3da 100644 --- a/source/tmpl/users.html +++ b/source/tmpl/users.html @@ -44,31 +44,37 @@
-
- +
+ +
+
+ + + +
- +
- +
- +
-
- +
+
diff --git a/types/types.go b/types/types.go index 94efca33..137bff11 100644 --- a/types/types.go +++ b/types/types.go @@ -61,7 +61,10 @@ type Email struct { To string Subject string Template string + From string Data interface{} + Source string + Sent bool } type Config struct {