做完了一部分,备份一下,防止以外删除

This commit is contained in:
2025-11-11 15:16:38 +08:00
parent 9600e5b6e0
commit e98dd4d725
39 changed files with 2423 additions and 183 deletions

View File

@@ -1,7 +1,7 @@
# Research: Fiber Middleware Integration
**Feature**: 001-fiber-middleware-integration
**Date**: 2025-11-10
**Feature**: 001-fiber-middleware-integration
**Date**: 2025-11-10
**Phase**: 0 - Research & Discovery
## Overview
@@ -26,20 +26,20 @@ This document resolves technical unknowns and establishes best practices for int
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
log.Info("Config file changed", zap.String("file", e.Name))
// Reload config atomically
newConfig := &Config{}
if err := viper.Unmarshal(newConfig); err != nil {
log.Error("Failed to reload config", zap.Error(err))
return // Keep existing config
}
// Validate new config
if err := newConfig.Validate(); err != nil {
log.Error("Invalid config", zap.Error(err))
return // Keep existing config
}
// Atomic swap using sync/atomic
atomic.StorePointer(&globalConfig, unsafe.Pointer(newConfig))
})
@@ -191,12 +191,12 @@ type TokenValidator struct {
func (v *TokenValidator) Validate(token string) (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
defer cancel()
// Check Redis availability
if err := v.redis.Ping(ctx).Err(); err != nil {
return "", ErrRedisUnavailable // Fail closed
}
// Get user ID from token
userID, err := v.redis.Get(ctx, constants.RedisAuthTokenKey(token)).Result()
if err == redis.Nil {
@@ -205,7 +205,7 @@ func (v *TokenValidator) Validate(token string) (string, error) {
if err != nil {
return "", fmt.Errorf("redis get: %w", err)
}
return userID, nil
}
@@ -217,12 +217,12 @@ func KeyAuth(validator *validator.TokenValidator, logger *zap.Logger) fiber.Hand
userID, err := validator.Validate(key)
if err != nil {
logger.Warn("Token validation failed",
zap.String("request_id", c.Locals("requestid").(string)),
zap.String("request_id", c.Locals(constants.ContextKeyRequestID).(string)),
zap.Error(err),
)
return false, err
}
// Store user ID in context
c.Locals("user_id", userID)
return true, nil
@@ -413,32 +413,32 @@ func main() {
// Create root context with cancellation
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Initialize components with context
cfg := config.Load()
go config.Watch(ctx, cfg) // Pass context to watcher
app := setupApp(cfg)
// Graceful shutdown signal handling
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt, syscall.SIGTERM)
go func() {
if err := app.Listen(cfg.Server.Address); err != nil {
log.Fatal("Server failed", zap.Error(err))
}
}()
<-quit // Block until signal
log.Info("Shutting down server...")
cancel() // Cancel context (stops config watcher)
if err := app.ShutdownWithTimeout(30 * time.Second); err != nil {
log.Error("Forced shutdown", zap.Error(err))
}
log.Info("Server stopped")
}
```
@@ -455,7 +455,7 @@ func Watch(ctx context.Context, cfg *Config) {
// Reload config logic
}
})
<-ctx.Done() // Block until cancelled
log.Info("Config watcher stopped")
}
@@ -512,22 +512,22 @@ func TestTokenValidator_Validate(t *testing.T) {
},
// More cases...
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockRedis := &mock.Redis{}
tt.setupMock(mockRedis)
validator := NewTokenValidator(mockRedis, zap.NewNop())
userID, err := validator.Validate(tt.token)
if err != tt.wantErr {
t.Errorf("got error %v, want %v", err, tt.wantErr)
}
if userID != tt.wantUser {
t.Errorf("got userID %s, want %s", userID, tt.wantUser)
}
mockRedis.AssertExpectations(t)
})
}
@@ -538,7 +538,7 @@ func TestTokenValidator_Validate(t *testing.T) {
```go
func TestMiddlewareChain(t *testing.T) {
// Start testcontainer Redis
redisContainer, err := testcontainers.GenericContainer(ctx,
redisContainer, err := testcontainers.GenericContainer(ctx,
testcontainers.GenericContainerRequest{
ContainerRequest: testcontainers.ContainerRequest{
Image: "redis:7-alpine",
@@ -548,10 +548,10 @@ func TestMiddlewareChain(t *testing.T) {
})
require.NoError(t, err)
defer redisContainer.Terminate(ctx)
// Setup app with middleware
app := setupTestApp(redisContainer)
// Test cases
tests := []struct {
name string
@@ -571,22 +571,22 @@ func TestMiddlewareChain(t *testing.T) {
},
// More cases...
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.setupToken != nil {
tt.setupToken(redisClient)
}
req := httptest.NewRequest("GET", "/api/v1/test", nil)
for k, v := range tt.headers {
req.Header.Set(k, v)
}
resp, err := app.Test(req)
require.NoError(t, err)
assert.Equal(t, tt.expectedStatus, resp.StatusCode)
// Parse response body and check code
var body response.Response
json.NewDecoder(resp.Body).Decode(&body)