Files
Edu/services/api-gateway/internal/middleware/cors.go
SpecialX 0a71b02e04
Some checks failed
CI / quality-ts (push) Failing after 48s
CI / quality-go (push) Failing after 4s
CI / quality-proto (push) Failing after 2s
CI / deploy (push) Has been skipped
fix: code compliance audit and fix across all services
NestJS (6 services): implement @RequirePermission decorator with
SetMetadata+Reflector, register APP_GUARD globally, fix as assertions
to type guards, add explicit return types, fix import type for express,
fix /metrics implicit any, replace native Error with ApplicationError,
remove typeorm remnants, register LifecycleService.

teacher-bff: add logger, ApplicationError, GlobalErrorFilter, forward
real userId to downstream, log downstream failures, migrate health
controller to shared/health.

Go (2 services): interface to any, doc comments, CORS dev whitelist,
JWT secret fail-fast, push-gateway internal API auth, metrics and
readyz endpoints, remove dead code.

Python (2 services): lifespan return type, dev_mode to bool, data-ana
APIRouter, ai POST body model, ClickHouse async wrapping.
2026-07-09 17:28:27 +08:00

73 lines
2.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package middleware
import (
"log"
"net/http"
"os"
"strconv"
"strings"
"github.com/gin-gonic/gin"
)
// corsMaxAge 预检缓存时长12 小时
const corsMaxAge = 12 * 60 * 60
// devCORSOrigins 是未配置 CORS_ORIGINS 时的开发环境默认白名单
const devCORSOrigins = "http://localhost:3000,http://localhost:3001"
// CORS 返回跨域资源共享中间件。
// 允许来源从环境变量 CORS_ORIGINS 读取(逗号分隔);
// 未配置时使用开发环境白名单localhost:3000/3001并打印 warning。
// 允许方法GET POST PUT DELETE OPTIONS PATCH
// 允许头Authorization Content-Type X-Request-Id X-Trace-Id
// 暴露头X-Request-Id X-Trace-Id
func CORS() gin.HandlerFunc {
allowed := parseCORSOrigins(os.Getenv("CORS_ORIGINS"))
if len(allowed) == 0 {
log.Println("warning: CORS_ORIGINS not set, using dev default whitelist")
allowed = parseCORSOrigins(devCORSOrigins)
}
return func(c *gin.Context) {
origin := c.GetHeader("Origin")
allowOrigin := ""
if allowed[origin] {
allowOrigin = origin
}
if allowOrigin != "" {
h := c.Writer.Header()
h.Set("Access-Control-Allow-Origin", allowOrigin)
h.Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, PATCH")
h.Set("Access-Control-Allow-Headers", "Authorization, Content-Type, X-Request-Id, X-Trace-Id")
h.Set("Access-Control-Expose-Headers", "X-Request-Id, X-Trace-Id")
h.Set("Access-Control-Max-Age", strconv.Itoa(corsMaxAge))
// 非通配来源需标注 Vary便于缓存正确区分
h.Add("Vary", "Origin")
}
// 预检请求直接返回 204
if c.Request.Method == http.MethodOptions {
c.AbortWithStatus(http.StatusNoContent)
return
}
c.Next()
}
}
// parseCORSOrigins 解析逗号分隔的来源列表为集合,空字符串返回空 map
func parseCORSOrigins(raw string) map[string]bool {
allowed := map[string]bool{}
if raw == "" {
return allowed
}
for _, o := range strings.Split(raw, ",") {
o = strings.TrimSpace(o)
if o != "" {
allowed[o] = true
}
}
return allowed
}