75b2f3f6b2
modified: main_dc/yalarba/api_yal/internal/domain/account/dto.go new file: main_dc/yalarba/api_yal/internal/domain/account/errors.go modified: main_dc/yalarba/api_yal/internal/domain/account/handler.go modified: main_dc/yalarba/api_yal/internal/domain/account/router.go modified: main_dc/yalarba/api_yal/internal/domain/account/service.go new file: main_dc/yalarba/api_yal/internal/domain/account/types.go new file: main_dc/yalarba/api_yal/internal/middleware/admin.go modified: main_dc/yalarba/api_yal/internal/middleware/auth.go new file: main_dc/yalarba/api_yal/internal/middleware/context.go new file: main_dc/yalarba/api_yal/internal/middleware/logging.go modified: main_dc/yalarba/api_yal/internal/router/router.go last but not yet commit
111 lines
3.8 KiB
Go
111 lines
3.8 KiB
Go
package router
|
|
|
|
import (
|
|
"api_yal/internal/config"
|
|
"api_yal/internal/logger"
|
|
"api_yal/internal/domain/auth"
|
|
"api_yal/internal/domain/account"
|
|
"time"
|
|
|
|
"encoding/json"
|
|
"net/http"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
ChiMiddleware "github.com/go-chi/chi/v5/middleware"
|
|
"github.com/go-chi/cors"
|
|
"go.uber.org/zap"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// SetupRouter инициализирует и настраивает основной маршрутизатор приложения
|
|
func SetupRouter(db *gorm.DB, config *config.Config) http.Handler {
|
|
zapLogger := logger.Get()
|
|
zapLogger.Info("Начало настройки маршрутов")
|
|
|
|
r := chi.NewRouter()
|
|
|
|
// ВСЕ middleware должны быть определены ДО маршрутов
|
|
// 1. Сначала добавляем production middleware
|
|
addProductionMiddleware(r, config)
|
|
|
|
// 2. Затем добавляем middleware аутентификации (он тоже применяется ко всем маршрутам)
|
|
zapLogger.Debug("Auth middleware применён")
|
|
|
|
// 3. И только потом регистрируем маршруты
|
|
// Health check (не требует аутентификации, поэтому должен быть выше AuthMiddleware)
|
|
r.Get("/health", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
if err := json.NewEncoder(w).Encode(map[string]string{"status": "healthy"}); err != nil {
|
|
zapLogger.Error("Ошибка при отправке health check ответа",
|
|
zap.String("path", r.URL.Path),
|
|
zap.Error(err),
|
|
)
|
|
}
|
|
})
|
|
zapLogger.Debug("Health check маршрут зарегистрирован")
|
|
|
|
// Группируем API маршруты под /api/v1
|
|
r.Route("/api/v1", func(r chi.Router) {
|
|
// Регистрируем маршруты аутентификации
|
|
auth.RegisterRoutes(r, db, config.JWTSecret)
|
|
|
|
// Регистрируем маршруты аккаунтов
|
|
account.RegisterRoutes(r, db, config.JWTSecret)
|
|
})
|
|
|
|
zapLogger.Info("Настройка маршрутов завершена")
|
|
|
|
// Логируем маршруты (опционально)
|
|
routeLogger := logger.NewRouteLogger(logger.NewWrapper(zapLogger))
|
|
routeLogger.LogRoutes(r)
|
|
|
|
return r
|
|
}
|
|
|
|
// addProductionMiddleware добавляет все middleware для production
|
|
func addProductionMiddleware(r *chi.Mux, config *config.Config) {
|
|
// Базовые middleware
|
|
r.Use(ChiMiddleware.RequestID)
|
|
r.Use(ChiMiddleware.RealIP)
|
|
r.Use(ChiMiddleware.Logger)
|
|
r.Use(ChiMiddleware.Recoverer)
|
|
r.Use(ChiMiddleware.Timeout(30 * time.Second))
|
|
r.Use(ChiMiddleware.Compress(5, "gzip"))
|
|
r.Use(ChiMiddleware.StripSlashes)
|
|
|
|
// CORS
|
|
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 проверка
|
|
r.Use(ChiMiddleware.AllowContentType("application/json", "application/xml"))
|
|
|
|
// Rate limiting
|
|
if config.RateLimit.Enabled {
|
|
r.Use(ChiMiddleware.Throttle(config.RateLimit.RequestsPerSecond))
|
|
}
|
|
|
|
// Security headers
|
|
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)
|
|
})
|
|
})
|
|
} |