package middleware import ( "crypto/rand" "encoding/hex" "fmt" "strings" "sync/atomic" "time" "hfb_sys/backend/internal/logging" "github.com/gin-gonic/gin" ) var requestIDFallbackCounter atomic.Uint64 const ( RequestIDHeader = "X-Request-ID" ContextRequestID = "request_id" ) func RequestID() gin.HandlerFunc { return func(c *gin.Context) { requestID := c.GetHeader(RequestIDHeader) if !validRequestID(requestID) { requestID = newRequestID() } c.Set(ContextRequestID, requestID) c.Writer.Header().Set(RequestIDHeader, requestID) c.Request = c.Request.WithContext(logging.WithRequestID(c.Request.Context(), requestID)) c.Next() } } func GetRequestID(c *gin.Context) string { value, ok := c.Get(ContextRequestID) if !ok { return "" } requestID, ok := value.(string) if !ok { return "" } return requestID } func newRequestID() string { buf := make([]byte, 16) if _, err := rand.Read(buf); err != nil { return fmt.Sprintf("%x-%x", time.Now().UnixNano(), requestIDFallbackCounter.Add(1)) } return hex.EncodeToString(buf) } func validRequestID(value string) bool { if value == "" || len(value) > 64 { return false } return strings.IndexFunc(value, func(char rune) bool { return !((char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z') || (char >= '0' && char <= '9') || char == '-' || char == '_' || char == '.') }) == -1 }