diff --git a/core/failures.go b/core/failures.go index 49b14b49..2df56aca 100644 --- a/core/failures.go +++ b/core/failures.go @@ -20,6 +20,7 @@ import ( "github.com/ararog/timeago" "github.com/hunterlong/statup/types" "github.com/hunterlong/statup/utils" + "sort" "strings" "time" ) @@ -29,7 +30,7 @@ type failure struct { } const ( - limitedFailures = 64 + limitedFailures = 32 ) // CreateFailure will create a new failure record for a service @@ -41,8 +42,9 @@ func (s *Service) CreateFailure(fail types.FailureInterface) (int64, error) { utils.Log(3, row.Error) return 0, row.Error } - s.Failures = append(s.Failures, f.Select()) - if len(s.Failures) >= limitedFailures { + sort.Sort(types.FailSort(s.Failures)) + s.Failures = append(s.Failures, f) + if len(s.Failures) > limitedFailures { s.Failures = s.Failures[1:] } return f.Id, row.Error diff --git a/core/services.go b/core/services.go index 8b7724e0..028188e1 100644 --- a/core/services.go +++ b/core/services.go @@ -102,10 +102,9 @@ func (c *Core) SelectAllServices(start bool) ([]*Service, error) { service.Start() service.CheckinProcess() } - failures := service.LimitedFailures(limitedFailures) - service.Failures = nil - for _, fail := range failures { - service.Failures = append(service.Failures, fail.Select()) + fails := service.LimitedFailures(limitedFailures) + for _, f := range fails { + service.Failures = append(service.Failures, f) } CoreApp.Services = append(CoreApp.Services, service) } diff --git a/handlers/api.go b/handlers/api.go index 2b529cf1..eeffa79d 100644 --- a/handlers/api.go +++ b/handlers/api.go @@ -128,14 +128,14 @@ func apiServiceHandler(w http.ResponseWriter, r *http.Request) { return } vars := mux.Vars(r) - service := core.SelectServicer(utils.StringInt(vars["id"])) - if service == nil { + servicer := core.SelectServicer(utils.StringInt(vars["id"])) + if servicer == nil { sendErrorJson(errors.New("service not found"), w, r) return } - + service := servicer.Select() w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(service.Select()) + json.NewEncoder(w).Encode(service) } func apiCreateServiceHandler(w http.ResponseWriter, r *http.Request) { @@ -181,7 +181,7 @@ func apiServiceUpdateHandler(w http.ResponseWriter, r *http.Request) { sendErrorJson(err, w, r) return } - service.Check(true) + go service.Check(true) w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(service) } @@ -218,12 +218,8 @@ func apiAllServicesHandler(w http.ResponseWriter, r *http.Request) { return } services := core.Services() - var servicesOut []*types.Service - for _, s := range services { - servicesOut = append(servicesOut, s.Select()) - } w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(servicesOut) + json.NewEncoder(w).Encode(services) } func apiNotifierGetHandler(w http.ResponseWriter, r *http.Request) { @@ -250,8 +246,11 @@ func apiNotifierUpdateHandler(w http.ResponseWriter, r *http.Request) { var notification *notifier.Notification fmt.Println(r.Body) decoder := json.NewDecoder(r.Body) - decoder.Decode(¬ification) - + err := decoder.Decode(¬ification) + if err != nil { + sendErrorJson(err, w, r) + return + } notifer, not, err := notifier.SelectNotifier(vars["notifier"]) if err != nil { sendErrorJson(err, w, r) @@ -264,9 +263,9 @@ func apiNotifierUpdateHandler(w http.ResponseWriter, r *http.Request) { notifer.Port = notification.Port notifer.Password = notification.Password notifer.Username = notification.Username - notifer.Enabled = notification.Enabled notifer.ApiKey = notification.ApiKey notifer.ApiSecret = notification.ApiSecret + notifer.Enabled = types.NewNullBool(notification.Enabled.Bool) _, err = notifier.Update(not, notifer) if err != nil { @@ -293,6 +292,28 @@ func apiAllMessagesHandler(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(messages) } +func apiMessageCreateHandler(w http.ResponseWriter, r *http.Request) { + if !isAPIAuthorized(r) { + sendUnauthorizedJson(w, r) + return + } + var message *types.Message + decoder := json.NewDecoder(r.Body) + err := decoder.Decode(&message) + if err != nil { + sendErrorJson(err, w, r) + return + } + msg := core.ReturnMessage(message) + _, err = msg.Create() + if err != nil { + sendErrorJson(err, w, r) + return + } + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(msg) +} + func apiMessageGetHandler(w http.ResponseWriter, r *http.Request) { if !isAPIAuthorized(r) { sendUnauthorizedJson(w, r) @@ -388,27 +409,6 @@ func apiNotifiersHandler(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(notifiers) } -func apiAllServiceFailuresHandler(w http.ResponseWriter, r *http.Request) { - if !isAPIAuthorized(r) { - sendUnauthorizedJson(w, r) - return - } - allServices, _ := core.CoreApp.SelectAllServices(false) - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(allServices) -} - -func apiServiceFailuresHandler(w http.ResponseWriter, r *http.Request) { - if !isAPIAuthorized(r) { - sendUnauthorizedJson(w, r) - return - } - vars := mux.Vars(r) - service := core.SelectService(utils.StringInt(vars["id"])) - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(service.AllFailures()) -} - func sendErrorJson(err error, w http.ResponseWriter, r *http.Request) { output := apiResponse{ Status: "error", diff --git a/handlers/handlers_test.go b/handlers/handlers_test.go index dc66ca94..37225957 100644 --- a/handlers/handlers_test.go +++ b/handlers/handlers_test.go @@ -17,7 +17,6 @@ package handlers import ( "github.com/hunterlong/statup/core" - "github.com/hunterlong/statup/core/notifier" _ "github.com/hunterlong/statup/notifiers" "github.com/hunterlong/statup/source" "github.com/hunterlong/statup/utils" @@ -364,34 +363,6 @@ func TestPrometheusHandler(t *testing.T) { assert.True(t, isRouteAuthenticated(req)) } -func TestSaveNotificationHandler(t *testing.T) { - notification, _, err := notifier.SelectNotifier("email") - assert.Nil(t, err) - assert.False(t, notification.IsRunning()) - - form := url.Values{} - form.Add("enable", "on") - form.Add("host", "smtp.emailer.com") - form.Add("port", "587") - form.Add("username", "exampleuser") - form.Add("password", "password123") - form.Add("var1", "info@betatude.com") - form.Add("var2", "sendto@gmail.com") - form.Add("api_key", "") - form.Add("api_secret", "") - form.Add("limits", "7") - req, err := http.NewRequest("POST", "/settings/notifier/email", strings.NewReader(form.Encode())) - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - assert.Equal(t, 303, rr.Code) - assert.True(t, isRouteAuthenticated(req)) - notification, _, err = notifier.SelectNotifier("email") - assert.Nil(t, err) - assert.True(t, notification.IsRunning()) -} - func TestViewNotificationSettingsHandler(t *testing.T) { req, err := http.NewRequest("GET", "/settings", nil) assert.Nil(t, err) diff --git a/handlers/messages.go b/handlers/messages.go index 26599df3..c7270536 100644 --- a/handlers/messages.go +++ b/handlers/messages.go @@ -16,7 +16,9 @@ package handlers import ( + "github.com/gorilla/mux" "github.com/hunterlong/statup/core" + "github.com/hunterlong/statup/utils" "net/http" ) @@ -28,3 +30,18 @@ func messagesHandler(w http.ResponseWriter, r *http.Request) { messages, _ := core.SelectMessages() executeResponse(w, r, "messages.html", messages, nil) } + +func viewMessageHandler(w http.ResponseWriter, r *http.Request) { + if !IsAuthenticated(r) { + http.Redirect(w, r, "/", http.StatusSeeOther) + return + } + vars := mux.Vars(r) + id := utils.StringInt(vars["id"]) + message, err := core.SelectMessage(id) + if err != nil { + w.WriteHeader(http.StatusNotFound) + return + } + executeResponse(w, r, "message.html", message, nil) +} diff --git a/handlers/routes.go b/handlers/routes.go index 3996c6fa..cf6d3200 100644 --- a/handlers/routes.go +++ b/handlers/routes.go @@ -69,6 +69,7 @@ func Router() *mux.Router { // MESSAGES Routes r.Handle("/messages", http.HandlerFunc(messagesHandler)).Methods("GET") + r.Handle("/message/{id}", http.HandlerFunc(viewMessageHandler)).Methods("GET") // SETTINGS Routes r.Handle("/settings", http.HandlerFunc(settingsHandler)).Methods("GET") @@ -76,7 +77,6 @@ func Router() *mux.Router { r.Handle("/settings/css", http.HandlerFunc(saveSASSHandler)).Methods("POST") r.Handle("/settings/build", http.HandlerFunc(saveAssetsHandler)).Methods("GET") r.Handle("/settings/delete_assets", http.HandlerFunc(deleteAssetsHandler)).Methods("GET") - r.Handle("/settings/notifier/{method}", http.HandlerFunc(saveNotificationHandler)).Methods("POST") r.Handle("/settings/notifier/{method}/test", http.HandlerFunc(testNotificationHandler)).Methods("POST") r.Handle("/settings/export", http.HandlerFunc(exportHandler)).Methods("GET") @@ -104,7 +104,6 @@ func Router() *mux.Router { r.Handle("/api/services/{id}/ping", http.HandlerFunc(apiServicePingDataHandler)).Methods("GET") r.Handle("/api/services/{id}", http.HandlerFunc(apiServiceUpdateHandler)).Methods("POST") r.Handle("/api/services/{id}", http.HandlerFunc(apiServiceDeleteHandler)).Methods("DELETE") - r.Handle("/api/service/{id}/failures", http.HandlerFunc(apiServiceFailuresHandler)).Methods("GET") r.Handle("/api/checkin/{api}", http.HandlerFunc(apiCheckinHandler)) // API USER Routes @@ -121,7 +120,7 @@ func Router() *mux.Router { // API MESSAGES Routes r.Handle("/api/messages", http.HandlerFunc(apiAllMessagesHandler)).Methods("GET") - r.Handle("/api/messages", http.HandlerFunc(apiNotifierUpdateHandler)).Methods("POST") + r.Handle("/api/messages", http.HandlerFunc(apiMessageCreateHandler)).Methods("POST") r.Handle("/api/messages/{id}", http.HandlerFunc(apiMessageGetHandler)).Methods("GET") r.Handle("/api/messages/{id}", http.HandlerFunc(apiMessageUpdateHandler)).Methods("POST") r.Handle("/api/messages/{id}", http.HandlerFunc(apiMessageDeleteHandler)).Methods("DELETE") diff --git a/handlers/settings.go b/handlers/settings.go index 039c4942..0aca41d1 100644 --- a/handlers/settings.go +++ b/handlers/settings.go @@ -135,70 +135,6 @@ func parseGet(r *http.Request) url.Values { return r.Form } -func saveNotificationHandler(w http.ResponseWriter, r *http.Request) { - var err error - if !IsAuthenticated(r) { - http.Redirect(w, r, "/", http.StatusSeeOther) - return - } - form := parseForm(r) - vars := mux.Vars(r) - method := vars["method"] - enabled := form.Get("enable") - host := form.Get("host") - port := int(utils.StringInt(form.Get("port"))) - username := form.Get("username") - password := form.Get("password") - var1 := form.Get("var1") - var2 := form.Get("var2") - apiKey := form.Get("api_key") - apiSecret := form.Get("api_secret") - limits := int(utils.StringInt(form.Get("limits"))) - - notifer, notif, err := notifier.SelectNotifier(method) - if err != nil { - utils.Log(3, fmt.Sprintf("issue saving notifier %v: %v", method, err)) - executeResponse(w, r, "settings.html", core.CoreApp, "/settings") - return - } - - if host != "" { - notifer.Host = host - } - if port != 0 { - notifer.Port = port - } - if username != "" { - notifer.Username = username - } - if password != "" && password != "##########" { - notifer.Password = password - } - if var1 != "" { - notifer.Var1 = var1 - } - if var2 != "" { - notifer.Var2 = var2 - } - if apiKey != "" { - notifer.ApiKey = apiKey - } - if apiSecret != "" { - notifer.ApiSecret = apiSecret - } - if limits != 0 { - notifer.Limits = limits - } - notifer.Enabled = types.NewNullBool(enabled == "on") - - _, err = notifier.Update(notif, notifer) - if err != nil { - utils.Log(3, fmt.Sprintf("issue updating notifier: %v", err)) - } - notifier.OnSave(notifer.Method) - executeResponse(w, r, "settings.html", core.CoreApp, "/settings") -} - func testNotificationHandler(w http.ResponseWriter, r *http.Request) { var err error if !IsAuthenticated(r) { diff --git a/source/js/main.js b/source/js/main.js index 007c55c3..f7cffda8 100644 --- a/source/js/main.js +++ b/source/js/main.js @@ -185,7 +185,7 @@ $('form.ajax_form').on('submit', function() { return } if (k.value === "on") { - k.value = true + k.value = (k.value === "on") } if($.isNumeric(k.value)){ if (k.name !== "password") { diff --git a/source/tmpl/message.html b/source/tmpl/message.html index 862f54d7..d3502327 100644 --- a/source/tmpl/message.html +++ b/source/tmpl/message.html @@ -14,15 +14,14 @@ {{define "extra_scripts"}} - {{end}} \ No newline at end of file diff --git a/source/tmpl/messages.html b/source/tmpl/messages.html index 8e48ce00..0ac5a110 100644 --- a/source/tmpl/messages.html +++ b/source/tmpl/messages.html @@ -45,15 +45,14 @@ {{define "extra_scripts"}} - {{end}} \ No newline at end of file diff --git a/types/failure.go b/types/failure.go index 0cc3b374..e2359196 100644 --- a/types/failure.go +++ b/types/failure.go @@ -36,3 +36,15 @@ type FailureInterface interface { Ago() string // Ago returns a human readable timestamp ParseError() string // ParseError returns a human readable error for a service failure } + +type FailSort []FailureInterface + +func (s FailSort) Len() int { + return len(s) +} +func (s FailSort) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} +func (s FailSort) Less(i, j int) bool { + return s[i].Select().Id < s[j].Select().Id +} diff --git a/types/service.go b/types/service.go index 672090f0..9eac994f 100644 --- a/types/service.go +++ b/types/service.go @@ -21,33 +21,33 @@ import ( // Service is the main struct for Services type Service struct { - Id int64 `gorm:"primary_key;column:id" json:"id"` - Name string `gorm:"column:name" json:"name"` - Domain string `gorm:"column:domain" json:"domain"` - Expected NullString `gorm:"column:expected" json:"expected"` - ExpectedStatus int `gorm:"default:200;column:expected_status" json:"expected_status"` - Interval int `gorm:"default:30;column:check_interval" json:"check_interval"` - Type string `gorm:"column:check_type" json:"type"` - Method string `gorm:"column:method" json:"method"` - PostData NullString `gorm:"column:post_data" json:"post_data"` - Port int `gorm:"not null;column:port" json:"port"` - Timeout int `gorm:"default:30;column:timeout" json:"timeout"` - Order int `gorm:"default:0;column:order_id" json:"order_id"` - AllowNotifications NullBool `gorm:"default:false;column:allow_notifications" json:"allow_notifications"` - CreatedAt time.Time `gorm:"column:created_at" json:"created_at"` - UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"` - Online bool `gorm:"-" json:"online"` - Latency float64 `gorm:"-" json:"latency"` - PingTime float64 `gorm:"-" json:"ping_time"` - Online24Hours float32 `gorm:"-" json:"online_24_hours"` - AvgResponse string `gorm:"-" json:"avg_response"` - Running chan bool `gorm:"-" json:"-"` - Checkpoint time.Time `gorm:"-" json:"-"` - SleepDuration time.Duration `gorm:"-" json:"-"` - LastResponse string `gorm:"-" json:"-"` - LastStatusCode int `gorm:"-" json:"status_code"` - LastOnline time.Time `gorm:"-" json:"last_online"` - Failures []*Failure `gorm:"-" json:"failures,omitempty"` + Id int64 `gorm:"primary_key;column:id" json:"id"` + Name string `gorm:"column:name" json:"name"` + Domain string `gorm:"column:domain" json:"domain"` + Expected NullString `gorm:"column:expected" json:"expected"` + ExpectedStatus int `gorm:"default:200;column:expected_status" json:"expected_status"` + Interval int `gorm:"default:30;column:check_interval" json:"check_interval"` + Type string `gorm:"column:check_type" json:"type"` + Method string `gorm:"column:method" json:"method"` + PostData NullString `gorm:"column:post_data" json:"post_data"` + Port int `gorm:"not null;column:port" json:"port"` + Timeout int `gorm:"default:30;column:timeout" json:"timeout"` + Order int `gorm:"default:0;column:order_id" json:"order_id"` + AllowNotifications NullBool `gorm:"default:false;column:allow_notifications" json:"allow_notifications"` + CreatedAt time.Time `gorm:"column:created_at" json:"created_at"` + UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"` + Online bool `gorm:"-" json:"online"` + Latency float64 `gorm:"-" json:"latency"` + PingTime float64 `gorm:"-" json:"ping_time"` + Online24Hours float32 `gorm:"-" json:"online_24_hours"` + AvgResponse string `gorm:"-" json:"avg_response"` + Running chan bool `gorm:"-" json:"-"` + Checkpoint time.Time `gorm:"-" json:"-"` + SleepDuration time.Duration `gorm:"-" json:"-"` + LastResponse string `gorm:"-" json:"-"` + LastStatusCode int `gorm:"-" json:"status_code"` + LastOnline time.Time `gorm:"-" json:"last_online"` + Failures []FailureInterface `gorm:"-" json:"failures,omitempty"` } type ServiceInterface interface {