Files
webhooks/pkg/server/handler.go
3a3f7a6b49 feat: add audit middleware to webhooks server (#154) (#155)
* feat: add audit middleware to webhooks server

Wire httpaudit.Middleware from go-libs/v5 into the webhooks HTTP handler
to capture and publish audit events via the existing Watermill publisher
infrastructure. This was lost when the upgrade PR was squash-merged.

* fix: remove duplicate publish module registration

The publisher is already provided by the worker module. Adding it again
in serve.go causes an fx "already provided" conflict.

* fix: move publish module from worker to serve

Move publish.FXModuleFromFlags to serve.go so the publisher is available
in both server-only and worker modes. Previously it was only in the
worker module, causing a missing dependency when running without --worker.

---------

Co-authored-by: Hanzo Dev <[email protected]>
2026-05-22 15:48:09 +00:00

82 lines
2.2 KiB
Go

package server
import (
"net/http"
"github.com/ThreeDotsLabs/watermill/message"
"github.com/go-chi/chi/v5"
"github.com/formancehq/go-libs/v2/service"
"github.com/formancehq/go-libs/v5/pkg/audit/httpaudit"
"github.com/formancehq/go-libs/v2/auth"
"github.com/formancehq/go-libs/v2/logging"
"github.com/formancehq/webhooks/pkg/storage"
)
const (
PathHealthCheck = "/_healthcheck"
PathInfo = "/_info"
PathConfigs = "/configs"
PathTest = "/test"
PathActivate = "/activate"
PathDeactivate = "/deactivate"
PathChangeSecret = "/secret/change"
PathId = "/{" + PathParamId + "}"
PathParamId = "id"
)
type serverHandler struct {
*chi.Mux
store storage.Store
httpClient *http.Client
}
func newServerHandler(
store storage.Store,
httpClient *http.Client,
logger logging.Logger,
info ServiceInfo,
authenticator auth.Authenticator,
publisher message.Publisher,
debug bool,
) http.Handler {
h := &serverHandler{
Mux: chi.NewRouter(),
store: store,
httpClient: httpClient,
}
h.Use(httpaudit.Middleware(publisher, "audit-events", "webhooks", nil))
h.Use(func(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
handler.ServeHTTP(w, r)
})
})
h.Use(func(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
handler.ServeHTTP(w, r.WithContext(logging.ContextWithLogger(r.Context(), logger)))
})
})
h.Get(PathHealthCheck, h.HealthCheckHandle)
h.Get(PathInfo, h.getInfo(info))
h.Group(func(r chi.Router) {
r.Use(auth.Middleware(authenticator))
r.Use(service.OTLPMiddleware("webhooks", debug))
r.Get(PathConfigs, h.getManyConfigsHandle)
r.Post(PathConfigs, h.insertOneConfigHandle)
r.Delete(PathConfigs+PathId, h.deleteOneConfigHandle)
r.Put(PathConfigs+PathId, h.updateOneConfigHandle)
r.Get(PathConfigs+PathId+PathTest, h.testOneConfigHandle)
r.Put(PathConfigs+PathId+PathActivate, h.activateOneConfigHandle)
r.Put(PathConfigs+PathId+PathDeactivate, h.deactivateOneConfigHandle)
r.Put(PathConfigs+PathId+PathChangeSecret, h.changeSecretHandle)
})
return h
}