From fc2c811e93965186c186511f2b070bd10316069c Mon Sep 17 00:00:00 2001 From: chenchen Date: Fri, 8 May 2026 18:36:24 +0800 Subject: [PATCH] feat(server): notify mcp-server billing_provider=newapi after Agnet user sync MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements Heicode's choice (b) for §7.7.1 ②: in syncLocalUserFromAgnet, fire-and-forget PUT mcp-server's internal /api/auth/internal/billing-provider {email, billing_provider:"newapi"} so mcp-server's User table billing_provider column lands as 'newapi' for users that came in via Heicode Manager (vs the default 'litellm' for native taijiagent users). - Goroutine: never blocks login on this side-effect; mcp-server endpoint is idempotent so retries from repeat logins are harmless. - Token via env MCP_SERVER_INTERNAL_TOKEN (K8s/compose secret); empty env silently skips (dev-friendly). - Reuses agnetHTTPClient + common.Marshal + common.GetUUID per repo conventions. Spec: docs/Heicode-对接进度与待办.md §7.8.1. Co-Authored-By: Claude Opus 4.7 (1M context) --- heicode/controller/heicode_agnet_session.go | 58 +++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/heicode/controller/heicode_agnet_session.go b/heicode/controller/heicode_agnet_session.go index 534cd37..e34a6a6 100644 --- a/heicode/controller/heicode_agnet_session.go +++ b/heicode/controller/heicode_agnet_session.go @@ -174,6 +174,61 @@ func fetchAgnetRefresh(baseURL, refreshToken string) (access string, refresh str return env.Data.Token, env.Data.RefreshToken, nil } +// markBillingProviderNewapi notifies mcp-server that this user's billing +// provider should be set to "newapi" (per Heicode 决策 7.7.1 ②). +// +// Fire-and-forget: 失败不影响登录主流程;mcp-server 那边也允许重复调用 +// (内部端点对相同 email 幂等)。详见 Heicode-对接进度与待办.md §7.8.1。 +// +// Token 通过 K8s secret / docker compose env `MCP_SERVER_INTERNAL_TOKEN` +// 注入;未配置时跳过(开发环境兼容)。 +func markBillingProviderNewapi(email string) { + tok := strings.TrimSpace(os.Getenv("MCP_SERVER_INTERNAL_TOKEN")) + if tok == "" { + return + } + email = strings.TrimSpace(email) + if email == "" { + return + } + go func(email, tok string) { + body, err := common.Marshal(map[string]string{ + "email": email, + "billing_provider": "newapi", + }) + if err != nil { + common.SysLog("markBillingProviderNewapi: marshal failed: " + err.Error()) + return + } + baseURL := defaultHeicodeAuthBaseURL() + req, err := http.NewRequest( + http.MethodPut, + baseURL+"/api/auth/internal/billing-provider", + strings.NewReader(string(body)), + ) + if err != nil { + common.SysLog("markBillingProviderNewapi: req build failed: " + err.Error()) + return + } + req.Header.Set("Authorization", "Bearer "+tok) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-Request-Id", common.GetUUID()) + res, err := agnetHTTPClient().Do(req) + if err != nil { + common.SysLog("markBillingProviderNewapi: send failed: " + err.Error()) + return + } + defer res.Body.Close() + if res.StatusCode >= 400 { + b, _ := io.ReadAll(io.LimitReader(res.Body, 1<<14)) + common.SysLog(fmt.Sprintf( + "markBillingProviderNewapi: %d %s — %s", + res.StatusCode, email, strings.TrimSpace(string(b)), + )) + } + }(email, tok) +} + func syncLocalUserFromAgnet(me agnetMeEnvelope) (*model.User, error) { email := strings.TrimSpace(me.Data.Email) if email == "" { @@ -242,6 +297,9 @@ func syncLocalUserFromAgnet(me agnetMeEnvelope) (*model.User, error) { } } + // Notify mcp-server billing_provider=newapi (fire-and-forget; goroutine). + markBillingProviderNewapi(user.Email) + return &user, nil }