package main import ( "log" "net/http" "github.com/gin-gonic/gin" "github.com/muyuqingfeng/iloom/gateway/internal/config" "github.com/muyuqingfeng/iloom/gateway/internal/middleware" "github.com/muyuqingfeng/iloom/gateway/internal/proxy" sharedMW "github.com/muyuqingfeng/iloom/shared/pkg/middleware" ) func main() { cfg := config.Load() r := gin.New() r.Use( sharedMW.RequestID(), sharedMW.Logger(), sharedMW.Recovery(), sharedMW.CORS(cfg.AllowOrigins), middleware.RateLimit(100), ) r.GET("/health", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"status": "ok"}) }) v1 := r.Group("/api/v1") { // /api/v1/auth/* -> AUTH_SERVICE_URL // Public auth endpoints (no JWT required) authPublic := v1.Group("/auth") authPublic.POST("/register", proxy.NewReverseProxy(cfg.AuthServiceURL)) authPublic.POST("/login", proxy.NewReverseProxy(cfg.AuthServiceURL)) authPublic.POST("/logout", proxy.NewReverseProxy(cfg.AuthServiceURL)) authPublic.POST("/refresh", proxy.NewReverseProxy(cfg.AuthServiceURL)) // Protected auth endpoints (JWT required to set X-User-ID) authProtected := v1.Group("/auth", middleware.AuthRequired(cfg.JWTSecret)) authProtected.GET("/me", proxy.NewReverseProxy(cfg.AuthServiceURL)) // /api/v1/common/* -> AUTH_SERVICE_URL (JWT required) common := v1.Group("/common", middleware.AuthRequired(cfg.JWTSecret)) common.Any("/*path", proxy.NewReverseProxy(cfg.AuthServiceURL)) // /api/v1/purchaser/* -> PURCHASER_SERVICE_URL (JWT + role=purchaser) purchaser := v1.Group("/purchaser", middleware.AuthRequired(cfg.JWTSecret), middleware.RoleRequired("purchaser"), ) purchaser.Any("/*path", proxy.NewReverseProxy(cfg.PurchaserServiceURL)) // /api/v1/textile/* -> TEXTILE_SERVICE_URL (JWT + role=textile) textile := v1.Group("/textile", middleware.AuthRequired(cfg.JWTSecret), middleware.RoleRequired("textile"), ) textile.Any("/*path", proxy.NewReverseProxy(cfg.TextileServiceURL)) // /api/v1/washing/* -> WASHING_SERVICE_URL (JWT + role=washing) washing := v1.Group("/washing", middleware.AuthRequired(cfg.JWTSecret), middleware.RoleRequired("washing"), ) washing.Any("/*path", proxy.NewReverseProxy(cfg.WashingServiceURL)) // /api/v1/muyu/* -> MUYU_GATEWAY_URL (muyu warehouse subsystem) // muyu-gateway handles its own authentication internally muyu := v1.Group("/muyu", middleware.AuthRequired(cfg.JWTSecret), ) muyu.Any("/*path", proxy.NewReverseProxy(cfg.MuyuGatewayURL)) } // WebSocket proxy — route by role (JWT + role check applied) r.GET("/ws/v1/purchaser", middleware.AuthRequired(cfg.JWTSecret), middleware.RoleRequired("purchaser"), proxy.NewWSProxy(cfg.PurchaserServiceURL, cfg.AllowOrigins), ) r.GET("/ws/v1/textile", middleware.AuthRequired(cfg.JWTSecret), middleware.RoleRequired("textile"), proxy.NewWSProxy(cfg.TextileServiceURL, cfg.AllowOrigins), ) r.GET("/ws/v1/washing", middleware.AuthRequired(cfg.JWTSecret), middleware.RoleRequired("washing"), proxy.NewWSProxy(cfg.WashingServiceURL, cfg.AllowOrigins), ) addr := ":" + cfg.Port log.Printf("gateway listening on %s", addr) if err := r.Run(addr); err != nil { log.Fatalf("server error: %v", err) } }