package main import ( "context" "fmt" "log" "os" "strings" "time" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/cors" "github.com/gofiber/fiber/v2/middleware/logger" "github.com/gooseek/backend/internal/search" "github.com/gooseek/backend/pkg/config" ) func main() { cfg, err := config.Load() if err != nil { log.Fatal("Failed to load config:", err) } searchClient := search.NewSearXNGClient(cfg) app := fiber.New(fiber.Config{ ReadTimeout: 30 * time.Second, WriteTimeout: 30 * time.Second, IdleTimeout: 60 * time.Second, }) app.Use(logger.New()) app.Use(cors.New()) app.Get("/health", func(c *fiber.Ctx) error { return c.JSON(fiber.Map{"status": "ok"}) }) app.Get("/api/v1/search", func(c *fiber.Ctx) error { query := c.Query("q") if query == "" { return c.Status(400).JSON(fiber.Map{"error": "Query parameter 'q' is required"}) } opts := &search.SearchOptions{} if engines := c.Query("engines"); engines != "" { opts.Engines = strings.Split(engines, ",") } if categories := c.Query("categories"); categories != "" { opts.Categories = strings.Split(categories, ",") } if pageno := c.QueryInt("pageno", 1); pageno > 0 { opts.PageNo = pageno } if lang := c.Query("language"); lang != "" { opts.Language = lang } ctx, cancel := context.WithTimeout(context.Background(), cfg.SearchTimeout) defer cancel() result, err := searchClient.Search(ctx, query, opts) if err != nil { return c.Status(500).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(result) }) app.Get("/api/v1/search/images", func(c *fiber.Ctx) error { query := c.Query("q") if query == "" { return c.Status(400).JSON(fiber.Map{"error": "Query parameter 'q' is required"}) } ctx, cancel := context.WithTimeout(context.Background(), cfg.SearchTimeout) defer cancel() result, err := searchClient.Search(ctx, query, &search.SearchOptions{ Categories: []string{"images"}, PageNo: 1, }) if err != nil { return c.Status(500).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(result) }) app.Get("/api/v1/search/videos", func(c *fiber.Ctx) error { query := c.Query("q") if query == "" { return c.Status(400).JSON(fiber.Map{"error": "Query parameter 'q' is required"}) } ctx, cancel := context.WithTimeout(context.Background(), cfg.SearchTimeout) defer cancel() result, err := searchClient.Search(ctx, query, &search.SearchOptions{ Categories: []string{"videos"}, PageNo: 1, }) if err != nil { return c.Status(500).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(result) }) app.Get("/api/v1/search/media", func(c *fiber.Ctx) error { query := c.Query("q") if query == "" { return c.Status(400).JSON(fiber.Map{"error": "Query parameter 'q' is required"}) } maxImages := c.QueryInt("maxImages", 8) maxVideos := c.QueryInt("maxVideos", 6) ctx, cancel := context.WithTimeout(context.Background(), cfg.SearchTimeout*2) defer cancel() result, err := searchClient.SearchMedia(ctx, query, &search.MediaSearchOptions{ MaxImages: maxImages, MaxVideos: maxVideos, }) if err != nil { return c.Status(500).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(result) }) port := cfg.SearchSvcPort log.Printf("search-svc listening on :%d", port) log.Fatal(app.Listen(fmt.Sprintf(":%d", port))) } func init() { if os.Getenv("PORT") == "" { os.Setenv("PORT", "3001") } }