From 19a985ad49e5d3d865cc174685f67df596737f2a Mon Sep 17 00:00:00 2001 From: dbastrikin Date: Sun, 15 Mar 2026 21:10:37 +0200 Subject: [PATCH] refactor: restructure internal/ into adapters/, infra/, and app layers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - internal/gemini/ → internal/adapters/openai/ (renamed package to openai) - internal/pexels/ → internal/adapters/pexels/ - internal/config/ → internal/infra/config/ - internal/database/ → internal/infra/database/ - internal/locale/ → internal/infra/locale/ - internal/middleware/ → internal/infra/middleware/ - internal/server/ → internal/infra/server/ All import paths and call sites updated accordingly. Co-Authored-By: Claude Sonnet 4.6 --- backend/cmd/server/main.go | 6 ++-- backend/cmd/server/providers.go | 20 +++++------ backend/cmd/server/wire.go | 10 +++--- backend/cmd/server/wire_gen.go | 6 ++-- .../{gemini => adapters/openai}/client.go | 2 +- .../{gemini => adapters/openai}/menu.go | 2 +- .../{gemini => adapters/openai}/recipe.go | 4 +-- .../openai}/recognition.go | 2 +- .../internal/{ => adapters}/pexels/client.go | 0 backend/internal/auth/handler.go | 2 +- backend/internal/cuisine/handler.go | 2 +- backend/internal/diary/handler.go | 2 +- backend/internal/dish/repository.go | 2 +- backend/internal/home/handler.go | 2 +- backend/internal/{ => infra}/config/config.go | 0 .../internal/{ => infra}/database/postgres.go | 0 backend/internal/{ => infra}/locale/locale.go | 0 .../internal/{ => infra}/middleware/auth.go | 0 .../internal/{ => infra}/middleware/cors.go | 0 .../{ => infra}/middleware/language.go | 2 +- .../{ => infra}/middleware/logging.go | 0 .../{ => infra}/middleware/recovery.go | 0 .../{ => infra}/middleware/request_id.go | 0 backend/internal/{ => infra}/server/server.go | 2 +- backend/internal/ingredient/repository.go | 2 +- backend/internal/language/handler.go | 2 +- backend/internal/menu/handler.go | 20 +++++------ backend/internal/menu/repository.go | 2 +- backend/internal/product/handler.go | 2 +- backend/internal/recipe/handler.go | 2 +- backend/internal/recipe/repository.go | 2 +- backend/internal/recognition/handler.go | 36 +++++++++---------- backend/internal/recommendation/handler.go | 18 +++++----- backend/internal/savedrecipe/handler.go | 2 +- backend/internal/savedrecipe/repository.go | 2 +- backend/internal/tag/handler.go | 2 +- backend/internal/units/handler.go | 2 +- backend/internal/user/handler.go | 2 +- .../tests/auth/handler_integration_test.go | 2 +- .../ingredient/repository_integration_test.go | 2 +- backend/tests/locale/locale_test.go | 2 +- backend/tests/middleware/auth_test.go | 2 +- backend/tests/middleware/recovery_test.go | 2 +- backend/tests/middleware/request_id_test.go | 2 +- 44 files changed, 87 insertions(+), 87 deletions(-) rename backend/internal/{gemini => adapters/openai}/client.go (99%) rename backend/internal/{gemini => adapters/openai}/menu.go (99%) rename backend/internal/{gemini => adapters/openai}/recipe.go (98%) rename backend/internal/{gemini => adapters/openai}/recognition.go (99%) rename backend/internal/{ => adapters}/pexels/client.go (100%) rename backend/internal/{ => infra}/config/config.go (100%) rename backend/internal/{ => infra}/database/postgres.go (100%) rename backend/internal/{ => infra}/locale/locale.go (100%) rename backend/internal/{ => infra}/middleware/auth.go (100%) rename backend/internal/{ => infra}/middleware/cors.go (100%) rename backend/internal/{ => infra}/middleware/language.go (90%) rename backend/internal/{ => infra}/middleware/logging.go (100%) rename backend/internal/{ => infra}/middleware/recovery.go (100%) rename backend/internal/{ => infra}/middleware/request_id.go (100%) rename backend/internal/{ => infra}/server/server.go (98%) diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go index d3e407b..819af9a 100644 --- a/backend/cmd/server/main.go +++ b/backend/cmd/server/main.go @@ -10,9 +10,9 @@ import ( "syscall" "time" - "github.com/food-ai/backend/internal/config" - "github.com/food-ai/backend/internal/database" - "github.com/food-ai/backend/internal/locale" + "github.com/food-ai/backend/internal/infra/config" + "github.com/food-ai/backend/internal/infra/database" + "github.com/food-ai/backend/internal/infra/locale" ) func main() { diff --git a/backend/cmd/server/providers.go b/backend/cmd/server/providers.go index 8b8b7b9..0d2c4b3 100644 --- a/backend/cmd/server/providers.go +++ b/backend/cmd/server/providers.go @@ -5,22 +5,22 @@ import ( "time" "github.com/food-ai/backend/internal/auth" - "github.com/food-ai/backend/internal/config" + "github.com/food-ai/backend/internal/infra/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/adapters/openai" "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/infra/middleware" + "github.com/food-ai/backend/internal/adapters/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/infra/server" "github.com/food-ai/backend/internal/tag" "github.com/food-ai/backend/internal/units" "github.com/food-ai/backend/internal/user" @@ -38,7 +38,7 @@ type tagListHandler http.HandlerFunc // --------------------------------------------------------------------------- -type geminiAPIKey string +type openaiAPIKey string type pexelsAPIKey string type jwtSecret string type jwtAccessDuration time.Duration @@ -49,8 +49,8 @@ type allowedOrigins []string // Config extractors // --------------------------------------------------------------------------- -func newGeminiAPIKey(appConfig *config.Config) geminiAPIKey { - return geminiAPIKey(appConfig.OpenAIAPIKey) +func newOpenAIAPIKey(appConfig *config.Config) openaiAPIKey { + return openaiAPIKey(appConfig.OpenAIAPIKey) } func newPexelsAPIKey(appConfig *config.Config) pexelsAPIKey { @@ -83,8 +83,8 @@ func newFirebaseCredentialsFile(appConfig *config.Config) string { // Constructor wrappers for functions that accept primitive newtypes // --------------------------------------------------------------------------- -func newGeminiClient(key geminiAPIKey) *gemini.Client { - return gemini.NewClient(string(key)) +func newOpenAIClient(key openaiAPIKey) *openai.Client { + return openai.NewClient(string(key)) } func newPexelsClient(key pexelsAPIKey) *pexels.Client { diff --git a/backend/cmd/server/wire.go b/backend/cmd/server/wire.go index 5b91933..b823562 100644 --- a/backend/cmd/server/wire.go +++ b/backend/cmd/server/wire.go @@ -6,14 +6,14 @@ import ( "net/http" "github.com/food-ai/backend/internal/auth" - "github.com/food-ai/backend/internal/config" + "github.com/food-ai/backend/internal/infra/config" "github.com/food-ai/backend/internal/diary" "github.com/food-ai/backend/internal/dish" "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/infra/middleware" + "github.com/food-ai/backend/internal/adapters/pexels" "github.com/food-ai/backend/internal/product" "github.com/food-ai/backend/internal/recipe" "github.com/food-ai/backend/internal/recognition" @@ -27,7 +27,7 @@ import ( func initRouter(appConfig *config.Config, pool *pgxpool.Pool) (http.Handler, error) { wire.Build( // Config extractors - newGeminiAPIKey, + newOpenAIAPIKey, newPexelsAPIKey, newJWTSecret, newJWTAccessDuration, @@ -49,7 +49,7 @@ func initRouter(appConfig *config.Config, pool *pgxpool.Pool) (http.Handler, err user.NewHandler, // External clients - newGeminiClient, + newOpenAIClient, newPexelsClient, // Ingredient diff --git a/backend/cmd/server/wire_gen.go b/backend/cmd/server/wire_gen.go index 01b9fb3..2d0eb6e 100644 --- a/backend/cmd/server/wire_gen.go +++ b/backend/cmd/server/wire_gen.go @@ -8,7 +8,7 @@ package main import ( "github.com/food-ai/backend/internal/auth" - "github.com/food-ai/backend/internal/config" + "github.com/food-ai/backend/internal/infra/config" "github.com/food-ai/backend/internal/diary" "github.com/food-ai/backend/internal/dish" "github.com/food-ai/backend/internal/home" @@ -41,8 +41,8 @@ func initRouter(appConfig *config.Config, pool *pgxpool.Pool) (http.Handler, err handler := auth.NewHandler(service) userService := user.NewService(repository) userHandler := user.NewHandler(userService) - mainGeminiAPIKey := newGeminiAPIKey(appConfig) - client := newGeminiClient(mainGeminiAPIKey) + mainGeminiAPIKey := newOpenAIAPIKey(appConfig) + client := newOpenAIClient(mainGeminiAPIKey) mainPexelsAPIKey := newPexelsAPIKey(appConfig) pexelsClient := newPexelsClient(mainPexelsAPIKey) productRepository := product.NewRepository(pool) diff --git a/backend/internal/gemini/client.go b/backend/internal/adapters/openai/client.go similarity index 99% rename from backend/internal/gemini/client.go rename to backend/internal/adapters/openai/client.go index f89d5db..1b0147e 100644 --- a/backend/internal/gemini/client.go +++ b/backend/internal/adapters/openai/client.go @@ -1,4 +1,4 @@ -package gemini +package openai import ( "bytes" diff --git a/backend/internal/gemini/menu.go b/backend/internal/adapters/openai/menu.go similarity index 99% rename from backend/internal/gemini/menu.go rename to backend/internal/adapters/openai/menu.go index ac7c2b7..6bb6629 100644 --- a/backend/internal/gemini/menu.go +++ b/backend/internal/adapters/openai/menu.go @@ -1,4 +1,4 @@ -package gemini +package openai import ( "context" diff --git a/backend/internal/gemini/recipe.go b/backend/internal/adapters/openai/recipe.go similarity index 98% rename from backend/internal/gemini/recipe.go rename to backend/internal/adapters/openai/recipe.go index 8a6e299..53e478b 100644 --- a/backend/internal/gemini/recipe.go +++ b/backend/internal/adapters/openai/recipe.go @@ -1,4 +1,4 @@ -package gemini +package openai import ( "context" @@ -6,7 +6,7 @@ import ( "fmt" "strings" - "github.com/food-ai/backend/internal/locale" + "github.com/food-ai/backend/internal/infra/locale" ) // RecipeGenerator generates recipes using the Gemini AI. diff --git a/backend/internal/gemini/recognition.go b/backend/internal/adapters/openai/recognition.go similarity index 99% rename from backend/internal/gemini/recognition.go rename to backend/internal/adapters/openai/recognition.go index e8ba6f7..65c25ee 100644 --- a/backend/internal/gemini/recognition.go +++ b/backend/internal/adapters/openai/recognition.go @@ -1,4 +1,4 @@ -package gemini +package openai import ( "encoding/json" diff --git a/backend/internal/pexels/client.go b/backend/internal/adapters/pexels/client.go similarity index 100% rename from backend/internal/pexels/client.go rename to backend/internal/adapters/pexels/client.go diff --git a/backend/internal/auth/handler.go b/backend/internal/auth/handler.go index e8fadc2..42df8f4 100644 --- a/backend/internal/auth/handler.go +++ b/backend/internal/auth/handler.go @@ -5,7 +5,7 @@ import ( "log/slog" "net/http" - "github.com/food-ai/backend/internal/middleware" + "github.com/food-ai/backend/internal/infra/middleware" ) const maxRequestBodySize = 1 << 20 // 1 MB diff --git a/backend/internal/cuisine/handler.go b/backend/internal/cuisine/handler.go index 38a44e7..be10937 100644 --- a/backend/internal/cuisine/handler.go +++ b/backend/internal/cuisine/handler.go @@ -5,7 +5,7 @@ import ( "log/slog" "net/http" - "github.com/food-ai/backend/internal/locale" + "github.com/food-ai/backend/internal/infra/locale" "github.com/jackc/pgx/v5/pgxpool" ) diff --git a/backend/internal/diary/handler.go b/backend/internal/diary/handler.go index 8d8bf2f..f13dd1e 100644 --- a/backend/internal/diary/handler.go +++ b/backend/internal/diary/handler.go @@ -5,7 +5,7 @@ import ( "log/slog" "net/http" - "github.com/food-ai/backend/internal/middleware" + "github.com/food-ai/backend/internal/infra/middleware" "github.com/go-chi/chi/v5" ) diff --git a/backend/internal/dish/repository.go b/backend/internal/dish/repository.go index 834dfbb..3e24324 100644 --- a/backend/internal/dish/repository.go +++ b/backend/internal/dish/repository.go @@ -5,7 +5,7 @@ import ( "errors" "fmt" - "github.com/food-ai/backend/internal/locale" + "github.com/food-ai/backend/internal/infra/locale" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" ) diff --git a/backend/internal/home/handler.go b/backend/internal/home/handler.go index 2f92a46..a068cb8 100644 --- a/backend/internal/home/handler.go +++ b/backend/internal/home/handler.go @@ -8,7 +8,7 @@ import ( "net/http" "time" - "github.com/food-ai/backend/internal/middleware" + "github.com/food-ai/backend/internal/infra/middleware" "github.com/jackc/pgx/v5/pgxpool" ) diff --git a/backend/internal/config/config.go b/backend/internal/infra/config/config.go similarity index 100% rename from backend/internal/config/config.go rename to backend/internal/infra/config/config.go diff --git a/backend/internal/database/postgres.go b/backend/internal/infra/database/postgres.go similarity index 100% rename from backend/internal/database/postgres.go rename to backend/internal/infra/database/postgres.go diff --git a/backend/internal/locale/locale.go b/backend/internal/infra/locale/locale.go similarity index 100% rename from backend/internal/locale/locale.go rename to backend/internal/infra/locale/locale.go diff --git a/backend/internal/middleware/auth.go b/backend/internal/infra/middleware/auth.go similarity index 100% rename from backend/internal/middleware/auth.go rename to backend/internal/infra/middleware/auth.go diff --git a/backend/internal/middleware/cors.go b/backend/internal/infra/middleware/cors.go similarity index 100% rename from backend/internal/middleware/cors.go rename to backend/internal/infra/middleware/cors.go diff --git a/backend/internal/middleware/language.go b/backend/internal/infra/middleware/language.go similarity index 90% rename from backend/internal/middleware/language.go rename to backend/internal/infra/middleware/language.go index 0f51228..d2bf206 100644 --- a/backend/internal/middleware/language.go +++ b/backend/internal/infra/middleware/language.go @@ -3,7 +3,7 @@ package middleware import ( "net/http" - "github.com/food-ai/backend/internal/locale" + "github.com/food-ai/backend/internal/infra/locale" ) // Language reads the Accept-Language request header, resolves the best diff --git a/backend/internal/middleware/logging.go b/backend/internal/infra/middleware/logging.go similarity index 100% rename from backend/internal/middleware/logging.go rename to backend/internal/infra/middleware/logging.go diff --git a/backend/internal/middleware/recovery.go b/backend/internal/infra/middleware/recovery.go similarity index 100% rename from backend/internal/middleware/recovery.go rename to backend/internal/infra/middleware/recovery.go diff --git a/backend/internal/middleware/request_id.go b/backend/internal/infra/middleware/request_id.go similarity index 100% rename from backend/internal/middleware/request_id.go rename to backend/internal/infra/middleware/request_id.go diff --git a/backend/internal/server/server.go b/backend/internal/infra/server/server.go similarity index 98% rename from backend/internal/server/server.go rename to backend/internal/infra/server/server.go index 1d815b5..6ab78b8 100644 --- a/backend/internal/server/server.go +++ b/backend/internal/infra/server/server.go @@ -11,7 +11,7 @@ import ( "github.com/food-ai/backend/internal/ingredient" "github.com/food-ai/backend/internal/language" "github.com/food-ai/backend/internal/menu" - "github.com/food-ai/backend/internal/middleware" + "github.com/food-ai/backend/internal/infra/middleware" "github.com/food-ai/backend/internal/recipe" "github.com/food-ai/backend/internal/product" "github.com/food-ai/backend/internal/recognition" diff --git a/backend/internal/ingredient/repository.go b/backend/internal/ingredient/repository.go index 10ab185..b73a60b 100644 --- a/backend/internal/ingredient/repository.go +++ b/backend/internal/ingredient/repository.go @@ -6,7 +6,7 @@ import ( "errors" "fmt" - "github.com/food-ai/backend/internal/locale" + "github.com/food-ai/backend/internal/infra/locale" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" ) diff --git a/backend/internal/language/handler.go b/backend/internal/language/handler.go index 02ffa09..d49d6ef 100644 --- a/backend/internal/language/handler.go +++ b/backend/internal/language/handler.go @@ -4,7 +4,7 @@ import ( "encoding/json" "net/http" - "github.com/food-ai/backend/internal/locale" + "github.com/food-ai/backend/internal/infra/locale" ) type languageItem struct { diff --git a/backend/internal/menu/handler.go b/backend/internal/menu/handler.go index 0957e40..4a94deb 100644 --- a/backend/internal/menu/handler.go +++ b/backend/internal/menu/handler.go @@ -11,9 +11,9 @@ import ( "time" "github.com/food-ai/backend/internal/dish" - "github.com/food-ai/backend/internal/gemini" - "github.com/food-ai/backend/internal/locale" - "github.com/food-ai/backend/internal/middleware" + "github.com/food-ai/backend/internal/adapters/openai" + "github.com/food-ai/backend/internal/infra/locale" + "github.com/food-ai/backend/internal/infra/middleware" "github.com/food-ai/backend/internal/user" "github.com/go-chi/chi/v5" ) @@ -41,7 +41,7 @@ type RecipeSaver interface { // Handler handles menu and shopping-list endpoints. type Handler struct { repo *Repository - gemini *gemini.Client + openaiClient *openai.Client pexels PhotoSearcher userLoader UserLoader productLister ProductLister @@ -51,7 +51,7 @@ type Handler struct { // NewHandler creates a new Handler. func NewHandler( repo *Repository, - geminiClient *gemini.Client, + openaiClient *openai.Client, pexels PhotoSearcher, userLoader UserLoader, productLister ProductLister, @@ -59,7 +59,7 @@ func NewHandler( ) *Handler { return &Handler{ repo: repo, - gemini: geminiClient, + openaiClient: openaiClient, pexels: pexels, userLoader: userLoader, productLister: productLister, @@ -137,7 +137,7 @@ func (h *Handler) GenerateMenu(w http.ResponseWriter, r *http.Request) { } // Generate 7-day plan via Gemini. - days, err := h.gemini.GenerateMenu(r.Context(), menuReq) + days, err := h.openaiClient.GenerateMenu(r.Context(), menuReq) if err != nil { slog.Error("generate menu", "user_id", userID, "err", err) writeError(w, http.StatusServiceUnavailable, "menu generation failed, please try again") @@ -449,8 +449,8 @@ type userPreferences struct { Restrictions []string `json:"restrictions"` } -func buildMenuRequest(u *user.User, lang string) gemini.MenuRequest { - req := gemini.MenuRequest{DailyCalories: 2000, Lang: lang} +func buildMenuRequest(u *user.User, lang string) openai.MenuRequest { + req := openai.MenuRequest{DailyCalories: 2000, Lang: lang} if u.Goal != nil { req.UserGoal = *u.Goal } @@ -468,7 +468,7 @@ func buildMenuRequest(u *user.User, lang string) gemini.MenuRequest { } // recipeToCreateRequest converts a Gemini recipe to a dish.CreateRequest. -func recipeToCreateRequest(r gemini.Recipe) dish.CreateRequest { +func recipeToCreateRequest(r openai.Recipe) dish.CreateRequest { cr := dish.CreateRequest{ Name: r.Title, Description: r.Description, diff --git a/backend/internal/menu/repository.go b/backend/internal/menu/repository.go index 00e6c77..b0bf23e 100644 --- a/backend/internal/menu/repository.go +++ b/backend/internal/menu/repository.go @@ -7,7 +7,7 @@ import ( "fmt" "time" - "github.com/food-ai/backend/internal/locale" + "github.com/food-ai/backend/internal/infra/locale" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" ) diff --git a/backend/internal/product/handler.go b/backend/internal/product/handler.go index 36b82fd..bb3061a 100644 --- a/backend/internal/product/handler.go +++ b/backend/internal/product/handler.go @@ -6,7 +6,7 @@ import ( "log/slog" "net/http" - "github.com/food-ai/backend/internal/middleware" + "github.com/food-ai/backend/internal/infra/middleware" "github.com/go-chi/chi/v5" ) diff --git a/backend/internal/recipe/handler.go b/backend/internal/recipe/handler.go index a3aba5f..ae15f55 100644 --- a/backend/internal/recipe/handler.go +++ b/backend/internal/recipe/handler.go @@ -5,7 +5,7 @@ import ( "log/slog" "net/http" - "github.com/food-ai/backend/internal/middleware" + "github.com/food-ai/backend/internal/infra/middleware" "github.com/go-chi/chi/v5" ) diff --git a/backend/internal/recipe/repository.go b/backend/internal/recipe/repository.go index 64a6665..717802c 100644 --- a/backend/internal/recipe/repository.go +++ b/backend/internal/recipe/repository.go @@ -5,7 +5,7 @@ import ( "errors" "fmt" - "github.com/food-ai/backend/internal/locale" + "github.com/food-ai/backend/internal/infra/locale" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" ) diff --git a/backend/internal/recognition/handler.go b/backend/internal/recognition/handler.go index 0b16f89..b3db114 100644 --- a/backend/internal/recognition/handler.go +++ b/backend/internal/recognition/handler.go @@ -8,9 +8,9 @@ import ( "strings" "sync" - "github.com/food-ai/backend/internal/gemini" + "github.com/food-ai/backend/internal/adapters/openai" + "github.com/food-ai/backend/internal/infra/middleware" "github.com/food-ai/backend/internal/ingredient" - "github.com/food-ai/backend/internal/middleware" ) // IngredientRepository is the subset of ingredient.Repository used by this handler. @@ -23,13 +23,13 @@ type IngredientRepository interface { // Handler handles POST /ai/* recognition endpoints. type Handler struct { - gemini *gemini.Client + openaiClient *openai.Client ingredientRepo IngredientRepository } // NewHandler creates a new Handler. -func NewHandler(geminiClient *gemini.Client, repo IngredientRepository) *Handler { - return &Handler{gemini: geminiClient, ingredientRepo: repo} +func NewHandler(openaiClient *openai.Client, repo IngredientRepository) *Handler { + return &Handler{openaiClient: openaiClient, ingredientRepo: repo} } // --------------------------------------------------------------------------- @@ -60,12 +60,12 @@ type EnrichedItem struct { // ReceiptResponse is the response for POST /ai/recognize-receipt. type ReceiptResponse struct { - Items []EnrichedItem `json:"items"` - Unrecognized []gemini.UnrecognizedItem `json:"unrecognized"` + Items []EnrichedItem `json:"items"` + Unrecognized []openai.UnrecognizedItem `json:"unrecognized"` } // DishResponse is the response for POST /ai/recognize-dish. -type DishResponse = gemini.DishResult +type DishResponse = openai.DishResult // --------------------------------------------------------------------------- // Handlers @@ -83,7 +83,7 @@ func (h *Handler) RecognizeReceipt(w http.ResponseWriter, r *http.Request) { return } - result, err := h.gemini.RecognizeReceipt(r.Context(), req.ImageBase64, req.MimeType) + result, err := h.openaiClient.RecognizeReceipt(r.Context(), req.ImageBase64, req.MimeType) if err != nil { slog.Error("recognize receipt", "err", err) writeErrorJSON(w, http.StatusServiceUnavailable, "recognition failed, please try again") @@ -110,13 +110,13 @@ func (h *Handler) RecognizeProducts(w http.ResponseWriter, r *http.Request) { } // Process each image in parallel. - allItems := make([][]gemini.RecognizedItem, len(req.Images)) + allItems := make([][]openai.RecognizedItem, len(req.Images)) var wg sync.WaitGroup for i, img := range req.Images { wg.Add(1) go func(i int, img imageRequest) { defer wg.Done() - items, err := h.gemini.RecognizeProducts(r.Context(), img.ImageBase64, img.MimeType) + items, err := h.openaiClient.RecognizeProducts(r.Context(), img.ImageBase64, img.MimeType) if err != nil { slog.Warn("recognize products from image", "index", i, "err", err) return @@ -140,7 +140,7 @@ func (h *Handler) RecognizeDish(w http.ResponseWriter, r *http.Request) { return } - result, err := h.gemini.RecognizeDish(r.Context(), req.ImageBase64, req.MimeType) + result, err := h.openaiClient.RecognizeDish(r.Context(), req.ImageBase64, req.MimeType) if err != nil { slog.Error("recognize dish", "err", err) writeErrorJSON(w, http.StatusServiceUnavailable, "recognition failed, please try again") @@ -156,7 +156,7 @@ func (h *Handler) RecognizeDish(w http.ResponseWriter, r *http.Request) { // enrichItems matches each recognized item against ingredient_mappings. // Items without a match trigger a Gemini classification call and upsert into the DB. -func (h *Handler) enrichItems(ctx context.Context, items []gemini.RecognizedItem) []EnrichedItem { +func (h *Handler) enrichItems(ctx context.Context, items []openai.RecognizedItem) []EnrichedItem { result := make([]EnrichedItem, 0, len(items)) for _, item := range items { enriched := EnrichedItem{ @@ -188,7 +188,7 @@ func (h *Handler) enrichItems(ctx context.Context, items []gemini.RecognizedItem } } else { // No mapping — ask AI to classify and save for future reuse. - classification, err := h.gemini.ClassifyIngredient(ctx, item.Name) + classification, err := h.openaiClient.ClassifyIngredient(ctx, item.Name) if err != nil { slog.Warn("classify unknown ingredient", "name", item.Name, "err", err) } else { @@ -208,7 +208,7 @@ func (h *Handler) enrichItems(ctx context.Context, items []gemini.RecognizedItem } // saveClassification upserts an AI-produced ingredient classification into the DB. -func (h *Handler) saveClassification(ctx context.Context, c *gemini.IngredientClassification) *ingredient.IngredientMapping { +func (h *Handler) saveClassification(ctx context.Context, c *openai.IngredientClassification) *ingredient.IngredientMapping { if c == nil || c.CanonicalName == "" { return nil } @@ -252,8 +252,8 @@ func (h *Handler) saveClassification(ctx context.Context, c *gemini.IngredientCl // mergeAndDeduplicate combines results from multiple images. // Items sharing the same name (case-insensitive) have their quantities summed. -func mergeAndDeduplicate(batches [][]gemini.RecognizedItem) []gemini.RecognizedItem { - seen := make(map[string]*gemini.RecognizedItem) +func mergeAndDeduplicate(batches [][]openai.RecognizedItem) []openai.RecognizedItem { + seen := make(map[string]*openai.RecognizedItem) var order []string for _, batch := range batches { @@ -273,7 +273,7 @@ func mergeAndDeduplicate(batches [][]gemini.RecognizedItem) []gemini.RecognizedI } } - result := make([]gemini.RecognizedItem, 0, len(order)) + result := make([]openai.RecognizedItem, 0, len(order)) for _, key := range order { result = append(result, *seen[key]) } diff --git a/backend/internal/recommendation/handler.go b/backend/internal/recommendation/handler.go index 816307d..7c7ae63 100644 --- a/backend/internal/recommendation/handler.go +++ b/backend/internal/recommendation/handler.go @@ -8,9 +8,9 @@ import ( "strconv" "sync" - "github.com/food-ai/backend/internal/gemini" - "github.com/food-ai/backend/internal/locale" - "github.com/food-ai/backend/internal/middleware" + "github.com/food-ai/backend/internal/adapters/openai" + "github.com/food-ai/backend/internal/infra/locale" + "github.com/food-ai/backend/internal/infra/middleware" "github.com/food-ai/backend/internal/user" ) @@ -37,16 +37,16 @@ type userPreferences struct { // Handler handles GET /recommendations. type Handler struct { - gemini *gemini.Client + openaiClient *openai.Client pexels PhotoSearcher userLoader UserLoader productLister ProductLister } // NewHandler creates a new Handler. -func NewHandler(geminiClient *gemini.Client, pexels PhotoSearcher, userLoader UserLoader, productLister ProductLister) *Handler { +func NewHandler(openaiClient *openai.Client, pexels PhotoSearcher, userLoader UserLoader, productLister ProductLister) *Handler { return &Handler{ - gemini: geminiClient, + openaiClient: openaiClient, pexels: pexels, userLoader: userLoader, productLister: productLister, @@ -84,7 +84,7 @@ func (h *Handler) GetRecommendations(w http.ResponseWriter, r *http.Request) { slog.Warn("load products for recommendations", "user_id", userID, "err", err) } - recipes, err := h.gemini.GenerateRecipes(r.Context(), req) + recipes, err := h.openaiClient.GenerateRecipes(r.Context(), req) if err != nil { slog.Error("generate recipes", "user_id", userID, "err", err) writeErrorJSON(w, http.StatusServiceUnavailable, "recipe generation failed, please try again") @@ -109,8 +109,8 @@ func (h *Handler) GetRecommendations(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusOK, recipes) } -func buildRecipeRequest(u *user.User, count int, lang string) gemini.RecipeRequest { - req := gemini.RecipeRequest{ +func buildRecipeRequest(u *user.User, count int, lang string) openai.RecipeRequest { + req := openai.RecipeRequest{ Count: count, DailyCalories: 2000, // sensible default Lang: lang, diff --git a/backend/internal/savedrecipe/handler.go b/backend/internal/savedrecipe/handler.go index 05d47b4..e6f9731 100644 --- a/backend/internal/savedrecipe/handler.go +++ b/backend/internal/savedrecipe/handler.go @@ -6,7 +6,7 @@ import ( "log/slog" "net/http" - "github.com/food-ai/backend/internal/middleware" + "github.com/food-ai/backend/internal/infra/middleware" "github.com/go-chi/chi/v5" ) diff --git a/backend/internal/savedrecipe/repository.go b/backend/internal/savedrecipe/repository.go index ffa0f82..f99544f 100644 --- a/backend/internal/savedrecipe/repository.go +++ b/backend/internal/savedrecipe/repository.go @@ -7,7 +7,7 @@ import ( "fmt" "github.com/food-ai/backend/internal/dish" - "github.com/food-ai/backend/internal/locale" + "github.com/food-ai/backend/internal/infra/locale" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" ) diff --git a/backend/internal/tag/handler.go b/backend/internal/tag/handler.go index d5c6c2b..46e0084 100644 --- a/backend/internal/tag/handler.go +++ b/backend/internal/tag/handler.go @@ -5,7 +5,7 @@ import ( "log/slog" "net/http" - "github.com/food-ai/backend/internal/locale" + "github.com/food-ai/backend/internal/infra/locale" "github.com/jackc/pgx/v5/pgxpool" ) diff --git a/backend/internal/units/handler.go b/backend/internal/units/handler.go index 5f3795e..e6fef45 100644 --- a/backend/internal/units/handler.go +++ b/backend/internal/units/handler.go @@ -5,7 +5,7 @@ import ( "log/slog" "net/http" - "github.com/food-ai/backend/internal/locale" + "github.com/food-ai/backend/internal/infra/locale" "github.com/jackc/pgx/v5/pgxpool" ) diff --git a/backend/internal/user/handler.go b/backend/internal/user/handler.go index 4a3d12a..9ef0a16 100644 --- a/backend/internal/user/handler.go +++ b/backend/internal/user/handler.go @@ -5,7 +5,7 @@ import ( "log/slog" "net/http" - "github.com/food-ai/backend/internal/middleware" + "github.com/food-ai/backend/internal/infra/middleware" ) const maxRequestBodySize = 1 << 20 // 1 MB diff --git a/backend/tests/auth/handler_integration_test.go b/backend/tests/auth/handler_integration_test.go index 1b623e8..45783ed 100644 --- a/backend/tests/auth/handler_integration_test.go +++ b/backend/tests/auth/handler_integration_test.go @@ -13,7 +13,7 @@ import ( "github.com/food-ai/backend/internal/auth" "github.com/food-ai/backend/internal/auth/mocks" - "github.com/food-ai/backend/internal/middleware" + "github.com/food-ai/backend/internal/infra/middleware" "github.com/food-ai/backend/internal/testutil" "github.com/food-ai/backend/internal/user" "github.com/go-chi/chi/v5" diff --git a/backend/tests/ingredient/repository_integration_test.go b/backend/tests/ingredient/repository_integration_test.go index 6cc89c3..3d84228 100644 --- a/backend/tests/ingredient/repository_integration_test.go +++ b/backend/tests/ingredient/repository_integration_test.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/food-ai/backend/internal/ingredient" - "github.com/food-ai/backend/internal/locale" + "github.com/food-ai/backend/internal/infra/locale" "github.com/food-ai/backend/internal/testutil" ) diff --git a/backend/tests/locale/locale_test.go b/backend/tests/locale/locale_test.go index 9012b30..4c32572 100644 --- a/backend/tests/locale/locale_test.go +++ b/backend/tests/locale/locale_test.go @@ -5,7 +5,7 @@ import ( "net/http" "testing" - "github.com/food-ai/backend/internal/locale" + "github.com/food-ai/backend/internal/infra/locale" ) func TestParse(t *testing.T) { diff --git a/backend/tests/middleware/auth_test.go b/backend/tests/middleware/auth_test.go index 8c8d674..6c79b6f 100644 --- a/backend/tests/middleware/auth_test.go +++ b/backend/tests/middleware/auth_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/food-ai/backend/internal/middleware" + "github.com/food-ai/backend/internal/infra/middleware" "github.com/golang-jwt/jwt/v5" ) diff --git a/backend/tests/middleware/recovery_test.go b/backend/tests/middleware/recovery_test.go index fcf8bdd..aaa9bc4 100644 --- a/backend/tests/middleware/recovery_test.go +++ b/backend/tests/middleware/recovery_test.go @@ -5,7 +5,7 @@ import ( "net/http/httptest" "testing" - "github.com/food-ai/backend/internal/middleware" + "github.com/food-ai/backend/internal/infra/middleware" ) func TestRecovery_NoPanic(t *testing.T) { diff --git a/backend/tests/middleware/request_id_test.go b/backend/tests/middleware/request_id_test.go index b36923b..0aa6646 100644 --- a/backend/tests/middleware/request_id_test.go +++ b/backend/tests/middleware/request_id_test.go @@ -5,7 +5,7 @@ import ( "net/http/httptest" "testing" - "github.com/food-ai/backend/internal/middleware" + "github.com/food-ai/backend/internal/infra/middleware" ) func TestRequestID_GeneratesNew(t *testing.T) {