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:
2026-01-29 13:33:18 -05:00
parent ce0e448e58
commit 4b8cb58c84
4 changed files with 59 additions and 17 deletions
+6 -1
View File
@@ -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)
+3
View File
@@ -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
View File
@@ -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
}
+7
View File
@@ -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 == "" {