package main import ( "net/http" "time" "github.com/food-ai/backend/internal/domain/auth" "github.com/food-ai/backend/internal/infra/config" "github.com/food-ai/backend/internal/domain/diary" "github.com/food-ai/backend/internal/domain/dish" "github.com/food-ai/backend/internal/adapters/kafka" "github.com/food-ai/backend/internal/adapters/openai" "github.com/food-ai/backend/internal/domain/home" "github.com/food-ai/backend/internal/domain/menu" "github.com/food-ai/backend/internal/infra/middleware" "github.com/food-ai/backend/internal/adapters/pexels" "github.com/food-ai/backend/internal/domain/product" "github.com/food-ai/backend/internal/domain/recipe" "github.com/food-ai/backend/internal/domain/recognition" "github.com/food-ai/backend/internal/domain/recommendation" "github.com/food-ai/backend/internal/domain/savedrecipe" "github.com/food-ai/backend/internal/domain/cuisine" "github.com/food-ai/backend/internal/infra/server" "github.com/food-ai/backend/internal/domain/tag" "github.com/food-ai/backend/internal/domain/units" "github.com/food-ai/backend/internal/domain/user" "github.com/food-ai/backend/internal/domain/userproduct" "github.com/jackc/pgx/v5/pgxpool" ) // --------------------------------------------------------------------------- // Newtypes for config primitives — prevents Wire type collisions. // --------------------------------------------------------------------------- // Newtypes for list handlers — prevents Wire type collisions on http.HandlerFunc. type unitsListHandler http.HandlerFunc type cuisineListHandler http.HandlerFunc type tagListHandler http.HandlerFunc // --------------------------------------------------------------------------- type openaiAPIKey string type pexelsAPIKey string type jwtSecret string type jwtAccessDuration time.Duration type jwtRefreshDuration time.Duration type allowedOrigins []string // --------------------------------------------------------------------------- // Config extractors // --------------------------------------------------------------------------- func newOpenAIAPIKey(appConfig *config.Config) openaiAPIKey { return openaiAPIKey(appConfig.OpenAIAPIKey) } func newPexelsAPIKey(appConfig *config.Config) pexelsAPIKey { return pexelsAPIKey(appConfig.PexelsAPIKey) } func newJWTSecret(appConfig *config.Config) jwtSecret { return jwtSecret(appConfig.JWTSecret) } func newJWTAccessDuration(appConfig *config.Config) jwtAccessDuration { return jwtAccessDuration(appConfig.JWTAccessDuration) } func newJWTRefreshDuration(appConfig *config.Config) jwtRefreshDuration { return jwtRefreshDuration(appConfig.JWTRefreshDuration) } func newAllowedOrigins(appConfig *config.Config) allowedOrigins { return allowedOrigins(appConfig.AllowedOrigins) } // newFirebaseCredentialsFile is the only string that reaches NewFirebaseAuthOrNoop, // so no newtype is needed here. func newFirebaseCredentialsFile(appConfig *config.Config) string { return appConfig.FirebaseCredentialsFile } // --------------------------------------------------------------------------- // Constructor wrappers for functions that accept primitive newtypes // --------------------------------------------------------------------------- func newOpenAIClient(key openaiAPIKey) *openai.Client { return openai.NewClient(string(key)) } func newPexelsClient(key pexelsAPIKey) *pexels.Client { return pexels.NewClient(string(key)) } func newJWTManager( secret jwtSecret, accessDuration jwtAccessDuration, refreshDuration jwtRefreshDuration, ) *auth.JWTManager { return auth.NewJWTManager(string(secret), time.Duration(accessDuration), time.Duration(refreshDuration)) } // --------------------------------------------------------------------------- // List handler providers // --------------------------------------------------------------------------- func newUnitsListHandler(pool *pgxpool.Pool) unitsListHandler { return unitsListHandler(units.NewListHandler(pool)) } func newCuisineListHandler(pool *pgxpool.Pool) cuisineListHandler { return cuisineListHandler(cuisine.NewListHandler(pool)) } func newTagListHandler(pool *pgxpool.Pool) tagListHandler { return tagListHandler(tag.NewListHandler(pool)) } // --------------------------------------------------------------------------- // newRouter wraps server.NewRouter to accept newtypes. func newRouter( pool *pgxpool.Pool, authHandler *auth.Handler, userHandler *user.Handler, recommendationHandler *recommendation.Handler, savedRecipeHandler *savedrecipe.Handler, productHandler *product.Handler, userProductHandler *userproduct.Handler, recognitionHandler *recognition.Handler, menuHandler *menu.Handler, diaryHandler *diary.Handler, homeHandler *home.Handler, dishHandler *dish.Handler, recipeHandler *recipe.Handler, authMiddleware func(http.Handler) http.Handler, origins allowedOrigins, unitsHandler unitsListHandler, cuisineHandler cuisineListHandler, tagHandler tagListHandler, ) http.Handler { return server.NewRouter( pool, authHandler, userHandler, recommendationHandler, savedRecipeHandler, productHandler, userProductHandler, recognitionHandler, menuHandler, diaryHandler, homeHandler, dishHandler, recipeHandler, authMiddleware, []string(origins), http.HandlerFunc(unitsHandler), http.HandlerFunc(cuisineHandler), http.HandlerFunc(tagHandler), ) } // --------------------------------------------------------------------------- // jwtAdapter — adapts *auth.JWTManager to middleware.AccessTokenValidator. // --------------------------------------------------------------------------- type jwtAdapter struct { jwtManager *auth.JWTManager } func newJWTAdapter(jm *auth.JWTManager) *jwtAdapter { return &jwtAdapter{jwtManager: jm} } func (adapter *jwtAdapter) ValidateAccessToken(tokenStr string) (*middleware.TokenClaims, error) { claims, validationError := adapter.jwtManager.ValidateAccessToken(tokenStr) if validationError != nil { return nil, validationError } return &middleware.TokenClaims{ UserID: claims.UserID, Plan: claims.Plan, }, nil } // newAuthMiddleware wraps middleware.Auth for Wire injection. func newAuthMiddleware(validator middleware.AccessTokenValidator) func(http.Handler) http.Handler { return middleware.Auth(validator) } // --------------------------------------------------------------------------- // Kafka providers // --------------------------------------------------------------------------- func newKafkaProducer(appConfig *config.Config) (*kafka.Producer, error) { return kafka.NewProducer(appConfig.KafkaBrokers) } // --------------------------------------------------------------------------- // Interface assertions (compile-time checks) // --------------------------------------------------------------------------- var _ middleware.AccessTokenValidator = (*jwtAdapter)(nil) var _ menu.PhotoSearcher = (*pexels.Client)(nil) var _ menu.UserLoader = (*user.Repository)(nil) var _ menu.ProductLister = (*userproduct.Repository)(nil) var _ menu.RecipeSaver = (*dish.Repository)(nil) var _ recommendation.PhotoSearcher = (*pexels.Client)(nil) var _ recommendation.UserLoader = (*user.Repository)(nil) var _ recommendation.ProductLister = (*userproduct.Repository)(nil) var _ recognition.ProductRepository = (*product.Repository)(nil) var _ recognition.KafkaPublisher = (*kafka.Producer)(nil) var _ recognition.JobRepository = (*recognition.PostgresJobRepository)(nil) var _ user.UserRepository = (*user.Repository)(nil)