diff --git a/.env.example b/.env.example index 4dc9e31..30250e3 100644 --- a/.env.example +++ b/.env.example @@ -1,18 +1,20 @@ # iloom 微服务环境变量配置 +# Copy this file to .env and fill in real values. NEVER commit .env to git. # MySQL (共享 muyu_wms 数据库) -DB_DSN=root:muyu2026@tcp(mysql:3306)/muyu_wms?parseTime=true&charset=utf8mb4 +MYSQL_ROOT_PASSWORD=your_mysql_password_here +DB_DSN=root:your_password@tcp(mysql:3306)/muyu_wms?parseTime=true&charset=utf8mb4 # JWT (复用 muyu 认证) -JWT_SECRET=muyu-wms-jwt-secret-key-2026 -JWT_REFRESH_SECRET=muyu-wms-jwt-refresh-2026 +JWT_SECRET=your_jwt_secret_here +JWT_REFRESH_SECRET=your_jwt_refresh_secret_here # muyu gRPC 地址 MUYU_SYSTEM_RPC_ADDR=system-rpc:9001 MUYU_INVENTORY_RPC_ADDR=inventory-rpc:9002 # 服务间调用密钥 -INTERNAL_SERVICE_KEY=internal-service-shared-secret +INTERNAL_SERVICE_KEY=your_internal_service_key_here # 服务端口 GATEWAY_PORT=8080 diff --git a/auth-service/internal/config/config.go b/auth-service/internal/config/config.go index 2c4ec80..ab67feb 100644 --- a/auth-service/internal/config/config.go +++ b/auth-service/internal/config/config.go @@ -13,9 +13,9 @@ type Config struct { func Load() *Config { return &Config{ Port: getEnv("PORT", "8081"), - DBDSN: getEnv("DB_DSN", "root:muyu2026@tcp(127.0.0.1:3306)/muyu_wms?parseTime=true&charset=utf8mb4"), - JWTSecret: getEnv("JWT_SECRET", "muyu-wms-jwt-secret-key-2026"), - JWTRefreshSecret: getEnv("JWT_REFRESH_SECRET", "muyu-wms-jwt-refresh-2026"), + DBDSN: getEnv("DB_DSN", ""), + JWTSecret: getEnv("JWT_SECRET", ""), + JWTRefreshSecret: getEnv("JWT_REFRESH_SECRET", ""), SystemRPCAddr: getEnv("MUYU_SYSTEM_RPC_ADDR", "127.0.0.1:9001"), } } diff --git a/docker-compose.integrated.yml b/docker-compose.integrated.yml index f013087..ea66d00 100644 --- a/docker-compose.integrated.yml +++ b/docker-compose.integrated.yml @@ -10,7 +10,7 @@ services: - "${GATEWAY_PORT:-8080}:8080" environment: - PORT=8080 - - JWT_SECRET=${JWT_SECRET:-muyu-wms-jwt-secret-key-2026} + - JWT_SECRET=${JWT_SECRET:?Set JWT_SECRET in .env} - AUTH_SERVICE_URL=http://auth-service:8081 - PURCHASER_SERVICE_URL=http://purchaser-service:8082 - TEXTILE_SERVICE_URL=http://textile-service:8083 @@ -37,9 +37,9 @@ services: - "${AUTH_SERVICE_PORT:-8081}:8081" environment: - PORT=8081 - - DB_DSN=root:muyu2026@tcp(muyu-mysql:3306)/muyu_wms?parseTime=true&charset=utf8mb4 - - JWT_SECRET=${JWT_SECRET:-muyu-wms-jwt-secret-key-2026} - - JWT_REFRESH_SECRET=${JWT_REFRESH_SECRET:-muyu-wms-jwt-refresh-2026} + - DB_DSN=${DB_DSN:?Set DB_DSN in .env} + - JWT_SECRET=${JWT_SECRET:?Set JWT_SECRET in .env} + - JWT_REFRESH_SECRET=${JWT_REFRESH_SECRET:?Set JWT_REFRESH_SECRET in .env} - MUYU_SYSTEM_RPC_ADDR=muyu-system-rpc:9001 healthcheck: test: ["CMD", "wget", "-qO-", "http://localhost:8081/health"] @@ -58,8 +58,8 @@ services: - "${PURCHASER_SERVICE_PORT:-8082}:8082" environment: - PORT=8082 - - DB_DSN=root:muyu2026@tcp(muyu-mysql:3306)/muyu_wms?parseTime=true&charset=utf8mb4 - - INTERNAL_SERVICE_KEY=${INTERNAL_SERVICE_KEY:-internal-service-shared-secret} + - DB_DSN=${DB_DSN:?Set DB_DSN in .env} + - INTERNAL_SERVICE_KEY=${INTERNAL_SERVICE_KEY:?Set INTERNAL_SERVICE_KEY in .env} - MUYU_INVENTORY_RPC_ADDR=muyu-inventory-rpc:9002 healthcheck: test: ["CMD", "wget", "-qO-", "http://localhost:8082/health"] @@ -78,8 +78,8 @@ services: - "${TEXTILE_SERVICE_PORT:-8083}:8083" environment: - PORT=8083 - - DB_DSN=root:muyu2026@tcp(muyu-mysql:3306)/muyu_wms?parseTime=true&charset=utf8mb4 - - INTERNAL_SERVICE_KEY=${INTERNAL_SERVICE_KEY:-internal-service-shared-secret} + - DB_DSN=${DB_DSN:?Set DB_DSN in .env} + - INTERNAL_SERVICE_KEY=${INTERNAL_SERVICE_KEY:?Set INTERNAL_SERVICE_KEY in .env} - PURCHASER_SERVICE_URL=http://purchaser-service:8082 healthcheck: test: ["CMD", "wget", "-qO-", "http://localhost:8083/health"] @@ -98,8 +98,8 @@ services: - "${WASHING_SERVICE_PORT:-8084}:8084" environment: - PORT=8084 - - DB_DSN=root:muyu2026@tcp(muyu-mysql:3306)/muyu_wms?parseTime=true&charset=utf8mb4 - - INTERNAL_SERVICE_KEY=${INTERNAL_SERVICE_KEY:-internal-service-shared-secret} + - DB_DSN=${DB_DSN:?Set DB_DSN in .env} + - INTERNAL_SERVICE_KEY=${INTERNAL_SERVICE_KEY:?Set INTERNAL_SERVICE_KEY in .env} - PURCHASER_SERVICE_URL=http://purchaser-service:8082 healthcheck: test: ["CMD", "wget", "-qO-", "http://localhost:8084/health"] diff --git a/docker-compose.yml b/docker-compose.yml index 931bd3c..5f18425 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,7 @@ # 集成模式(与 muyu-apiserver 联合): # 1. cd ../muyu-apiserver/deploy && docker compose up # 2. 设置环境变量指向 muyu 的服务: -# export DB_DSN="root:muyu2026@tcp(muyu-mysql:3306)/muyu_wms?parseTime=true&charset=utf8mb4" +# export DB_DSN="root:yourpassword@tcp(muyu-mysql:3306)/muyu_wms?parseTime=true&charset=utf8mb4" # export MUYU_SYSTEM_RPC_ADDR=muyu-system-rpc:9001 # export MUYU_INVENTORY_RPC_ADDR=muyu-inventory-rpc:9002 # 3. docker compose --profile integrated up @@ -15,7 +15,7 @@ services: mysql: image: mysql:8.0 environment: - MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-muyu2026} + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:?Set MYSQL_ROOT_PASSWORD in .env} MYSQL_DATABASE: ${MYSQL_DATABASE:-muyu_wms} TZ: Asia/Shanghai ports: @@ -26,7 +26,7 @@ services: - ./scripts/init-iloom.sql:/docker-entrypoint-initdb.d/02-iloom.sql command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci healthcheck: - test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-p${MYSQL_ROOT_PASSWORD:-muyu2026}"] + test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-p${MYSQL_ROOT_PASSWORD}"] interval: 5s timeout: 5s retries: 10 @@ -39,7 +39,7 @@ services: - "${GATEWAY_PORT:-8080}:8080" environment: - PORT=8080 - - JWT_SECRET=${JWT_SECRET:-muyu-wms-jwt-secret-key-2026} + - JWT_SECRET=${JWT_SECRET:?Set JWT_SECRET in .env} - AUTH_SERVICE_URL=http://auth-service:8081 - PURCHASER_SERVICE_URL=http://purchaser-service:8082 - TEXTILE_SERVICE_URL=http://textile-service:8083 @@ -63,9 +63,9 @@ services: - "${AUTH_SERVICE_PORT:-8081}:8081" environment: - PORT=8081 - - DB_DSN=${DB_DSN:-root:muyu2026@tcp(mysql:3306)/muyu_wms?parseTime=true&charset=utf8mb4} - - JWT_SECRET=${JWT_SECRET:-muyu-wms-jwt-secret-key-2026} - - JWT_REFRESH_SECRET=${JWT_REFRESH_SECRET:-muyu-wms-jwt-refresh-2026} + - DB_DSN=${DB_DSN:?Set DB_DSN in .env} + - JWT_SECRET=${JWT_SECRET:?Set JWT_SECRET in .env} + - JWT_REFRESH_SECRET=${JWT_REFRESH_SECRET:?Set JWT_REFRESH_SECRET in .env} - MUYU_SYSTEM_RPC_ADDR=${MUYU_SYSTEM_RPC_ADDR:-system-rpc:9001} depends_on: mysql: @@ -84,8 +84,8 @@ services: - "${PURCHASER_SERVICE_PORT:-8082}:8082" environment: - PORT=8082 - - DB_DSN=${DB_DSN:-root:muyu2026@tcp(mysql:3306)/muyu_wms?parseTime=true&charset=utf8mb4} - - INTERNAL_SERVICE_KEY=${INTERNAL_SERVICE_KEY:-internal-service-shared-secret} + - DB_DSN=${DB_DSN:?Set DB_DSN in .env} + - INTERNAL_SERVICE_KEY=${INTERNAL_SERVICE_KEY:?Set INTERNAL_SERVICE_KEY in .env} - MUYU_INVENTORY_RPC_ADDR=${MUYU_INVENTORY_RPC_ADDR:-inventory-rpc:9002} depends_on: mysql: @@ -104,8 +104,8 @@ services: - "${TEXTILE_SERVICE_PORT:-8083}:8083" environment: - PORT=8083 - - DB_DSN=${DB_DSN:-root:muyu2026@tcp(mysql:3306)/muyu_wms?parseTime=true&charset=utf8mb4} - - INTERNAL_SERVICE_KEY=${INTERNAL_SERVICE_KEY:-internal-service-shared-secret} + - DB_DSN=${DB_DSN:?Set DB_DSN in .env} + - INTERNAL_SERVICE_KEY=${INTERNAL_SERVICE_KEY:?Set INTERNAL_SERVICE_KEY in .env} - PURCHASER_SERVICE_URL=http://purchaser-service:8082 depends_on: mysql: @@ -124,8 +124,8 @@ services: - "${WASHING_SERVICE_PORT:-8084}:8084" environment: - PORT=8084 - - DB_DSN=${DB_DSN:-root:muyu2026@tcp(mysql:3306)/muyu_wms?parseTime=true&charset=utf8mb4} - - INTERNAL_SERVICE_KEY=${INTERNAL_SERVICE_KEY:-internal-service-shared-secret} + - DB_DSN=${DB_DSN:?Set DB_DSN in .env} + - INTERNAL_SERVICE_KEY=${INTERNAL_SERVICE_KEY:?Set INTERNAL_SERVICE_KEY in .env} - PURCHASER_SERVICE_URL=http://purchaser-service:8082 depends_on: mysql: diff --git a/gateway/cmd/main.go b/gateway/cmd/main.go index 7e47cb1..9a0fc57 100644 --- a/gateway/cmd/main.go +++ b/gateway/cmd/main.go @@ -58,10 +58,22 @@ func main() { washing.Any("/*path", proxy.NewReverseProxy(cfg.WashingServiceURL)) } - // WebSocket proxy — route by role - r.GET("/ws/v1/purchaser", proxy.NewWSProxy(cfg.PurchaserServiceURL)) - r.GET("/ws/v1/textile", proxy.NewWSProxy(cfg.TextileServiceURL)) - r.GET("/ws/v1/washing", proxy.NewWSProxy(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) diff --git a/gateway/internal/config/config.go b/gateway/internal/config/config.go index 78a40ed..67035e3 100644 --- a/gateway/internal/config/config.go +++ b/gateway/internal/config/config.go @@ -18,7 +18,7 @@ type Config struct { func Load() *Config { return &Config{ Port: getEnv("PORT", "8080"), - JWTSecret: getEnv("JWT_SECRET", "muyu-wms-jwt-secret-key-2026"), + JWTSecret: getEnv("JWT_SECRET", ""), AuthServiceURL: getEnv("AUTH_SERVICE_URL", "http://localhost:8081"), PurchaserServiceURL: getEnv("PURCHASER_SERVICE_URL", "http://localhost:8082"), TextileServiceURL: getEnv("TEXTILE_SERVICE_URL", "http://localhost:8083"), diff --git a/gateway/internal/proxy/ws_proxy.go b/gateway/internal/proxy/ws_proxy.go index 8ff591a..c68c83f 100644 --- a/gateway/internal/proxy/ws_proxy.go +++ b/gateway/internal/proxy/ws_proxy.go @@ -11,11 +11,29 @@ import ( "github.com/gorilla/websocket" ) -var upgrader = websocket.Upgrader{ - CheckOrigin: func(r *http.Request) bool { return true }, +func newUpgrader(allowedOrigins []string) websocket.Upgrader { + originSet := make(map[string]bool, len(allowedOrigins)) + for _, o := range allowedOrigins { + originSet[strings.TrimSpace(o)] = true + } + + return websocket.Upgrader{ + CheckOrigin: func(r *http.Request) bool { + if originSet["*"] { + return true + } + origin := r.Header.Get("Origin") + if origin == "" { + return false + } + return originSet[origin] + }, + } } -func NewWSProxy(targetURL string) gin.HandlerFunc { +func NewWSProxy(targetURL string, allowedOrigins []string) gin.HandlerFunc { + upgrader := newUpgrader(allowedOrigins) + return func(c *gin.Context) { clientConn, err := upgrader.Upgrade(c.Writer, c.Request, nil) if err != nil { @@ -50,6 +68,7 @@ func NewWSProxy(targetURL string) gin.HandlerFunc { go pumpMessages(backendConn, clientConn, errCh) <-errCh + <-errCh } } diff --git a/k8s/02-configmaps/muyu-configs.yaml b/k8s/02-configmaps/muyu-configs.yaml index bbbd338..5348916 100644 --- a/k8s/02-configmaps/muyu-configs.yaml +++ b/k8s/02-configmaps/muyu-configs.yaml @@ -1,3 +1,7 @@ +## DEPRECATED: These raw K8s manifests are superseded by iloom-deploy/charts/. +## DataSource values must be injected via Kubernetes Secrets at deploy time. +## See iloom-deploy/charts/infra/templates/secrets.yaml for the Helm-based approach. +## apiVersion: v1 kind: ConfigMap metadata: @@ -13,7 +17,7 @@ data: - etcd:2379 Key: system.rpc - DataSource: root:muyu2026@tcp(mysql:3306)/muyu_wms?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai + DataSource: REPLACE_AT_DEPLOY_TIME Cache: - Host: redis:6379 @@ -38,7 +42,7 @@ data: - etcd:2379 Key: inventory.rpc - DataSource: root:muyu2026@tcp(mysql:3306)/muyu_wms?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai + DataSource: REPLACE_AT_DEPLOY_TIME Cache: - Host: redis:6379 @@ -60,7 +64,7 @@ data: Port: 8888 Auth: - AccessSecret: muyu-wms-jwt-secret-key-2026 + AccessSecret: REPLACE_AT_DEPLOY_TIME AccessExpire: 7200 SystemRpc: diff --git a/purchaser-service/internal/config/config.go b/purchaser-service/internal/config/config.go index 297e2e1..97bc574 100644 --- a/purchaser-service/internal/config/config.go +++ b/purchaser-service/internal/config/config.go @@ -12,7 +12,7 @@ type Config struct { func Load() *Config { return &Config{ Port: getEnv("PORT", "8082"), - DBDSN: getEnv("DB_DSN", "root:muyu2026@tcp(127.0.0.1:3306)/muyu_wms?parseTime=true&charset=utf8mb4"), + DBDSN: getEnv("DB_DSN", ""), InternalServiceKey: getEnv("INTERNAL_SERVICE_KEY", "dev-internal-key"), InventoryRPCAddr: getEnv("MUYU_INVENTORY_RPC_ADDR", "127.0.0.1:9002"), } diff --git a/textile-service/internal/config/config.go b/textile-service/internal/config/config.go index e17d0aa..9c52d52 100644 --- a/textile-service/internal/config/config.go +++ b/textile-service/internal/config/config.go @@ -12,7 +12,7 @@ type Config struct { func Load() *Config { return &Config{ Port: getEnv("PORT", "8083"), - DBDSN: getEnv("DB_DSN", "root:muyu2026@tcp(127.0.0.1:3306)/muyu_wms?parseTime=true&charset=utf8mb4"), + DBDSN: getEnv("DB_DSN", ""), InternalServiceKey: getEnv("INTERNAL_SERVICE_KEY", "dev-internal-key"), PurchaserServiceURL: getEnv("PURCHASER_SERVICE_URL", "http://localhost:8082"), } diff --git a/washing-service/internal/config/config.go b/washing-service/internal/config/config.go index 50bf3ab..a5d466c 100644 --- a/washing-service/internal/config/config.go +++ b/washing-service/internal/config/config.go @@ -12,7 +12,7 @@ type Config struct { func Load() *Config { return &Config{ Port: getEnv("PORT", "8084"), - DBDSN: getEnv("DB_DSN", "root:muyu2026@tcp(127.0.0.1:3306)/muyu_wms?parseTime=true&charset=utf8mb4"), + DBDSN: getEnv("DB_DSN", ""), InternalServiceKey: getEnv("INTERNAL_SERVICE_KEY", "dev-internal-key"), PurchaserServiceURL: getEnv("PURCHASER_SERVICE_URL", "http://localhost:8082"), }