From 8b09e67da04ad57c008006146d5d20b6f42baab6 Mon Sep 17 00:00:00 2001 From: kolaente Date: Thu, 11 Jun 2026 21:32:25 +0200 Subject: [PATCH] fix(routes): generate request IDs at the start of the middleware chain Echo's RequestID middleware reuses the X-Request-Id header from a proxy or generates one, so logging and audit all see the same ID. RequestMeta previously read the request header before any later middleware could have set one, leaving the audit request_id mostly empty. --- pkg/routes/middleware/request_meta.go | 13 +++++-------- pkg/routes/routes.go | 5 +++++ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/pkg/routes/middleware/request_meta.go b/pkg/routes/middleware/request_meta.go index 747a37826..865cd5cde 100644 --- a/pkg/routes/middleware/request_meta.go +++ b/pkg/routes/middleware/request_meta.go @@ -22,21 +22,18 @@ import ( "github.com/labstack/echo/v5" ) -// RequestMeta stashes IP, User-Agent and X-Request-ID on the request context -// so events dispatched while handling the request carry them as message -// metadata (consumed by the audit listeners). +// RequestMeta stashes IP, User-Agent and the request ID on the request +// context so events dispatched while handling the request carry them as +// message metadata (consumed by the audit listeners). Must run after the +// RequestID middleware, which guarantees the response header is populated. func RequestMeta() echo.MiddlewareFunc { return func(next echo.HandlerFunc) echo.HandlerFunc { return func(c *echo.Context) error { req := c.Request() - requestID := req.Header.Get(echo.HeaderXRequestID) - if requestID == "" { - requestID = c.Response().Header().Get(echo.HeaderXRequestID) - } ctx := events.WithRequestMeta(req.Context(), &events.RequestMeta{ IP: c.RealIP(), UserAgent: req.UserAgent(), - RequestID: requestID, + RequestID: c.Response().Header().Get(echo.HeaderXRequestID), }) c.SetRequest(req.WithContext(ctx)) return next(c) diff --git a/pkg/routes/routes.go b/pkg/routes/routes.go index f8fc1609f..9f6af5af3 100644 --- a/pkg/routes/routes.go +++ b/pkg/routes/routes.go @@ -155,6 +155,11 @@ func NewEcho() *echo.Echo { e.Logger = log.NewEchoLogger(config.LogEnabled.GetBool(), config.LogHTTP.GetString(), config.LogFormat.GetString()) + // First middleware in the chain so every request has an ID — reuses the + // X-Request-Id header from a proxy or generates one — and everything + // downstream (logging, audit) sees the same value. + e.Use(middleware.RequestID()) + // Logger if config.LogEnabled.GetBool() && config.LogHTTP.GetString() != "off" { httpLogger := log.NewHTTPLogger(config.LogEnabled.GetBool(), config.LogHTTP.GetString(), config.LogFormat.GetString())