diff --git a/internal/handler/floor_handler.go b/internal/handler/floor_handler.go index 42b5135..9ada160 100644 --- a/internal/handler/floor_handler.go +++ b/internal/handler/floor_handler.go @@ -256,7 +256,7 @@ func FloorStatus(w http.ResponseWriter, r *http.Request) { func FloorOptions(db *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - woodFloors, _ := loadFloorOptions(db, "Hardwood") + woodFloors, _ := loadFloorOptions(db, "Hardwood") // non-critical: returns empty on error woodFloors2, _ := loadFloorOptions(db, "Engineered Wood") tileFloors, _ := loadFloorOptions(db, "Wood-Look Tile") vinylFloors, _ := loadFloorOptions(db, "SPC/LVP") @@ -316,34 +316,6 @@ Style: photorealistic, soft natural matte finish, minimal reflections, no mirror // ── Content Check ───────────────────────────────────────── -func CheckContent(client *openrouter.Client) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodPost { - writeErr(w, 405, "Method not allowed") - return - } - if err := r.ParseMultipartForm(20 << 20); err != nil { - writeJSON(w, 400, map[string]string{"error": err.Error()}) - return - } - file, _, _ := r.FormFile("image") - defer file.Close() - os.MkdirAll("uploads", 0755) - tmpPath := filepath.Join("uploads", fmt.Sprintf("check_%d.png", time.Now().UnixNano())) - dst, _ := os.Create(tmpPath) - io.Copy(dst, file) - dst.Close() - ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second) - defer cancel() - passed, score := validateContent(client, ctx, tmpPath) - msg := fmt.Sprintf("✅ Indoor photo score: %d/10", score) - if !passed { - msg = fmt.Sprintf("Non-indoor photo score: %d/10", score) - } - writeJSON(w, 200, map[string]any{"passed": passed, "score": score, "message": msg}) - } -} - func validateContent(client *openrouter.Client, ctx context.Context, imagePath string) (bool, int) { imgData, err := os.ReadFile(imagePath) if err != nil { diff --git a/internal/handler/product_handler.go b/internal/handler/product_handler.go index fb38a68..25a4a42 100644 --- a/internal/handler/product_handler.go +++ b/internal/handler/product_handler.go @@ -79,12 +79,13 @@ func Products(db *sql.DB) http.HandlerFunc { } } // Attach variants (same style_name, different sizes) - if variants, _ := repository.GetVariantsByStyle(db, sku); len(variants) > 0 { + variants, _ := repository.GetVariantsByStyle(db, sku) + if len(variants) > 0 { product.Variants = variants } // Check favorite status and build specs userID := middleware.UserID(r.Context()) - favMap, _ := repository.GetFavoritedSKUs(db, userID, []string{sku}) + favMap, _ := repository.GetFavoritedSKUs(db, userID, []string{sku}) // ok to ignore err: non-critical product.IsFavorited = favMap[sku] // Build specs: main product + variants as size options allVariants := append([]model.Product{*product}, product.Variants...) @@ -139,7 +140,7 @@ func Products(db *sql.DB) http.HandlerFunc { skus[i] = p.SKU } userID := middleware.UserID(r.Context()) - favSKUs, _ := repository.GetFavoritedSKUs(db, userID, skus) + favSKUs, _ := repository.GetFavoritedSKUs(db, userID, skus) // ok to ignore err: non-critical specCounts := repository.GetSpecCounts(db, skus) items := make([]ProductItem, 0, len(prods)) diff --git a/internal/handler/recommend_handler.go b/internal/handler/recommend_handler.go index 07dc6f9..68728eb 100644 --- a/internal/handler/recommend_handler.go +++ b/internal/handler/recommend_handler.go @@ -2,8 +2,8 @@ package handler import ( "database/sql" - "fmt" "floorvisualizer/internal/logger" + "fmt" "net/http" "floorvisualizer/internal/model" diff --git a/internal/queue/worker.go b/internal/queue/worker.go index ca650d6..a36e21d 100644 --- a/internal/queue/worker.go +++ b/internal/queue/worker.go @@ -3,8 +3,8 @@ package queue import ( "context" "database/sql" - "fmt" "floorvisualizer/internal/logger" + "fmt" "os" "path/filepath" "time" @@ -77,7 +77,11 @@ func processJob(ctx context.Context, rdb *redis.Client, client *openrouter.Clien // Step 0: content check SetJobProgress(jobCtx, rdb, jobID, "check", "Checking content...") logger.Info("[%s] Content check", jobID) - if passed, _ := validateContent(client, jobCtx, inputPath); !passed { + passed, score := validateContent(client, jobCtx, inputPath) + if score == 0 { + logger.Warn("[%s] Content check failed (score=0)", jobID) + } + if !passed { SetJobError(jobCtx, rdb, jobID, "Content check failed: Please upload an indoor room photo") return } @@ -102,7 +106,7 @@ func processJob(ctx context.Context, rdb *redis.Client, client *openrouter.Clien logger.Info("[%s] Inpainting: %s + %s", jobID, floor.Name, pattern.Name) floorPrompt := handler.BuildFloorPromptPublic(*floor, *pattern, room) - result, err := client.GenerateImageToFile(jobCtx, openrouter.ImageGenerationRequest{ + _, err = client.GenerateImageToFile(jobCtx, openrouter.ImageGenerationRequest{ Prompt: floorPrompt, OutputPath: outputPath, InputImagePath: inputPath, MaskImagePath: maskPath, ImageSize: "2K", }) @@ -112,10 +116,13 @@ func processJob(ctx context.Context, rdb *redis.Client, client *openrouter.Clien } // Clean up intermediate files - os.Remove(inputPath) - os.Remove(maskPath) + if err := os.Remove(inputPath); err != nil { + logger.Warn("[%s] Failed to remove input: %v", jobID, err) + } + if err := os.Remove(maskPath); err != nil { + logger.Warn("[%s] Failed to remove mask: %v", jobID, err) + } - _ = result SetJobResult(jobCtx, rdb, jobID, "/"+filepath.ToSlash(outputPath)) logger.Info("[%s] Done", jobID) } @@ -180,4 +187,3 @@ func base64enc(data []byte) string { } return string(b) } -