// Command server starts the OpenGoods public read-only API. package main import ( "context" "log" "net/http" "time" "github.com/jackc/pgx/v5/pgxpool" "github.com/baicai2026-baicai/goods/api/internal/config" "github.com/baicai2026-baicai/goods/api/internal/handler" "github.com/baicai2026-baicai/goods/api/internal/publicweb" "github.com/baicai2026-baicai/goods/api/internal/ratelimit" "github.com/baicai2026-baicai/goods/api/internal/store" ) func main() { cfg := config.Load() ctx := context.Background() pool, err := pgxpool.New(ctx, cfg.DatabaseURL) if err != nil { log.Fatalf("failed to create db pool: %v", err) } defer pool.Close() pingCtx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() if err := pool.Ping(pingCtx); err != nil { log.Printf("warning: database not reachable at startup: %v", err) } limiter := ratelimit.New(cfg.RedisURL) if !limiter.Enabled() { log.Print("warning: Redis not configured; public API rate limiting disabled") } h := handler.New(store.New(pool), publicweb.Dist()). WithRateLimit(limiter, cfg.AnonRateLimitPerMin). WithQuotas(cfg.AnonTotalQuota, cfg.RegisteredRateLimitPerMin, cfg.RegisteredQuotaTotal) srv := &http.Server{ Addr: cfg.Addr, Handler: h.Router(), ReadHeaderTimeout: 10 * time.Second, } log.Printf("OpenGoods API listening on %s", cfg.Addr) if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("server error: %v", err) } }