chore: initial commit of ai site platform

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
whm
2026-07-31 10:31:17 +08:00
commit 4ca82fb58a
203 changed files with 45745 additions and 0 deletions

View File

@@ -0,0 +1,186 @@
package proxy
import (
"io"
"net"
"net/http"
"net/http/httputil"
"net/url"
"strings"
"time"
)
type Director struct {
Platform *url.URL
AI *url.URL
Timeout time.Duration
}
func New(platformURL, aiURL string, timeoutSec int) (*Director, error) {
p, err := url.Parse(platformURL)
if err != nil {
return nil, err
}
a, err := url.Parse(aiURL)
if err != nil {
return nil, err
}
if timeoutSec <= 0 {
timeoutSec = 60
}
return &Director{Platform: p, AI: a, Timeout: time.Duration(timeoutSec) * time.Second}, nil
}
func (d *Director) Handler() http.HandlerFunc {
transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 10 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
return func(w http.ResponseWriter, r *http.Request) {
target, path := d.route(r.URL.Path)
proxy := httputil.NewSingleHostReverseProxy(target)
proxy.Transport = transport
proxy.FlushInterval = 100 * time.Millisecond
proxy.ErrorHandler = func(rw http.ResponseWriter, req *http.Request, err error) {
rw.Header().Set("Content-Type", "application/json")
rw.WriteHeader(http.StatusBadGateway)
_, _ = rw.Write([]byte(`{"code":502,"message":"upstream unavailable: ` + escape(err.Error()) + `"}`))
}
proxy.ModifyResponse = func(resp *http.Response) error {
// 网关已统一加 CORS去掉上游重复头避免浏览器报 Failed to fetch
for _, h := range []string{
"Access-Control-Allow-Origin",
"Access-Control-Allow-Credentials",
"Access-Control-Allow-Headers",
"Access-Control-Allow-Methods",
"Access-Control-Expose-Headers",
"Access-Control-Max-Age",
} {
resp.Header.Del(h)
}
resp.Header.Set("X-Gateway", "aijianzhan-gateway")
return nil
}
// 重写路径
r.URL.Path = path
r.Host = target.Host
r.Header.Set("X-Forwarded-Host", r.Header.Get("Host"))
r.Header.Set("X-Forwarded-Proto", "http")
if r.Header.Get("X-Request-Id") == "" {
r.Header.Set("X-Request-Id", newRequestID())
}
// 超时上下文
ctx := r.Context()
proxy.ServeHTTP(w, r.WithContext(ctx))
}
}
func (d *Director) route(path string) (*url.URL, string) {
// AI/ai/* → 上游去掉 /ai 前缀
if strings.HasPrefix(path, "/ai/") || path == "/ai" {
next := strings.TrimPrefix(path, "/ai")
if next == "" {
next = "/"
}
return d.AI, next
}
// 生成蓝图:统一 /api/v1/apps/generate兼容旧冒号路径避免重复实现
if path == "/api/v1/apps/generate" || strings.HasPrefix(path, "/api/v1/apps/generate?") {
return d.AI, path
}
if strings.HasPrefix(path, "/api/v1/apps:generate") || strings.Contains(path, "/apps%3Agenerate") {
return d.AI, "/api/v1/apps/generate"
}
// 其余 /api 走中台
return d.Platform, path
}
func escape(s string) string {
s = strings.ReplaceAll(s, `\`, `\\`)
s = strings.ReplaceAll(s, `"`, `\"`)
if len(s) > 200 {
s = s[:200]
}
return s
}
func newRequestID() string {
return strings.ReplaceAll(time.Now().UTC().Format("20060102T150405.000000000"), ".", "")
}
// Health 聚合探测
func (d *Director) Health(w http.ResponseWriter, _ *http.Request) {
type st struct {
Name string `json:"name"`
OK bool `json:"ok"`
Detail string `json:"detail,omitempty"`
}
client := &http.Client{Timeout: 2 * time.Second}
out := []st{
probe(client, "platform", d.Platform.String()+"/api/v1/auth/login"),
probe(client, "ai", d.AI.String()+"/health"),
}
allOK := true
for _, s := range out {
if !s.OK {
allOK = false
break
}
}
code := http.StatusOK
if !allOK {
code = http.StatusServiceUnavailable
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
_, _ = io.WriteString(w, `{"gateway":"ok","upstreams":[`)
for i, s := range out {
if i > 0 {
_, _ = io.WriteString(w, ",")
}
ok := "false"
if s.OK {
ok = "true"
}
_, _ = io.WriteString(w, `{"name":"`+s.Name+`","ok":`+ok+`}`)
}
_, _ = io.WriteString(w, `]}`)
}
func probe(client *http.Client, name, rawURL string) struct {
Name string `json:"name"`
OK bool `json:"ok"`
Detail string `json:"detail,omitempty"`
} {
// login 用 OPTIONS/GET 可能 405用短超时 HEAD/GET health 风格
req, _ := http.NewRequest(http.MethodGet, rawURL, nil)
if name == "platform" {
// 未登录会 401/405 都说明服务活着;连接失败才算挂
req, _ = http.NewRequest(http.MethodPost, rawURL, strings.NewReader(`{}`))
req.Header.Set("Content-Type", "application/json")
}
resp, err := client.Do(req)
if err != nil {
return struct {
Name string `json:"name"`
OK bool `json:"ok"`
Detail string `json:"detail,omitempty"`
}{Name: name, OK: false, Detail: err.Error()}
}
defer resp.Body.Close()
return struct {
Name string `json:"name"`
OK bool `json:"ok"`
Detail string `json:"detail,omitempty"`
}{Name: name, OK: true}
}