From 00192f937edc9862832c1f9732f180445f2c810c Mon Sep 17 00:00:00 2001 From: zenfun Date: Sun, 21 Dec 2025 13:26:16 +0800 Subject: [PATCH] feat(api): add log_request_body_enabled feature flag support Add runtime feature flag to control whether request bodies are stored in logs. The Handler now accepts a Redis client to check the log_request_body_enabled feature flag before persisting log records. - Add logRequestBodyFeatureKey constant for feature flag - Inject Redis client into Handler for feature flag lookups - Strip request body from log records when feature is disabled - Update tests to pass Redis client to NewHandler --- cmd/server/main.go | 2 +- internal/api/feature_handler.go | 9 +++++---- internal/api/handler.go | 25 +++++++++++++++++++++++-- internal/api/model_handler_test.go | 2 +- internal/api/provider_handler_test.go | 2 +- 5 files changed, 31 insertions(+), 9 deletions(-) diff --git a/cmd/server/main.go b/cmd/server/main.go index 0c3282b..9be89bd 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -119,7 +119,7 @@ func main() { masterService := service.NewMasterService(db) healthService := service.NewHealthCheckService(db, rdb) - handler := api.NewHandler(db, syncService, logWriter) + handler := api.NewHandler(db, syncService, logWriter, rdb) adminHandler := api.NewAdminHandler(db, masterService, syncService) masterHandler := api.NewMasterHandler(db, masterService, syncService) internalHandler := api.NewInternalHandler(db) diff --git a/internal/api/feature_handler.go b/internal/api/feature_handler.go index dec0c12..7c452f5 100644 --- a/internal/api/feature_handler.go +++ b/internal/api/feature_handler.go @@ -14,10 +14,11 @@ import ( const featuresKey = "meta:features" const ( - logRetentionFeatureKey = "log_retention_days" - logMaxRecordsFeatureKey = "log_max_records" - logRetentionRedisKey = "meta:log:retention_days" - logMaxRecordsRedisKey = "meta:log:max_records" + logRetentionFeatureKey = "log_retention_days" + logMaxRecordsFeatureKey = "log_max_records" + logRequestBodyFeatureKey = "log_request_body_enabled" + logRetentionRedisKey = "meta:log:retention_days" + logMaxRecordsRedisKey = "meta:log:max_records" ) // FeatureHandler manages lightweight feature flags stored in Redis for DP/CP runtime toggles. diff --git a/internal/api/handler.go b/internal/api/handler.go index 8b1058c..98f94f7 100644 --- a/internal/api/handler.go +++ b/internal/api/handler.go @@ -1,6 +1,7 @@ package api import ( + "context" "net/http" "strconv" "strings" @@ -11,6 +12,7 @@ import ( groupx "github.com/ez-api/foundation/group" "github.com/ez-api/foundation/provider" "github.com/gin-gonic/gin" + "github.com/redis/go-redis/v9" "gorm.io/gorm" ) @@ -18,10 +20,11 @@ type Handler struct { db *gorm.DB sync *service.SyncService logger *service.LogWriter + rdb *redis.Client } -func NewHandler(db *gorm.DB, sync *service.SyncService, logger *service.LogWriter) *Handler { - return &Handler{db: db, sync: sync, logger: logger} +func NewHandler(db *gorm.DB, sync *service.SyncService, logger *service.LogWriter, rdb *redis.Client) *Handler { + return &Handler{db: db, sync: sync, logger: logger, rdb: rdb} } // CreateKey is now handled by MasterHandler @@ -463,7 +466,25 @@ func (h *Handler) IngestLog(c *gin.Context) { return } + if !h.logRequestBodyEnabled(c.Request.Context()) { + rec.RequestBody = "" + } + // By default, only metadata is expected; payload fields may be empty. h.logger.Write(rec) c.JSON(http.StatusAccepted, gin.H{"status": "queued"}) } + +func (h *Handler) logRequestBodyEnabled(ctx context.Context) bool { + if h == nil || h.rdb == nil { + return true + } + raw, err := h.rdb.HGet(ctx, featuresKey, logRequestBodyFeatureKey).Result() + if err == redis.Nil || strings.TrimSpace(raw) == "" { + return true + } + if err != nil { + return true + } + return strings.EqualFold(raw, "true") || strings.EqualFold(raw, "1") +} diff --git a/internal/api/model_handler_test.go b/internal/api/model_handler_test.go index 7d7ce70..69a23c3 100644 --- a/internal/api/model_handler_test.go +++ b/internal/api/model_handler_test.go @@ -33,7 +33,7 @@ func newTestHandlerWithRedis(t *testing.T) (*Handler, *gorm.DB, *miniredis.Minir mr := miniredis.RunT(t) rdb := redis.NewClient(&redis.Options{Addr: mr.Addr()}) sync := service.NewSyncService(rdb) - return NewHandler(db, sync, nil), db, mr + return NewHandler(db, sync, nil, rdb), db, mr } func TestCreateModel_DefaultsKindChat_AndWritesModelsMeta(t *testing.T) { diff --git a/internal/api/provider_handler_test.go b/internal/api/provider_handler_test.go index 163b8eb..0999228 100644 --- a/internal/api/provider_handler_test.go +++ b/internal/api/provider_handler_test.go @@ -36,7 +36,7 @@ func newTestHandler(t *testing.T) (*Handler, *gorm.DB) { rdb := redis.NewClient(&redis.Options{Addr: mr.Addr()}) sync := service.NewSyncService(rdb) - return NewHandler(db, sync, nil), db + return NewHandler(db, sync, nil, rdb), db } func TestCreateProvider_DefaultsVertexLocationGlobal(t *testing.T) {