diff --git a/main_dc/yalarba/api_yal/go.mod b/main_dc/yalarba/api_yal/go.mod index a44437a..d86b210 100644 --- a/main_dc/yalarba/api_yal/go.mod +++ b/main_dc/yalarba/api_yal/go.mod @@ -22,6 +22,7 @@ require ( require ( github.com/go-chi/chi v1.5.5 github.com/go-chi/chi/v5 v5.2.5 + github.com/go-chi/cors v1.2.2 github.com/go-playground/validator/v10 v10.30.1 github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect diff --git a/main_dc/yalarba/api_yal/go.sum b/main_dc/yalarba/api_yal/go.sum index e9c0900..452d6c0 100644 --- a/main_dc/yalarba/api_yal/go.sum +++ b/main_dc/yalarba/api_yal/go.sum @@ -5,6 +5,8 @@ github.com/go-chi/chi v1.5.5 h1:vOB/HbEMt9QqBqErz07QehcOKHaWFtuj87tTDVz2qXE= github.com/go-chi/chi v1.5.5/go.mod h1:C9JqLr3tIYjDOZpzn+BCuxY8z8vmca43EeMgyZt7irw= github.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug= github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0= +github.com/go-chi/cors v1.2.2 h1:Jmey33TE+b+rB7fT8MUy1u0I4L+NARQlK6LhzKPSyQE= +github.com/go-chi/cors v1.2.2/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= diff --git a/main_dc/yalarba/api_yal/internal/config/config.go b/main_dc/yalarba/api_yal/internal/config/config.go index 733fd3e..b6ee746 100644 --- a/main_dc/yalarba/api_yal/internal/config/config.go +++ b/main_dc/yalarba/api_yal/internal/config/config.go @@ -14,8 +14,16 @@ type Config struct { ServerPort string UploadPath string LogLevel string - Environment string + Environment string `yaml:"environment" env:"ENVIRONMENT" default:"development"` AppPort string + CORS struct { + AllowedOrigins []string `yaml:"allowed_origins" env:"CORS_ALLOWED_ORIGINS"` + } `yaml:"cors"` + + RateLimit struct { + Enabled bool `yaml:"enabled" env:"RATE_LIMIT_ENABLED" default:"false"` + RequestsPerSecond int `yaml:"requests_per_second" env:"RATE_LIMIT_REQUESTS" default:"100"` + } `yaml:"rate_limit"` } func Load() *Config { diff --git a/main_dc/yalarba/api_yal/internal/router/router.go b/main_dc/yalarba/api_yal/internal/router/router.go index 0141f79..61ead1b 100644 --- a/main_dc/yalarba/api_yal/internal/router/router.go +++ b/main_dc/yalarba/api_yal/internal/router/router.go @@ -3,11 +3,16 @@ package router import ( "api_yal/internal/config" "api_yal/internal/logger" + CastomMiddleware "api_yal/internal/middleware" + "time" "encoding/json" - "github.com/go-chi/chi/v5" - "gorm.io/gorm" "net/http" + + "github.com/go-chi/chi/v5" + ChiMiddleware "github.com/go-chi/chi/v5/middleware" + "github.com/go-chi/cors" + "gorm.io/gorm" ) func SetupRouter(db *gorm.DB, config *config.Config) http.Handler { @@ -16,6 +21,9 @@ func SetupRouter(db *gorm.DB, config *config.Config) http.Handler { zapLogger.Info("Start setup routers") r := chi.NewRouter() + // Добавляем все production middleware + addProductionMiddleware(r, config) + // Health check r.Get("/health", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -23,7 +31,8 @@ func SetupRouter(db *gorm.DB, config *config.Config) http.Handler { json.NewEncoder(w).Encode(map[string]string{"status": "healthy"}) }) - r.Group() + // Auth middleware + r.Use(CastomMiddleware.AuthMiddlewareWithContext) zapLogger.Info("End setup routers") @@ -33,3 +42,67 @@ func SetupRouter(db *gorm.DB, config *config.Config) http.Handler { return r } + +// addProductionMiddleware добавляет все необходимые middleware для production окружения +func addProductionMiddleware(r *chi.Mux, config *config.Config) { + // Request ID middleware - добавляет уникальный ID к каждому запросу + r.Use(ChiMiddleware.RequestID) + + // Real IP middleware - корректно определяет реальный IP клиента (за прокси) + r.Use(ChiMiddleware.RealIP) + + // Logger middleware - логирует все запросы в формате Apache + r.Use(ChiMiddleware.Logger) + + // Recoverer middleware - восстанавливает после паники и возвращает 500 + r.Use(ChiMiddleware.Recoverer) + + // Timeout middleware - устанавливает таймаут на запросы (30 секунд) + r.Use(ChiMiddleware.Timeout(30 * time.Second)) + + // Compress middleware - сжимает ответы (gzip) + r.Use(ChiMiddleware.Compress(5, "gzip")) + + // StripSlashes middleware - удаляет слеши в конце URL или редиректит + r.Use(ChiMiddleware.StripSlashes) + + // CORS middleware - настройка CORS для production + r.Use(cors.Handler(cors.Options{ + AllowedOrigins: config.CORS.AllowedOrigins, + AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"}, + AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token", "X-Request-ID"}, + ExposedHeaders: []string{"Link", "X-Request-ID"}, + AllowCredentials: true, + MaxAge: 300, + })) + + // Content-Type middleware - проверяет Content-Type для POST/PUT запросов + r.Use(ChiMiddleware.AllowContentType("application/json", "application/xml")) + + // Дополнительные middleware в зависимости от конфигурации + if config.Environment == "development" { + // Добавляем дополнительное логирование для разработки + r.Use(ChiMiddleware.Logger) + } + + if config.RateLimit.Enabled { + // Добавляем rate limiting если включено в конфиге + r.Use(ChiMiddleware.Throttle(config.RateLimit.RequestsPerSecond)) + } + + // Security headers middleware + r.Use(func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("X-Content-Type-Options", "nosniff") + w.Header().Set("X-Frame-Options", "DENY") + w.Header().Set("X-XSS-Protection", "1; mode=block") + w.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin") + + if config.Environment == "production" { + w.Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload") + } + + next.ServeHTTP(w, r) + }) + }) +} \ No newline at end of file