Files
heicode-win/heicode/controller/heicode_auth_proxy.go
T
chenchenandClaude Opus 4.7 7c3ecbcefc feat(manager): align with product-package docs §10/§11
CORS unblock — add /api/heicode-auth/*proxyPath backend proxy to
HEICODE_AUTH_BASE_URL. Frontend defaults to same-origin path so
the browser never hits APIM directly.

Sidebar — replace backend jargon (Git sources / Deployments /
Events / Wallet / Available models / Profile) with the user-facing
labels docs §10 mandates: 总览 / 准备清单 / 任务总览 / 审计 /
模型与余额 / 客户端 / 账号安全.

/sk-sources rewritten as 4-card preparation wizard with progress
meter; full Git form moves into a 高级补充 sheet. Drops JSON
editor, permission manifest, snapshots and resource-grant pills.

/deployments simplified to 任务总览: objective + status + last
update. Drops risk / budget / scope / secret_ref pills and the
RunDetailPanel; manifest details only in audit/advanced views.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 13:01:14 +08:00

69 lines
2.1 KiB
Go

package controller
import (
"io"
"net/http"
"strings"
"time"
"github.com/gin-gonic/gin"
)
// HeicodeAuthProxy transparently forwards browser calls to the upstream Heicode
// identity service (APIM). The frontend cannot call APIM directly because that
// host does not include code.xinghanlab.com in its CORS allow-list, so we
// terminate the request same-origin and re-emit it server-side.
//
// Mounted at /api/heicode-auth/*proxyPath. The trailing path (everything after
// /api/heicode-auth/) is appended verbatim to HEICODE_AUTH_BASE_URL. Request
// method, query string, body, and the Authorization header are preserved.
func HeicodeAuthProxy(c *gin.Context) {
tail := strings.TrimPrefix(c.Param("proxyPath"), "/")
if tail == "" {
c.JSON(http.StatusNotFound, gin.H{"success": false, "message": "missing upstream path"})
return
}
baseURL := defaultHeicodeAuthBaseURL()
target := baseURL + "/" + tail
if raw := c.Request.URL.RawQuery; raw != "" {
target += "?" + raw
}
var body io.Reader
if c.Request.Body != nil {
body = c.Request.Body
}
req, err := http.NewRequestWithContext(c.Request.Context(), c.Request.Method, target, body)
if err != nil {
c.JSON(http.StatusBadGateway, gin.H{"success": false, "message": "upstream request build failed"})
return
}
// Forward only headers that matter for the upstream call. We intentionally
// drop Cookie, Host, Origin, Referer so APIM does not see browser context.
if auth := strings.TrimSpace(c.GetHeader("Authorization")); auth != "" {
req.Header.Set("Authorization", auth)
}
if ct := strings.TrimSpace(c.GetHeader("Content-Type")); ct != "" {
req.Header.Set("Content-Type", ct)
}
if accept := strings.TrimSpace(c.GetHeader("Accept")); accept != "" {
req.Header.Set("Accept", accept)
}
client := &http.Client{Timeout: 20 * time.Second}
resp, err := client.Do(req)
if err != nil {
c.JSON(http.StatusBadGateway, gin.H{"success": false, "message": "upstream request failed"})
return
}
defer resp.Body.Close()
if ct := resp.Header.Get("Content-Type"); ct != "" {
c.Writer.Header().Set("Content-Type", ct)
}
c.Writer.WriteHeader(resp.StatusCode)
_, _ = io.Copy(c.Writer, resp.Body)
}