diff --git a/Makefile b/Makefile index 878a74716a85947c3364eb70ff368d5cda1aedd9..bff439f71064723592b7bf74fcff62417ec395cf 100644 --- a/Makefile +++ b/Makefile @@ -172,6 +172,9 @@ docker-push-latest: docker push hunterlong/statup:latest docker push hunterlong/statup:latest-v$(VERSION) +docker-run-mssql: + docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=PaSsW0rD123' -p 1433:1433 -d microsoft/mssql-server-linux + # create Postgres, and MySQL instance using Docker (used for testing) databases: docker run --name statup_postgres -p 5432:5432 -e POSTGRES_PASSWORD=password123 -e POSTGRES_USER=root -e POSTGRES_DB=root -d postgres diff --git a/cmd/main_test.go b/cmd/main_test.go index da043787d8ad7673cf921dccf0323ce73572b6df..21ba79dabe945725f630e1b11b68fa121f3c2b21 100644 --- a/cmd/main_test.go +++ b/cmd/main_test.go @@ -52,7 +52,13 @@ func Clean() { utils.DeleteDirectory(dir + "/logs") } -func RunInit(t *testing.T) { +func RunInit(db string, t *testing.T) { + if db == "mssql" { + os.Setenv("DB_DATABASE", "tempdb") + os.Setenv("DB_PASS", "PaSsW0rD123") + os.Setenv("DB_PORT", "1433") + os.Setenv("DB_USER", "sa") + } source.Assets() Clean() route = handlers.Router() @@ -69,7 +75,7 @@ func TestRunAll(t *testing.T) { for _, dbt := range databases { t.Run(dbt+" init", func(t *testing.T) { - RunInit(t) + RunInit(dbt, t) }) t.Run(dbt+" Save Config", func(t *testing.T) { RunSaveConfig(t, dbt) @@ -206,6 +212,11 @@ func TestRunAll(t *testing.T) { t.Run(dbt+" Cleanup", func(t *testing.T) { core.Configs.Close() core.DbSession = nil + if dbt == "mssql" { + os.Setenv("DB_DATABASE", "root") + os.Setenv("DB_PASS", "password123") + os.Setenv("DB_PORT", "1433") + } //Clean() }) @@ -220,6 +231,8 @@ func RunSaveConfig(t *testing.T, db string) { port := 5432 if db == "mysql" { port = 3306 + } else if db == "mssql" { + port = 1433 } core.Configs = &core.DbConfig{DbConfig: &types.DbConfig{ DbConn: db, diff --git a/core/services.go b/core/services.go index 05480776da1b01cedd3230c8cf128aa6bbdd3e80..653dcace8a37b99883e983921f6f23f223fca561 100644 --- a/core/services.go +++ b/core/services.go @@ -163,12 +163,7 @@ func (s *Service) SmallText() string { } func (s *Service) DowntimeText() string { - lastFailure := s.lastFailure() - if lastFailure == nil { - return "" - } - got, _ := timeago.TimeAgoWithTime(time.Now().UTC().Add(s.Downtime()), time.Now().UTC()) - return fmt.Sprintf("Reported offline %v, %v", got, lastFailure.ParseError()) + return fmt.Sprintf("%v has been offline for %v", s.Name, utils.DurationReadable(s.Downtime())) } // GroupDataBy returns a SQL query as a string to group a column by a time @@ -188,13 +183,13 @@ func GroupDataBy(column string, id int64, start, end time.Time, increment string // Downtime returns the amount of time of a offline service func (s *Service) Downtime() time.Duration { hits, _ := s.Hits() - if len(hits) == 0 { - return time.Duration(0) - } fails := s.LimitedFailures() if len(fails) == 0 { return time.Duration(0) } + if len(hits) == 0 { + return time.Now().UTC().Sub(fails[len(fails)-1].CreatedAt.UTC()) + } since := fails[0].CreatedAt.UTC().Sub(hits[0].CreatedAt.UTC()) return since } diff --git a/dev/docker-compose.yml b/dev/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..3683a79e162d076eba4ac4c93c8f012a966a6071 --- /dev/null +++ b/dev/docker-compose.yml @@ -0,0 +1,32 @@ +postgres: + container_name: postgres + image: postgres + restart: always + ports: + - 5432:5432 + environment: + POSTGRES_PASSWORD: password123 + POSTGRES_USER: root + POSTGRES_DB: root + +mysql: + container_name: mysql + image: mysql:5.6 + restart: always + ports: + - 3306:3306 + environment: + MYSQL_ROOT_PASSWORD: password123 + MYSQL_DATABASE: root + MYSQL_USER: root + MYSQL_PASSWORD: password123 + +mssql: + container_name: mssql + image: microsoft/mssql-server-linux + restart: always + ports: + - 1433:1433 + environment: + SA_PASSWORD: PaSsW0rD123 + ACCEPT_EULA: "Y" diff --git a/handlers/api.go b/handlers/api.go index f01f125794ca063aae5f74fc484a287121af6375..9502f4bbbd634bab2ecced26430ea7d4cc6f3506 100644 --- a/handlers/api.go +++ b/handlers/api.go @@ -23,7 +23,6 @@ import ( "github.com/hunterlong/statup/utils" "net/http" "os" - "time" ) type ApiResponse struct { @@ -94,38 +93,6 @@ func apiServiceHandler(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(service) } -func apiServiceDataHandler(w http.ResponseWriter, r *http.Request) { - if !isAPIAuthorized(r) { - http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) - return - } - vars := mux.Vars(r) - fields := parseGet(r) - - startField := utils.StringInt(fields.Get("start")) - endField := utils.StringInt(fields.Get("end")) - var start time.Time - var end time.Time - if startField == 0 { - start = time.Now().Add(-24 * time.Hour).UTC() - } else { - start = time.Unix(startField, 0) - } - if endField == 0 { - end = time.Now().UTC() - } else { - end = time.Unix(endField, 0) - } - service := core.SelectService(utils.StringInt(vars["id"])) - if service == nil { - http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) - return - } - - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(core.GraphDataRaw(service, start, end).Array) -} - func apiCreateServiceHandler(w http.ResponseWriter, r *http.Request) { if !isAPIAuthorized(r) { http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) diff --git a/handlers/api_handlers_test.go b/handlers/api_handlers_test.go index f600041852814f95162e820a7b66915be2be477d..f7e7d39867b578be056f5ef510cf31d0f7f864d6 100644 --- a/handlers/api_handlers_test.go +++ b/handlers/api_handlers_test.go @@ -121,16 +121,6 @@ func TestApiServiceHandler(t *testing.T) { assert.Equal(t, "https://google.com", obj.Domain) } -func TestApiServiceDataHandler(t *testing.T) { - rr, err := httpRequestAPI(t, "GET", "/api/services/1/data", nil) - assert.Nil(t, err) - body := rr.Body.String() - var obj []*core.DateScan - formatJSON(body, &obj) - assert.Equal(t, 200, rr.Code) - assert.Equal(t, 60, len(obj)) -} - func TestApiCreateServiceHandler(t *testing.T) { rr, err := httpRequestAPI(t, "POST", "/api/services", strings.NewReader(NEW_HTTP_SERVICE)) assert.Nil(t, err) diff --git a/handlers/routes.go b/handlers/routes.go index c1449363254994b68221879bb9c96bfaca0db84a..fb81ac85162429cb481ed229b3ebc397352cc7bb 100644 --- a/handlers/routes.go +++ b/handlers/routes.go @@ -84,7 +84,6 @@ func Router() *mux.Router { r.Handle("/api/services", http.HandlerFunc(apiAllServicesHandler)).Methods("GET") r.Handle("/api/services", http.HandlerFunc(apiCreateServiceHandler)).Methods("POST") r.Handle("/api/services/{id}", http.HandlerFunc(apiServiceHandler)).Methods("GET") - r.Handle("/api/services/{id}/data", http.HandlerFunc(apiServiceDataHandler)).Methods("GET") r.Handle("/api/services/{id}", http.HandlerFunc(apiServiceUpdateHandler)).Methods("POST") r.Handle("/api/services/{id}", http.HandlerFunc(apiServiceDeleteHandler)).Methods("DELETE") diff --git a/utils/utils.go b/utils/utils.go index 7374f31730f188d9b1a88a211fe75e8a3ae27ec9..4824a087ddeadb3aa95fddf017e51fe656295ad5 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -190,3 +190,14 @@ func copyAndCapture(w io.Writer, r io.Reader) ([]byte, error) { } } } + +func DurationReadable(d time.Duration) string { + if d.Hours() >= 1 { + return fmt.Sprintf("%0.0f hours and %0.0f minutes", d.Hours(), d.Minutes()) + } else if d.Minutes() >= 1 { + return fmt.Sprintf("%0.0f minutes", d.Minutes()) + } else if d.Seconds() >= 1 { + return fmt.Sprintf("%0.0f seconds", d.Seconds()) + } + return d.String() +}