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))
// 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)
// Auth routes (no auth required, but rate limited)
+3
View File
@@ -34,6 +34,9 @@ services:
DATABASE_NAME: bookmann
JWT_SECRET: ${JWT_SECRET}
SERVER_PORT: 8765
TEST_MODE: ${TEST_MODE:-false}
RATE_LIMIT_ENABLED: ${RATE_LIMIT_ENABLED:-true}
REQUESTS_PER_MINUTE: ${REQUESTS_PER_MINUTE:-10}
ports:
- "8765:8765"
depends_on:
+43 -16
View File
@@ -3,29 +3,36 @@ package config
import (
"fmt"
"os"
"strconv"
)
type Config struct {
ServerPort string
JWTSecret string
UploadPath string
DatabaseHost string
DatabasePort string
DatabaseUser string
DatabasePassword string
DatabaseName string
ServerPort string
JWTSecret string
UploadPath string
DatabaseHost string
DatabasePort string
DatabaseUser string
DatabasePassword string
DatabaseName string
TestMode bool
RateLimitEnabled bool
RequestsPerMinute int
}
func LoadConfig() *Config {
return &Config{
ServerPort: getEnv("SERVER_PORT", "8080"),
DatabaseHost: getEnv("DATABASE_HOST", "localhost"),
DatabasePort: getEnv("DATABASE_PORT", "5432"),
DatabaseUser: getEnv("DATABASE_USER", "postgres"),
DatabasePassword: getEnv("DATABASE_PASSWORD", "password"),
DatabaseName: getEnv("DATABASE_NAME", "bookmann"),
JWTSecret: getEnv("JWT_SECRET", "your-secret-key"),
UploadPath: getEnv("UPLOAD_PATH", "./uploads"),
ServerPort: getEnv("SERVER_PORT", "8080"),
DatabaseHost: getEnv("DATABASE_HOST", "localhost"),
DatabasePort: getEnv("DATABASE_PORT", "5432"),
DatabaseUser: getEnv("DATABASE_USER", "postgres"),
DatabasePassword: getEnv("DATABASE_PASSWORD", "password"),
DatabaseName: getEnv("DATABASE_NAME", "bookmann"),
JWTSecret: getEnv("JWT_SECRET", "your-secret-key"),
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
}
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
type RateLimiterConfig struct {
Enabled bool
RequestsPerMinute int
CleanupInterval time.Duration
}
@@ -17,6 +18,7 @@ type RateLimiterConfig struct {
// DefaultRateLimiterConfig returns sensible defaults
func DefaultRateLimiterConfig() RateLimiterConfig {
return RateLimiterConfig{
Enabled: true,
RequestsPerMinute: 10,
CleanupInterval: 5 * time.Minute,
}
@@ -98,6 +100,11 @@ func (rl *RateLimiter) Allow(ip string) bool {
func RateLimiterMiddleware(rl *RateLimiter) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// If rate limiting is disabled, skip checks
if !rl.config.Enabled {
return next(c)
}
// Get client IP
ip := c.RealIP()
if ip == "" {