diff --git a/README.md b/README.md index 2fcedd2..34d5f6c 100644 --- a/README.md +++ b/README.md @@ -108,8 +108,7 @@ The compose example also works with Podman compatible compose tools. Its data vo `./data:/data:Z` for SELinux relabeling, and the container overrides runtime paths to use `/data`, `/app/static`, and `/app/templates`. -The image exposes `/health`, `/healthz`, and `/api/v1/health`. Docker and compose healthchecks -use `/health`. +The image exposes the health endpoint: `/health`. Docker and compose healthchecks use it. ## Reverse Proxy Security diff --git a/backend/libs/handlers/admin.go b/backend/libs/handlers/admin.go index cec35a2..41e8b87 100644 --- a/backend/libs/handlers/admin.go +++ b/backend/libs/handlers/admin.go @@ -1770,7 +1770,7 @@ func isHealthCheckLogEntry(raw map[string]any) bool { if idx := strings.IndexByte(path, '?'); idx >= 0 { path = path[:idx] } - return path == "/health" || path == "/healthz" || path == "/api/v1/health" + return path == "/health" } func logEntryFromMap(raw map[string]any) adminLogEntry { diff --git a/backend/libs/handlers/api_docs.go b/backend/libs/handlers/api_docs.go index 2fed685..adf1ef3 100644 --- a/backend/libs/handlers/api_docs.go +++ b/backend/libs/handlers/api_docs.go @@ -10,7 +10,6 @@ import ( type apiDocsData struct { BaseURL string UploadURL string - HealthURL string RequestSchemaURL string ResponseSchemaURL string ShareXExamplePath string @@ -39,7 +38,6 @@ func (a *App) APIDocs(w http.ResponseWriter, r *http.Request) { Data: apiDocsData{ BaseURL: a.cfg.BaseURL, UploadURL: a.cfg.BaseURL + "/api/v1/upload", - HealthURL: a.cfg.BaseURL + "/api/v1/health", RequestSchemaURL: a.cfg.BaseURL + "/api/v1/schemas/upload-request.json", ResponseSchemaURL: a.cfg.BaseURL + "/api/v1/schemas/upload-response.json", ShareXExamplePath: "examples/sharex/warpbox-anonymous.sxcu", diff --git a/backend/libs/handlers/app.go b/backend/libs/handlers/app.go index 9bd26d8..824829f 100644 --- a/backend/libs/handlers/app.go +++ b/backend/libs/handlers/app.go @@ -129,8 +129,8 @@ func (a *App) RegisterRoutes(mux *http.ServeMux) { mux.HandleFunc("GET /d/{boxID}/thumb/{fileID}", a.Thumbnail) mux.HandleFunc("GET /d/{boxID}/og-image.jpg", a.BoxOGImage) mux.HandleFunc("GET /health", a.Health) - mux.HandleFunc("GET /healthz", a.Health) - mux.HandleFunc("GET /api/v1/health", a.Health) + mux.HandleFunc("GET /healthz", notFound) + mux.HandleFunc("GET /api/v1/health", notFound) mux.HandleFunc("GET /api/v1/sharex/warpbox-anonymous.sxcu", a.ShareXAnonymousConfig) mux.HandleFunc("GET /api/v1/schemas/upload-request.json", a.UploadRequestSchema) mux.HandleFunc("GET /api/v1/schemas/upload-response.json", a.UploadResponseSchema) @@ -138,3 +138,7 @@ func (a *App) RegisterRoutes(mux *http.ServeMux) { mux.HandleFunc("GET /emoji/{pack}/{file}", a.EmojiAsset) mux.Handle("GET /static/", a.Static()) } + +func notFound(w http.ResponseWriter, r *http.Request) { + http.NotFound(w, r) +} diff --git a/backend/libs/handlers/health.go b/backend/libs/handlers/health.go index 613e81f..15f0c7c 100644 --- a/backend/libs/handlers/health.go +++ b/backend/libs/handlers/health.go @@ -13,6 +13,10 @@ type healthResponse struct { } func (a *App) Health(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/health" { + http.NotFound(w, r) + return + } helpers.WriteJSON(w, http.StatusOK, healthResponse{ Status: "ok", Time: time.Now().UTC().Format(time.RFC3339), diff --git a/backend/libs/handlers/health_test.go b/backend/libs/handlers/health_test.go index 14d42c5..4603608 100644 --- a/backend/libs/handlers/health_test.go +++ b/backend/libs/handlers/health_test.go @@ -13,16 +13,20 @@ func TestHealthRoutes(t *testing.T) { mux := http.NewServeMux() app.RegisterRoutes(mux) - for _, path := range []string{"/health", "/healthz", "/api/v1/health"} { - t.Run(path, func(t *testing.T) { - request := httptest.NewRequest(http.MethodGet, path, nil) - response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodGet, "/health", nil) + response := httptest.NewRecorder() - mux.ServeHTTP(response, request) + mux.ServeHTTP(response, request) - if response.Code != http.StatusOK { - t.Fatalf("status = %d, body = %s", response.Code, response.Body.String()) - } - }) + if response.Code != http.StatusOK { + t.Fatalf("status = %d, body = %s", response.Code, response.Body.String()) + } + for _, path := range []string{"/healthz", "/api/v1/health"} { + request := httptest.NewRequest(http.MethodGet, path, nil) + response := httptest.NewRecorder() + mux.ServeHTTP(response, request) + if response.Code != http.StatusNotFound { + t.Fatalf("%s status = %d, want 404", path, response.Code) + } } } diff --git a/backend/libs/services/bans.go b/backend/libs/services/bans.go index d5fc7d9..74ca312 100644 --- a/backend/libs/services/bans.go +++ b/backend/libs/services/bans.go @@ -472,7 +472,7 @@ func (s *BanService) MaliciousPattern(path string) (string, error) { } func shouldSkipMaliciousPath(path string) bool { - return path == "/health" || path == "/healthz" || path == "/api/v1/health" || strings.HasPrefix(path, "/static/") + return path == "/health" || strings.HasPrefix(path, "/static/") } func (s *BanService) RecordAbuse(ip, kind, detail string, threshold int, now time.Time) (AbuseResult, error) { diff --git a/backend/templates/pages/api.html b/backend/templates/pages/api.html index bff89fe..2de754c 100644 --- a/backend/templates/pages/api.html +++ b/backend/templates/pages/api.html @@ -14,7 +14,7 @@
POST /api/v1/uploadGET /api/v1/healthGET /health/api/v1/schemas/upload-request.json/api/v1/schemas/upload-response.json