refactor: migrate DI to Wire, replace startup registries with on-demand DB queries
- Add google/wire; generate wire_gen.go from wire.go injector - Move all constructor wiring out of main.go into providers.go / wire.go - Export recognition.IngredientRepository (was unexported, blocked Wire binding) - Delete units/cuisine/tag registry.go files (global state + unused NameFor helpers) - Replace List handlers with NewListHandler(pool) using COALESCE SQL queries - Remove units/cuisine/tag LoadFromDB from server startup; locale.LoadFromDB kept (locale.Supported is used by language middleware on every request) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
204
backend/cmd/server/providers.go
Normal file
204
backend/cmd/server/providers.go
Normal file
@@ -0,0 +1,204 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/food-ai/backend/internal/auth"
|
||||
"github.com/food-ai/backend/internal/config"
|
||||
"github.com/food-ai/backend/internal/diary"
|
||||
"github.com/food-ai/backend/internal/dish"
|
||||
"github.com/food-ai/backend/internal/gemini"
|
||||
"github.com/food-ai/backend/internal/home"
|
||||
"github.com/food-ai/backend/internal/ingredient"
|
||||
"github.com/food-ai/backend/internal/menu"
|
||||
"github.com/food-ai/backend/internal/middleware"
|
||||
"github.com/food-ai/backend/internal/pexels"
|
||||
"github.com/food-ai/backend/internal/product"
|
||||
"github.com/food-ai/backend/internal/recipe"
|
||||
"github.com/food-ai/backend/internal/recognition"
|
||||
"github.com/food-ai/backend/internal/recommendation"
|
||||
"github.com/food-ai/backend/internal/savedrecipe"
|
||||
"github.com/food-ai/backend/internal/cuisine"
|
||||
"github.com/food-ai/backend/internal/server"
|
||||
"github.com/food-ai/backend/internal/tag"
|
||||
"github.com/food-ai/backend/internal/units"
|
||||
"github.com/food-ai/backend/internal/user"
|
||||
"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 geminiAPIKey string
|
||||
type pexelsAPIKey string
|
||||
type jwtSecret string
|
||||
type jwtAccessDuration time.Duration
|
||||
type jwtRefreshDuration time.Duration
|
||||
type allowedOrigins []string
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Config extractors
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func newGeminiAPIKey(appConfig *config.Config) geminiAPIKey {
|
||||
return geminiAPIKey(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 newGeminiClient(key geminiAPIKey) *gemini.Client {
|
||||
return gemini.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,
|
||||
ingredientHandler *ingredient.Handler,
|
||||
productHandler *product.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,
|
||||
ingredientHandler,
|
||||
productHandler,
|
||||
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)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 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 = (*product.Repository)(nil)
|
||||
var _ menu.RecipeSaver = (*dish.Repository)(nil)
|
||||
var _ recommendation.PhotoSearcher = (*pexels.Client)(nil)
|
||||
var _ recommendation.UserLoader = (*user.Repository)(nil)
|
||||
var _ recommendation.ProductLister = (*product.Repository)(nil)
|
||||
var _ recognition.IngredientRepository = (*ingredient.Repository)(nil)
|
||||
var _ user.UserRepository = (*user.Repository)(nil)
|
||||
Reference in New Issue
Block a user