feat: add configurable test mode and rate limiting
- Add TestMode, RateLimitEnabled, RequestsPerMinute to Config - Add getEnvBool() and getEnvInt() helper functions - Update rate limiter to support enabled/disabled state - Pass test environment variables through docker-compose - Configure rate limiter dynamically in main.go This allows disabling rate limiting for integration testing while maintaining security in production environments.
This commit is contained in:
+6
-1
@@ -67,7 +67,12 @@ func main() {
|
|||||||
e.Use(ratelimit.RequestTracingMiddleware(cfg))
|
e.Use(ratelimit.RequestTracingMiddleware(cfg))
|
||||||
|
|
||||||
// Rate limiter for auth endpoints
|
// Rate limiter for auth endpoints
|
||||||
rateLimiter := ratelimit.NewRateLimiter(ratelimit.DefaultRateLimiterConfig())
|
rateLimiterConfig := ratelimit.RateLimiterConfig{
|
||||||
|
Enabled: cfg.RateLimitEnabled,
|
||||||
|
RequestsPerMinute: cfg.RequestsPerMinute,
|
||||||
|
CleanupInterval: 5 * time.Minute,
|
||||||
|
}
|
||||||
|
rateLimiter := ratelimit.NewRateLimiter(rateLimiterConfig)
|
||||||
rateLimitMiddleware := ratelimit.RateLimiterMiddleware(rateLimiter)
|
rateLimitMiddleware := ratelimit.RateLimiterMiddleware(rateLimiter)
|
||||||
|
|
||||||
// Auth routes (no auth required, but rate limited)
|
// Auth routes (no auth required, but rate limited)
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ services:
|
|||||||
DATABASE_NAME: bookmann
|
DATABASE_NAME: bookmann
|
||||||
JWT_SECRET: ${JWT_SECRET}
|
JWT_SECRET: ${JWT_SECRET}
|
||||||
SERVER_PORT: 8765
|
SERVER_PORT: 8765
|
||||||
|
TEST_MODE: ${TEST_MODE:-false}
|
||||||
|
RATE_LIMIT_ENABLED: ${RATE_LIMIT_ENABLED:-true}
|
||||||
|
REQUESTS_PER_MINUTE: ${REQUESTS_PER_MINUTE:-10}
|
||||||
ports:
|
ports:
|
||||||
- "8765:8765"
|
- "8765:8765"
|
||||||
depends_on:
|
depends_on:
|
||||||
|
|||||||
+43
-16
@@ -3,29 +3,36 @@ package config
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
ServerPort string
|
ServerPort string
|
||||||
JWTSecret string
|
JWTSecret string
|
||||||
UploadPath string
|
UploadPath string
|
||||||
DatabaseHost string
|
DatabaseHost string
|
||||||
DatabasePort string
|
DatabasePort string
|
||||||
DatabaseUser string
|
DatabaseUser string
|
||||||
DatabasePassword string
|
DatabasePassword string
|
||||||
DatabaseName string
|
DatabaseName string
|
||||||
|
TestMode bool
|
||||||
|
RateLimitEnabled bool
|
||||||
|
RequestsPerMinute int
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadConfig() *Config {
|
func LoadConfig() *Config {
|
||||||
return &Config{
|
return &Config{
|
||||||
ServerPort: getEnv("SERVER_PORT", "8080"),
|
ServerPort: getEnv("SERVER_PORT", "8080"),
|
||||||
DatabaseHost: getEnv("DATABASE_HOST", "localhost"),
|
DatabaseHost: getEnv("DATABASE_HOST", "localhost"),
|
||||||
DatabasePort: getEnv("DATABASE_PORT", "5432"),
|
DatabasePort: getEnv("DATABASE_PORT", "5432"),
|
||||||
DatabaseUser: getEnv("DATABASE_USER", "postgres"),
|
DatabaseUser: getEnv("DATABASE_USER", "postgres"),
|
||||||
DatabasePassword: getEnv("DATABASE_PASSWORD", "password"),
|
DatabasePassword: getEnv("DATABASE_PASSWORD", "password"),
|
||||||
DatabaseName: getEnv("DATABASE_NAME", "bookmann"),
|
DatabaseName: getEnv("DATABASE_NAME", "bookmann"),
|
||||||
JWTSecret: getEnv("JWT_SECRET", "your-secret-key"),
|
JWTSecret: getEnv("JWT_SECRET", "your-secret-key"),
|
||||||
UploadPath: getEnv("UPLOAD_PATH", "./uploads"),
|
UploadPath: getEnv("UPLOAD_PATH", "./uploads"),
|
||||||
|
TestMode: getEnvBool("TEST_MODE", false),
|
||||||
|
RateLimitEnabled: getEnvBool("RATE_LIMIT_ENABLED", true),
|
||||||
|
RequestsPerMinute: getEnvInt("REQUESTS_PER_MINUTE", 10),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,3 +47,23 @@ func getEnv(key, defaultValue string) string {
|
|||||||
}
|
}
|
||||||
return defaultValue
|
return defaultValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getEnvBool(key string, defaultValue bool) bool {
|
||||||
|
if value := os.Getenv(key); value != "" {
|
||||||
|
boolVal, err := strconv.ParseBool(value)
|
||||||
|
if err == nil {
|
||||||
|
return boolVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func getEnvInt(key string, defaultValue int) int {
|
||||||
|
if value := os.Getenv(key); value != "" {
|
||||||
|
intVal, err := strconv.Atoi(value)
|
||||||
|
if err == nil {
|
||||||
|
return intVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
// RateLimiterConfig defines rate limiting configuration
|
// RateLimiterConfig defines rate limiting configuration
|
||||||
type RateLimiterConfig struct {
|
type RateLimiterConfig struct {
|
||||||
|
Enabled bool
|
||||||
RequestsPerMinute int
|
RequestsPerMinute int
|
||||||
CleanupInterval time.Duration
|
CleanupInterval time.Duration
|
||||||
}
|
}
|
||||||
@@ -17,6 +18,7 @@ type RateLimiterConfig struct {
|
|||||||
// DefaultRateLimiterConfig returns sensible defaults
|
// DefaultRateLimiterConfig returns sensible defaults
|
||||||
func DefaultRateLimiterConfig() RateLimiterConfig {
|
func DefaultRateLimiterConfig() RateLimiterConfig {
|
||||||
return RateLimiterConfig{
|
return RateLimiterConfig{
|
||||||
|
Enabled: true,
|
||||||
RequestsPerMinute: 10,
|
RequestsPerMinute: 10,
|
||||||
CleanupInterval: 5 * time.Minute,
|
CleanupInterval: 5 * time.Minute,
|
||||||
}
|
}
|
||||||
@@ -98,6 +100,11 @@ func (rl *RateLimiter) Allow(ip string) bool {
|
|||||||
func RateLimiterMiddleware(rl *RateLimiter) echo.MiddlewareFunc {
|
func RateLimiterMiddleware(rl *RateLimiter) echo.MiddlewareFunc {
|
||||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
return func(c echo.Context) error {
|
return func(c echo.Context) error {
|
||||||
|
// If rate limiting is disabled, skip checks
|
||||||
|
if !rl.config.Enabled {
|
||||||
|
return next(c)
|
||||||
|
}
|
||||||
|
|
||||||
// Get client IP
|
// Get client IP
|
||||||
ip := c.RealIP()
|
ip := c.RealIP()
|
||||||
if ip == "" {
|
if ip == "" {
|
||||||
|
|||||||
Reference in New Issue
Block a user