iloom/gateway/cmd/main.go
Chever John 24c449ecae
security: remove hardcoded credentials and add WebSocket auth/CORS
- Clear default passwords in all service configs, require env vars
- Add JWT auth + role middleware to WebSocket endpoints
- Add origin whitelist to WebSocket upgrader (CORS protection)
- Fix goroutine leak in WS proxy (double errCh read)
- Update docker-compose to require secrets via ${VAR:?...} syntax
- Mark deprecated k8s configmap passwords as REPLACE_AT_DEPLOY_TIME

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-17 23:16:17 +08:00

84 lines
2.4 KiB
Go

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 (no auth required)
v1.Any("/auth/*path", 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))
}
// 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)
}
}