diff --git a/Makefile b/Makefile index 1b4bed6f..05a1e1e8 100644 --- a/Makefile +++ b/Makefile @@ -232,7 +232,7 @@ dev-deps: $(GOGET) github.com/ararog/timeago $(GOGET) gopkg.in/natefinch/lumberjack.v2 $(GOGET) golang.org/x/crypto/bcrypt - $(GOGET) github.com/99designs/gqlgen + $(GOGET) github.com/99designs/gqlgen/... # remove files for a clean compile/build clean: diff --git a/core/checker.go b/core/checker.go index 983f5110..3b640ec4 100644 --- a/core/checker.go +++ b/core/checker.go @@ -263,6 +263,7 @@ func recordSuccess(s *Service) { s.CreateHit(hit) notifier.OnSuccess(s.Service) s.Online = true + s.SuccessNotified = true } // recordFailure will create a new 'Failure' record in the database for a offline service @@ -276,6 +277,9 @@ func recordFailure(s *Service, issue string) { }} utils.Log(2, fmt.Sprintf("Service %v Failing: %v | Lookup in: %0.2f ms", s.Name, issue, fail.PingTime*1000)) s.CreateFailure(fail) - notifier.OnFailure(s.Service, fail.Failure) s.Online = false + s.SuccessNotified = false + s.UpdateNotify = CoreApp.UpdateNotify.Bool + s.DownText = s.DowntimeText() + notifier.OnFailure(s.Service, fail.Failure) } diff --git a/core/notifier/events.go b/core/notifier/events.go index de9eb1b5..a6a93246 100644 --- a/core/notifier/events.go +++ b/core/notifier/events.go @@ -38,6 +38,19 @@ func OnFailure(s *types.Service, f *types.Failure) { if !s.AllowNotifications.Bool { return } + + // check if User wants to receive every Status Change + if s.UpdateNotify { + // send only if User hasn't been already notified about the Downtime + if !s.UserNotified { + s.UserNotified = true + goto sendMessages + } else { + return + } + } + +sendMessages: for _, comm := range AllCommunications { if isType(comm, new(BasicEvents)) && isEnabled(comm) && inLimits(comm) { notifier := comm.(Notifier).Select() @@ -45,7 +58,6 @@ func OnFailure(s *types.Service, f *types.Failure) { comm.(BasicEvents).OnFailure(s, f) } } - } // OnSuccess will be triggered when a service is successful - BasicEvents interface @@ -53,6 +65,12 @@ func OnSuccess(s *types.Service) { if !s.AllowNotifications.Bool { return } + + // check if User wants to receive every Status Change + if s.UpdateNotify && s.UserNotified { + s.UserNotified = false + } + for _, comm := range AllCommunications { if isType(comm, new(BasicEvents)) && isEnabled(comm) && inLimits(comm) { notifier := comm.(Notifier).Select() @@ -60,7 +78,6 @@ func OnSuccess(s *types.Service) { comm.(BasicEvents).OnSuccess(s) } } - } // OnNewService is triggered when a new service is created - ServiceEvents interface diff --git a/handlers/function.go b/handlers/function.go index d17f1452..7ac44649 100644 --- a/handlers/function.go +++ b/handlers/function.go @@ -63,6 +63,9 @@ var handlerFuncs = func(w http.ResponseWriter, r *http.Request) template.FuncMap "USE_CDN": func() bool { return core.CoreApp.UseCdn.Bool }, + "UPDATENOTIFY": func() bool { + return core.CoreApp.UpdateNotify.Bool + }, "QrAuth": func() string { return fmt.Sprintf("statping://setup?domain=%v&api=%v", core.CoreApp.Domain, core.CoreApp.ApiSecret) }, diff --git a/handlers/settings.go b/handlers/settings.go index 89b5839b..23d0689f 100644 --- a/handlers/settings.go +++ b/handlers/settings.go @@ -62,11 +62,15 @@ func saveSettingsHandler(w http.ResponseWriter, r *http.Request) { timeFloat, _ := strconv.ParseFloat(timezone, 10) app.Timezone = float32(timeFloat) + app.UpdateNotify = types.NewNullBool(form.Get("update_notify") == "true") + app.UseCdn = types.NewNullBool(form.Get("enable_cdn") == "on") core.CoreApp, err = core.UpdateCore(app) if err != nil { utils.Log(3, fmt.Sprintf("issue updating Core: %v", err.Error())) } + + //notifiers.OnSettingsSaved(core.CoreApp.ToCore()) ExecuteResponse(w, r, "settings.gohtml", core.CoreApp, "/settings") } @@ -144,7 +148,7 @@ func bulkImportHandler(w http.ResponseWriter, r *http.Request) { // commaToService will convert a CSV comma delimited string slice to a Service type // this function is used for the bulk import services feature func commaToService(s []string) (*types.Service, error) { - if len(s) != 16 { + if len(s) != 17 { err := fmt.Errorf("does not have the expected amount of %v columns for a service", 16) return nil, err } @@ -169,6 +173,11 @@ func commaToService(s []string) (*types.Service, error) { return nil, err } + verifySsl, err := strconv.ParseBool(s[16]) + if err != nil { + return nil, err + } + newService := &types.Service{ Name: s[0], Domain: s[1], @@ -185,6 +194,7 @@ func commaToService(s []string) (*types.Service, error) { GroupId: int(utils.ToInt(s[13])), Headers: types.NewNullString(s[14]), Permalink: types.NewNullString(s[15]), + VerifySSL: types.NewNullBool(verifySsl), } return newService, nil diff --git a/notifiers/discord.go b/notifiers/discord.go index 82cb1c70..2d9c345b 100644 --- a/notifiers/discord.go +++ b/notifiers/discord.go @@ -75,9 +75,14 @@ func (u *discord) OnFailure(s *types.Service, f *types.Failure) { // OnSuccess will trigger successful service func (u *discord) OnSuccess(s *types.Service) { - if !s.Online { + if !s.Online || !s.SuccessNotified { u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id)) - msg := fmt.Sprintf(`{"content": "Your service '%v' is back online!"}`, s.Name) + var msg interface{} + if s.UpdateNotify { + s.UpdateNotify = false + } + msg = s.DownText + u.AddQueue(fmt.Sprintf("service_%v", s.Id), msg) } } diff --git a/notifiers/email.go b/notifiers/email.go index dd08e355..d57beee1 100644 --- a/notifiers/email.go +++ b/notifiers/email.go @@ -198,11 +198,17 @@ func (u *email) OnFailure(s *types.Service, f *types.Failure) { // OnSuccess will trigger successful service func (u *email) OnSuccess(s *types.Service) { - if !s.Online { + if !s.Online || !s.SuccessNotified { + var msg string + if s.UpdateNotify { + s.UpdateNotify = false + } + msg = s.DownText + u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id)) email := &emailOutgoing{ To: u.Var2, - Subject: fmt.Sprintf("Service %v is Back Online", s.Name), + Subject: msg, Template: mainEmailTemplate, Data: interface{}(s), From: u.Var1, diff --git a/notifiers/line_notify.go b/notifiers/line_notify.go index 4e5cd3f8..7409f249 100644 --- a/notifiers/line_notify.go +++ b/notifiers/line_notify.go @@ -78,16 +78,22 @@ func (u *lineNotifier) OnFailure(s *types.Service, f *types.Failure) { // OnSuccess will trigger successful service func (u *lineNotifier) OnSuccess(s *types.Service) { - if !s.Online { + if !s.Online || !s.SuccessNotified { + var msg string + if s.UpdateNotify { + s.UpdateNotify = false + } + msg = s.DownText + u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id)) - msg := fmt.Sprintf("Your service '%v' is back online!", s.Name) u.AddQueue(fmt.Sprintf("service_%v", s.Id), msg) } } // OnSave triggers when this notifier has been saved func (u *lineNotifier) OnSave() error { - utils.Log(1, fmt.Sprintf("Notification %v is receiving updated information.", u.Method)) - // Do updating stuff here + msg := fmt.Sprintf("Notification %v is receiving updated information.", u.Method) + utils.Log(1, msg) + u.AddQueue("saved", message) return nil } diff --git a/notifiers/mobile.go b/notifiers/mobile.go index 3cceebf1..ed238580 100644 --- a/notifiers/mobile.go +++ b/notifiers/mobile.go @@ -105,10 +105,16 @@ func (u *mobilePush) OnFailure(s *types.Service, f *types.Failure) { // OnSuccess will trigger successful service func (u *mobilePush) OnSuccess(s *types.Service) { data := dataJson(s, nil) - if !s.Online { + if !s.Online || !s.SuccessNotified { + var msgStr string + if s.UpdateNotify { + s.UpdateNotify = false + } + msgStr = s.DownText + u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id)) msg := &pushArray{ - Message: fmt.Sprintf("Your service '%v' is back online!", s.Name), + Message: msgStr, Title: "Service Online", Topic: mobileIdentifier, Data: data, diff --git a/notifiers/telegram.go b/notifiers/telegram.go index 000b3292..6215cf43 100644 --- a/notifiers/telegram.go +++ b/notifiers/telegram.go @@ -97,9 +97,14 @@ func (u *telegram) OnFailure(s *types.Service, f *types.Failure) { // OnSuccess will trigger successful service func (u *telegram) OnSuccess(s *types.Service) { - if !s.Online { + if !s.Online || !s.SuccessNotified { u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id)) - msg := fmt.Sprintf("Your service '%v' is back online!", s.Name) + var msg interface{} + if s.UpdateNotify { + s.UpdateNotify = false + } + msg = s.DownText + u.AddQueue(fmt.Sprintf("service_%v", s.Id), msg) } } diff --git a/notifiers/twilio.go b/notifiers/twilio.go index 6dcf9fbd..e660173d 100644 --- a/notifiers/twilio.go +++ b/notifiers/twilio.go @@ -107,9 +107,14 @@ func (u *twilio) OnFailure(s *types.Service, f *types.Failure) { // OnSuccess will trigger successful service func (u *twilio) OnSuccess(s *types.Service) { - if !s.Online { + if !s.Online || !s.SuccessNotified { u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id)) - msg := fmt.Sprintf("Your service '%v' is back online!", s.Name) + var msg string + if s.UpdateNotify { + s.UpdateNotify = false + } + msg = s.DownText + u.AddQueue(fmt.Sprintf("service_%v", s.Id), msg) } } diff --git a/source/js/main.js b/source/js/main.js index 3ddf566d..ee0a1db1 100644 --- a/source/js/main.js +++ b/source/js/main.js @@ -90,7 +90,7 @@ $('.toggle-service').on('click',function(e) { let obj = $(this); let serviceId = obj.attr("data-id"); let online = obj.attr("data-online"); - let d = confirm("Do you want to "+(online ? "stop" : "start")+" checking this service?"); + let d = confirm("Do you want to "+(eval(online) ? "stop" : "start")+" checking this service?"); if (d) { $.ajax({ url: "/api/services/" + serviceId + "/running", diff --git a/source/tmpl/settings.gohtml b/source/tmpl/settings.gohtml index 39cd66b1..05324939 100644 --- a/source/tmpl/settings.gohtml +++ b/source/tmpl/settings.gohtml @@ -37,6 +37,20 @@ +
+
+ + + + + + + + + +
+
+
diff --git a/types/core.go b/types/core.go index 03756cc2..ca73c684 100644 --- a/types/core.go +++ b/types/core.go @@ -37,6 +37,7 @@ type Core struct { Version string `gorm:"column:version" json:"version"` MigrationId int64 `gorm:"column:migration_id" json:"migration_id,omitempty"` UseCdn NullBool `gorm:"column:use_cdn;default:false" json:"using_cdn,omitempty"` + UpdateNotify NullBool `gorm:"column:update_notify;default:false" json:"update_notify,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"` diff --git a/types/service.go b/types/service.go index f0435dba..a904f980 100644 --- a/types/service.go +++ b/types/service.go @@ -50,6 +50,10 @@ type Service struct { Checkpoint time.Time `gorm:"-" json:"-"` SleepDuration time.Duration `gorm:"-" json:"-"` LastResponse string `gorm:"-" json:"-"` + UserNotified bool `gorm:"-" json:"-"` // True if the User was already notified about a Downtime + UpdateNotify bool `gorm:"-" json:"-"` // This Variable is a simple copy of `core.CoreApp.UpdateNotify.Bool` + DownText string `gorm:"-" json:"-"` // Contains the current generated Downtime Text + SuccessNotified bool `gorm:"-" json:"-"` // Is 'true' if the user has already be informed that the Services now again available LastStatusCode int `gorm:"-" json:"status_code"` LastOnline time.Time `gorm:"-" json:"last_success"` Failures []FailureInterface `gorm:"-" json:"failures,omitempty"` diff --git a/utils/utils.go b/utils/utils.go index 348cb02f..4fdee729 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -16,6 +16,7 @@ package utils import ( + "context" "crypto/tls" "errors" "fmt" @@ -24,6 +25,7 @@ import ( "io/ioutil" "math" "math/rand" + "net" "net/http" "os" "os/exec" @@ -298,6 +300,11 @@ func HttpRequest(url, method string, content interface{}, headers []string, body } var resp *http.Response + dialer := &net.Dialer{ + Timeout: timeout, + KeepAlive: timeout, + } + transport := &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: !verifySSL, @@ -307,6 +314,11 @@ func HttpRequest(url, method string, content interface{}, headers []string, body ResponseHeaderTimeout: timeout, TLSHandshakeTimeout: timeout, Proxy: http.ProxyFromEnvironment, + DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + // redirect all connections to host specified in url + addr = strings.Split(req.URL.Host, ":")[0] + addr[strings.LastIndex(addr, ":"):] + return dialer.DialContext(ctx, network, addr) + }, } client := &http.Client{ Transport: transport,