diff --git a/backend/.gitignore b/backend/.gitignore index 24a61c9..e7ccc7b 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -2,4 +2,6 @@ build .env coverage tmp -*.exe \ No newline at end of file +*.exe +*.crt +*.key \ No newline at end of file diff --git a/backend/go.mod b/backend/go.mod index b333713..df8039b 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -4,28 +4,18 @@ go 1.20 require ( github.com/go-playground/validator/v10 v10.15.3 - github.com/gofiber/fiber/v2 v2.49.1 + github.com/gorilla/mux v1.8.0 github.com/joho/godotenv v1.5.1 github.com/stretchr/testify v1.8.4 ) require ( - github.com/andybalholm/brotli v1.0.5 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/google/uuid v1.3.1 // indirect - github.com/klauspost/compress v1.16.7 // indirect github.com/leodido/go-urn v1.2.4 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/rivo/uniseg v0.2.0 // indirect - github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasthttp v1.49.0 // indirect - github.com/valyala/tcplisten v1.0.0 // indirect golang.org/x/crypto v0.7.0 // indirect golang.org/x/net v0.8.0 // indirect golang.org/x/sys v0.11.0 // indirect diff --git a/backend/go.sum b/backend/go.sum index 43da3a3..4b28077 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -1,5 +1,3 @@ -github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= -github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -12,27 +10,14 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.15.3 h1:S+sSpunYjNPDuXkWbK+x+bA7iXiW296KG4dL3X7xUZo= github.com/go-playground/validator/v10 v10.15.3/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= -github.com/gofiber/fiber/v2 v2.49.1 h1:0W2DRWevSirc8pJl4o8r8QejDR8TV6ZUCawHxwbIdOk= -github.com/gofiber/fiber/v2 v2.49.1/go.mod h1:nPUeEBUeeYGgwbDm59Gp7vS8MDyScL6ezr/Np9A13WU= -github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -41,18 +26,10 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.49.0 h1:9FdvCpmxB74LH4dPb7IJ1cOSsluR07XG3I1txXWwJpE= -github.com/valyala/fasthttp v1.49.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= -github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= -github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= diff --git a/backend/handlers/certCheckHandlers.go b/backend/handlers/certCheckHandlers.go index 26d5ce2..6a72421 100644 --- a/backend/handlers/certCheckHandlers.go +++ b/backend/handlers/certCheckHandlers.go @@ -9,11 +9,10 @@ import ( "strings" "time" - "github.com/gofiber/fiber/v2" "github.com/jlucaspains/sharp-cert-checker/models" ) -func (h Handlers) GetSiteList(c *fiber.Ctx) error { +func (h Handlers) GetSiteList(w http.ResponseWriter, r *http.Request) { regx := regexp.MustCompile(`https?:\/\/`) siteList := []models.CheckListResult{} for _, url := range h.SiteList { @@ -21,23 +20,21 @@ func (h Handlers) GetSiteList(c *fiber.Ctx) error { siteList = append(siteList, models.CheckListResult{Name: hostName, Url: url}) } - c.JSON(siteList) - - return nil + h.JSON(w, http.StatusOK, siteList) } -func (h Handlers) CheckStatus(c *fiber.Ctx) error { +func (h Handlers) CheckStatus(w http.ResponseWriter, r *http.Request) { params := &models.CertCheckParams{} - params.Url = c.Query("url") - log.Println("Received message for URL: " + params.Url) + params.Url, _ = h.getQueryParam(r, "url") if params.Url == "" { - c.Status(http.StatusBadRequest) - c.JSON(&fiber.Map{"error": "Missing URL parameter"}) - return nil + h.JSON(w, http.StatusBadRequest, &models.ErrorResult{Errors: []string{"Url is required"}}) + return } + log.Println("Received message for URL: " + params.Url) + client := &http.Client{ CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse @@ -52,7 +49,8 @@ func (h Handlers) CheckStatus(c *fiber.Ctx) error { resp, err := client.Get(params.Url) if err != nil { - return err + h.JSON(w, http.StatusBadRequest, &models.ErrorResult{Errors: []string{"Could not load provided URL"}}) + return } hostName := strings.Split(params.Url, ":")[1] @@ -60,8 +58,8 @@ func (h Handlers) CheckStatus(c *fiber.Ctx) error { if resp.TLS == nil { result := &models.CertCheckResult{Hostname: hostName, CertStartDate: time.Time{}, CertEndDate: time.Time{}, CertDnsNames: []string{}, IsValid: false} - c.JSON(result) - return nil + h.JSON(w, http.StatusOK, result) + return } certStartDate := resp.TLS.PeerCertificates[0].NotBefore @@ -83,9 +81,7 @@ func (h Handlers) CheckStatus(c *fiber.Ctx) error { ValidationIssues: errors, } - c.JSON(result) - - return nil + h.JSON(w, http.StatusOK, result) } func validate(cert *x509.Certificate, hostName string) (isValid bool, errors []string) { diff --git a/backend/handlers/certCheckHandlers_test.go b/backend/handlers/certCheckHandlers_test.go index fcc7cf3..6457ec8 100644 --- a/backend/handlers/certCheckHandlers_test.go +++ b/backend/handlers/certCheckHandlers_test.go @@ -1,16 +1,14 @@ package handlers import ( - "bytes" "crypto/tls" - "encoding/json" "fmt" "net/http" "net/http/httptest" "testing" "time" - "github.com/gofiber/fiber/v2" + "github.com/gorilla/mux" "github.com/jlucaspains/sharp-cert-checker/models" "github.com/joho/godotenv" "github.com/stretchr/testify/assert" @@ -20,70 +18,67 @@ func TestGetSiteList(t *testing.T) { godotenv.Load("../.test.env") handlers := new(Handlers) handlers.SiteList = handlers.GetConfigSites() - app := fiber.New() - defer app.Shutdown() - app.Get("/site-list", handlers.GetSiteList) + router := mux.NewRouter() + router.HandleFunc("/site-list", handlers.GetSiteList).Methods("GET") - code, respBody, err := getJsonTestRequestResponse[[]models.CertCheckResult](app, "GET", "/site-list", nil) + code, body, err := makeRequest[[]models.CheckListResult](router, "GET", "/site-list", nil) assert.Nil(t, err) assert.Equal(t, 200, code) - assert.Equal(t, 1, len(respBody)) + assert.Equal(t, 1, len(*body)) } func TestGetCheckStatus(t *testing.T) { handlers := new(Handlers) - app := fiber.New() - defer app.Shutdown() - app.Get("/check-status", handlers.CheckStatus) + router := mux.NewRouter() + router.HandleFunc("/check-status", handlers.CheckStatus).Methods("GET") url := fmt.Sprintf("/check-status?url=%s", "https://blog.lpains.net") - code, respBody, err := getJsonTestRequestResponse[models.CertCheckResult](app, "GET", url, nil) + code, body, err := makeRequest[models.CertCheckResult](router, "GET", url, nil) assert.Nil(t, err) assert.Equal(t, 200, code) - assert.True(t, respBody.IsValid) - assert.LessOrEqual(t, respBody.CertStartDate, time.Now()) - assert.GreaterOrEqual(t, respBody.CertEndDate, time.Now()) - assert.Contains(t, respBody.Hostname, "blog.lpains.net") - assert.Contains(t, respBody.CertDnsNames, "blog.lpains.net") + assert.True(t, body.IsValid) + assert.LessOrEqual(t, body.CertStartDate, time.Now()) + assert.GreaterOrEqual(t, body.CertEndDate, time.Now()) + assert.Contains(t, body.Hostname, "blog.lpains.net") + assert.Contains(t, body.CertDnsNames, "blog.lpains.net") } func TestGetCheckStatusNoUrl(t *testing.T) { handlers := new(Handlers) - app := fiber.New() - defer app.Shutdown() - app.Get("/check-status", handlers.CheckStatus) + router := mux.NewRouter() + router.HandleFunc("/check-status", handlers.CheckStatus).Methods("GET") url := "/check-status" - code, respBody, err := getJsonTestRequestResponse[map[string]string](app, "GET", url, nil) + code, body, err := makeRequest[models.ErrorResult](router, "GET", url, nil) assert.Nil(t, err) assert.Equal(t, 400, code) - assert.Equal(t, respBody["error"], "Missing URL parameter") + assert.Equal(t, "Url is required", body.Errors[0]) } func TestGetCheckStatusHttp(t *testing.T) { handlers := new(Handlers) - app := fiber.New() - defer app.Shutdown() - app.Get("/check-status", handlers.CheckStatus) + router := mux.NewRouter() + router.HandleFunc("/check-status", handlers.CheckStatus).Methods("GET") url := fmt.Sprintf("/check-status?url=%s", "http://blog.lpains.net") - code, respBody, err := getJsonTestRequestResponse[models.CertCheckResult](app, "GET", url, nil) + code, body, err := makeRequest[models.CertCheckResult](router, "GET", url, nil) assert.Nil(t, err) assert.Equal(t, 200, code) - assert.False(t, respBody.IsValid) + assert.False(t, body.IsValid) } func TestGetCheckStatusAllValidations(t *testing.T) { handlers := new(Handlers) - app := fiber.New() - defer app.Shutdown() + + router := mux.NewRouter() + router.HandleFunc("/check-status", handlers.CheckStatus).Methods("GET") mux := http.NewServeMux() ts := httptest.NewUnstartedServer(mux) @@ -147,32 +142,11 @@ yjbTOuy8KoxNb15g3Ysesbw= ts.StartTLS() defer ts.Close() - app.Get("/check-status", handlers.CheckStatus) - url := fmt.Sprintf("/check-status?url=%s", ts.URL) - code, respBody, err := getJsonTestRequestResponse[models.CertCheckResult](app, "GET", url, nil) + code, body, err := makeRequest[models.CertCheckResult](router, "GET", url, nil) assert.Nil(t, err) assert.Equal(t, 200, code) - assert.False(t, respBody.IsValid) - assert.Equal(t, []string{"Hostname is not valid", "Certificate is not valid yet or expired", "SHA1 is not a secure signature algorithm"}, respBody.ValidationIssues) -} - -func getJsonTestRequestResponse[K any | []any](app *fiber.App, method string, url string, reqBody any) (code int, respBody K, err error) { - bodyJson, _ := json.Marshal(reqBody) - req, _ := http.NewRequest(method, url, bytes.NewReader(bodyJson)) - resp, err := app.Test(req, 1000) - // If error we're done - if err != nil { - return - } - code = resp.StatusCode - // If no body content, we're done - if resp.ContentLength == 0 { - return - } - bodyData := make([]byte, resp.ContentLength) - _, _ = resp.Body.Read(bodyData) - err = json.Unmarshal(bodyData, &respBody) - return + assert.False(t, body.IsValid) + assert.Equal(t, []string{"Hostname is not valid", "Certificate is not valid yet or expired", "SHA1 is not a secure signature algorithm"}, body.ValidationIssues) } diff --git a/backend/handlers/handlers.go b/backend/handlers/handlers.go index 764bb85..00498da 100644 --- a/backend/handlers/handlers.go +++ b/backend/handlers/handlers.go @@ -1,6 +1,7 @@ package handlers import ( + "encoding/json" "fmt" "net/http" "os" @@ -13,6 +14,22 @@ type Handlers struct { SiteList []string } +func (h Handlers) JSON(w http.ResponseWriter, statusCode int, data interface{}) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(statusCode) + // convert data to json + result, _ := json.Marshal(data) + w.Write(result) +} + +func (h Handlers) getQueryParam(r *http.Request, key string) (string, error) { + if param := r.URL.Query()[key]; param != nil { + return param[0], nil + } + + return "", fmt.Errorf("%s not found", key) +} + func (h Handlers) GetConfigSites() []string { siteList := []string{} for i := 1; true; i++ { diff --git a/backend/handlers/handlers_test.go b/backend/handlers/handlers_test.go index f5bd88f..ba83ad8 100644 --- a/backend/handlers/handlers_test.go +++ b/backend/handlers/handlers_test.go @@ -1,10 +1,14 @@ package handlers import ( + "bytes" + "encoding/json" "net/http" + "net/http/httptest" "testing" "github.com/go-playground/validator/v10" + "github.com/gorilla/mux" "github.com/stretchr/testify/assert" ) @@ -31,3 +35,21 @@ func TestErrorTranslationSuccess(t *testing.T) { assert.Len(t, result.Errors, 1) assert.Equal(t, "Tagline2 should be greater than 1", result.Errors[0]) } + +func makeRequest[K any | []any](router *mux.Router, method string, url string, body any) (code int, respBody *K, err error) { + inputBody := "" + + if body != nil { + inputBodyJson, _ := json.Marshal(body) + inputBody = string(inputBodyJson) + } + + req, _ := http.NewRequest(method, url, bytes.NewReader([]byte(inputBody))) + rr := httptest.NewRecorder() + router.ServeHTTP(rr, req) + + result := new(K) + err = json.Unmarshal(rr.Body.Bytes(), &result) + + return rr.Code, result, err +} diff --git a/backend/handlers/health.go b/backend/handlers/health.go new file mode 100644 index 0000000..515f357 --- /dev/null +++ b/backend/handlers/health.go @@ -0,0 +1,14 @@ +package handlers + +import ( + "net/http" + + "github.com/jlucaspains/sharp-cert-checker/models" +) + +func (h Handlers) HealthCheck(w http.ResponseWriter, r *http.Request) { + h.JSON(w, http.StatusOK, &models.HealthResult{ + Healthy: true, + Dependencies: []models.HealthResultItem{}, + }) +} diff --git a/backend/main.go b/backend/main.go index 489be65..86396f7 100644 --- a/backend/main.go +++ b/backend/main.go @@ -3,11 +3,12 @@ package main import ( "fmt" "log" + "net/http" "os" - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/cors" + "github.com/gorilla/mux" "github.com/jlucaspains/sharp-cert-checker/handlers" + "github.com/jlucaspains/sharp-cert-checker/midlewares" "github.com/joho/godotenv" ) @@ -23,18 +24,34 @@ func loadEnv() { func main() { loadEnv() - app := fiber.New() - - app.Use(cors.New(cors.Config{ - AllowOrigins: "*", - })) - handlers := &handlers.Handlers{} handlers.SiteList = handlers.GetConfigSites() - app.Get("/api/check-url", handlers.CheckStatus) - app.Get("/api/site-list", handlers.GetSiteList) - app.Static("/", "./public") + router := mux.NewRouter() + router.HandleFunc("/api/check-url", handlers.CheckStatus).Methods("GET") + router.HandleFunc("/api/site-list", handlers.GetSiteList).Methods("GET") + router.HandleFunc("/health", handlers.HealthCheck).Methods("GET") + router.PathPrefix("/").Handler(http.FileServer(http.Dir("./public/"))) + + logMiddleware := midlewares.NewLogMiddleware(log.Default()) + router.Use(logMiddleware.Func()) + + hostPort, ok := os.LookupEnv("WEB_HOST_PORT") + if !ok { + hostPort = ":3000" + } + + useTls := false + certFile, ok := os.LookupEnv("TLS_CERT_FILE") + useTls = ok - app.Listen(":3000") + certKeyFile, ok := os.LookupEnv("TLS_CERT_KEY_FILE") + useTls = useTls && ok + + log.Printf("Starting TLS server on port: %s; use tls: %t", hostPort, useTls) + if useTls { + log.Fatalln(http.ListenAndServeTLS(hostPort, certFile, certKeyFile, router)) + } else { + log.Fatalln(http.ListenAndServe(hostPort, router)) + } } diff --git a/backend/midlewares/log.go b/backend/midlewares/log.go new file mode 100644 index 0000000..bb74a7f --- /dev/null +++ b/backend/midlewares/log.go @@ -0,0 +1,55 @@ +package midlewares + +import ( + "bytes" + "log" + "net/http" + "time" + + "github.com/gorilla/mux" +) + +type LogResponseWriter struct { + http.ResponseWriter + statusCode int + buf bytes.Buffer +} + +func NewLogResponseWriter(w http.ResponseWriter) *LogResponseWriter { + return &LogResponseWriter{ResponseWriter: w} +} + +func (w *LogResponseWriter) WriteHeader(code int) { + w.statusCode = code + w.ResponseWriter.WriteHeader(code) +} + +func (w *LogResponseWriter) Write(body []byte) (int, error) { + w.buf.Write(body) + return w.ResponseWriter.Write(body) +} + +type LogMiddleware struct { + logger *log.Logger +} + +func NewLogMiddleware(logger *log.Logger) *LogMiddleware { + return &LogMiddleware{logger: logger} +} + +func (m *LogMiddleware) Func() mux.MiddlewareFunc { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + startTime := time.Now() + + logRespWriter := NewLogResponseWriter(w) + next.ServeHTTP(logRespWriter, r) + + m.logger.Printf( + "url=%s duration=%s status=%d", + r.URL.String(), + time.Since(startTime).String(), + logRespWriter.statusCode) + }) + } +} diff --git a/backend/models/healthResult.go b/backend/models/healthResult.go new file mode 100644 index 0000000..615a17f --- /dev/null +++ b/backend/models/healthResult.go @@ -0,0 +1,12 @@ +package models + +type HealthResult struct { + Healthy bool `json:"healthy"` + Dependencies []HealthResultItem `json:"dependencies"` +} + +type HealthResultItem struct { + Name string `json:"name"` + Healthy bool `json:"healthy"` + Error string `json:"error"` +} diff --git a/readme.md b/readme.md index 2b9f43e..1ab8a4e 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,5 @@ # sharp-cert-checker -This project aims to provide a simple tool to monitor certificate validity. It is composed of a golang backend API built using [Fiber](https://gofiber.io/) and a frontend build using [Svelte](https://svelte.dev/). +This project aims to provide a simple tool to monitor certificate validity. It is composed of a golang backend API built using GO http server and [Gorilla Mux](https://github.com/gorilla/mux) and a frontend build using [Svelte](https://svelte.dev/). ![Demo image](/docs/demo.jpeg)