Skip to content

feat(channel): add balance query for New API channels - #6737

Open
lelinhomafx4021-creator wants to merge 3 commits into
QuantumNous:mainfrom
lelinhomafx4021-creator:pr/newapi-upstream-balance
Open

feat(channel): add balance query for New API channels#6737
lelinhomafx4021-creator wants to merge 3 commits into
QuantumNous:mainfrom
lelinhomafx4021-creator:pr/newapi-upstream-balance

Conversation

@lelinhomafx4021-creator

@lelinhomafx4021-creator lelinhomafx4021-creator commented Aug 9, 2026

Copy link
Copy Markdown

⚠️ 提交说明 / PR Notice

Important

  • 请提供人工撰写的简洁摘要,避免直接粘贴未经整理的 AI 输出。

📝 变更描述 / Description

New API 类型渠道增加上游余额查询。管理员刷新余额时,系统使用渠道中配置的 Bearer Token 请求上游 /api/usage/token/,并结合 /api/status 识别余额单位及无限额度状态。

查询结果保存到 balance_info,前端按上游原始单位展示 USD、CNY、credits、tokens 或自定义单位,避免把人民币或积分误显示为美元。本次变更仅处理 New API 渠道;多密钥渠道仍不支持余额汇总,也不增加自动切换逻辑。

🚀 变更类型 / Type of change

  • 🐛 Bug 修复 (Bug fix) - 请关联对应 Issue,避免将设计取舍、理解偏差或预期不一致直接归类为 bug
  • ✨ 新功能 (New feature) - 重大特性建议先通过 Issue 沟通
  • ⚡ 性能优化 / 重构 (Refactor)
  • 📝 文档更新 (Documentation)

🔗 关联任务 / Related Issue

✅ 提交前检查项 / Checklist

  • 人工确认: 我已亲自整理并撰写此描述,没有直接粘贴未经处理的 AI 输出。
  • 非重复提交: 我已搜索现有的 IssuesPRs,确认不是重复提交。
  • Bug fix 说明: 若此 PR 标记为 Bug fix,我已提交或关联对应 Issue,且不会将设计取舍、预期不一致或理解偏差直接归类为 bug。
  • 变更理解: 我已理解这些更改的工作原理及可能影响。
  • 范围聚焦: 本 PR 未包含任何与当前任务无关的代码改动。
  • 本地验证: 已在本地运行并通过测试或手动验证,维护者可以据此复核结果。
  • 安全合规: 代码中无敏感凭据,且符合项目代码规范。

📸 运行证明 / Proof of Work

  • 上游余额查询、单位换算、无限额度和持久化相关 Go 测试通过。
  • 前端类型检查、New API 余额展示测试及生产构建通过。
  • 使用 MySQL 完成数据库迁移和独立分支启动验证,/api/status 返回 HTTP 200。
  • 手动验证金额、credits 和无限额度均按对应单位展示;多密钥渠道保持“不支持余额查询”的现有行为。
Snipaste_2026-08-09_23-33-59

Summary by CodeRabbit

  • New Features

    • Added balance tracking for New API channels, including credits, tokens, currencies, exchange rates, and unlimited quotas.
    • Balance information now appears with appropriate formatting in channel lists and balance dialogs.
    • Added automatic balance refresh and upstream status details.
    • Added configurable balance resets when creating or copying channels.
  • Bug Fixes

    • Improved handling of expired, malformed, exhausted, and unsupported balances.
    • Prevented native-currency balances from triggering legacy low-balance warnings.
  • Documentation

    • Documented New API balance queries, supported units, refresh behavior, and display rules.

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d23e11a8-e7fa-4dda-b35f-21e840a58e90

📥 Commits

Reviewing files that changed from the base of the PR and between 6fcacec and 64d1871.

📒 Files selected for processing (2)
  • controller/channel_authz_test.go
  • controller/channel_billing_balance_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • controller/channel_authz_test.go
  • controller/channel_billing_balance_test.go

Walkthrough

This PR adds New API channel balance retrieval, validation, normalization, persistence, and display. It supports native units, unlimited quotas, currencies, balance refreshes, exhausted-channel handling, and structured frontend responses.

Changes

New API balance support

Layer / File(s) Summary
Balance metadata model
model/channel.go, model/channel_balance.go, model/channel_balance_test.go, controller/channel.go
Channels persist structured balance metadata with native units, currencies, display values, unlimited status, and update timestamps. Channel creation and copying reset balance state when configured.
Upstream balance retrieval
controller/channel-billing-newapi.go, controller/channel-billing.go, controller/channel_billing_balance_test.go, docs/channel/new-api-balance.md
Controllers fetch and validate New API usage data, normalize balances, update channels, disable exhausted channels, and document the upstream flow.
Channel lifecycle and authorization
controller/channel_authz.go, controller/channel_authz_test.go
Authorization treats balance_info as read-only. Tests cover field clearing and balance reset behavior.
Frontend balance contract and formatting
web/src/features/channels/types.ts, web/src/features/channels/lib/new-api-balance.ts, web/src/features/channels/lib/channel-utils.ts, web/src/features/channels/lib/index.ts, web/src/features/channels/lib/__tests__/new-api-balance.test.ts
The frontend validates and formats structured balances, exports the formatter, and excludes structured New API balances from legacy low-balance checks.
Balance refresh and channel display
web/src/features/channels/lib/channel-actions.ts, web/src/features/channels/components/dialogs/balance-query-dialog.tsx, web/src/features/channels/components/channels-columns.tsx
Balance actions, dialogs, sorting, and channel columns support structured New API responses while preserving legacy scalar balance behavior.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Admin
  participant ChannelBilling
  participant NewAPI
  participant ChannelDatabase
  participant ChannelUI
  Admin->>ChannelBilling: request New API balance refresh
  ChannelBilling->>NewAPI: fetch usage and status
  NewAPI-->>ChannelBilling: return quota and status data
  ChannelBilling->>ChannelDatabase: persist balance_info
  ChannelDatabase-->>ChannelBilling: return updated balance data
  ChannelBilling-->>ChannelUI: return structured balance response
  ChannelUI->>Admin: render native balance and timestamp
Loading

Possibly related PRs

Poem

I’m a rabbit watching quotas flow,
Credits and currencies now show.
Balance metadata settles in place,
Refreshes return with structured grace.
Exhausted channels know when to go.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding balance queries for New API channels.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@controller/channel-billing-newapi.go`:
- Around line 80-106: Update normalizeNewAPIBalance to clamp negative remaining
quotas to zero before populating info.Remaining or calculating converted USD
values, while preserving unlimited handling and allowing newAPIBalanceExhausted
to disable depleted channels during bulk refresh.
- Around line 70-77: Update GetResponseBody to close res.Body on every execution
path, including non-200 upstream responses returned as errors. Ensure the body
is closed immediately after a successful HTTP response is obtained while
preserving the existing response parsing and error behavior used by
getNewAPIChannelResponse.

In `@controller/channel.go`:
- Line 630: Update AddChannel before BatchInsertChannels to reset all
server-managed balance fields on addChannelRequest.Channel: Balance,
BalanceUpdatedTime, UsedQuota, and the existing BalanceInfo. Ensure
client-provided values for these fields cannot be persisted during channel
creation.
- Around line 1436-1440: Update the clone balance handling around BalanceInfo so
structured and legacy balance state follow the same reset policy: when
resetBalance is false, preserve the source BalanceInfo, Balance,
BalanceUpdatedTime, and UsedQuota; when true, clear all corresponding balance
fields. Add tests covering both reset modes and their resulting balance state.

In `@web/src/features/channels/lib/channel-actions.ts`:
- Around line 376-403: Gate all balance query state updates behind
response.success && hasPayload: in balance-query-dialog.tsx, update currentRow
and balanceUpdatedAt only for successful payloads, and in channel-actions.ts,
invalidate channel-list queries only under the same condition. Preserve the
existing payload formatting and success callback behavior, and do not process
response.data or response.balance from failed responses.

In `@web/src/features/channels/lib/new-api-balance.ts`:
- Around line 30-43: Remove the default value from the unlimitedLabel parameter
in formatNewAPIBalance so callers must explicitly provide a localized label.
Preserve the existing unlimited formatting behavior and require all call sites
to pass their localized label.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ea815216-ac8b-4462-8786-0404aa7684a4

📥 Commits

Reviewing files that changed from the base of the PR and between 823e263 and d345f5f.

📒 Files selected for processing (18)
  • controller/channel-billing-newapi.go
  • controller/channel-billing.go
  • controller/channel.go
  • controller/channel_authz.go
  • controller/channel_authz_test.go
  • controller/channel_billing_balance_test.go
  • docs/channel/new-api-balance.md
  • model/channel.go
  • model/channel_balance.go
  • model/channel_balance_test.go
  • web/src/features/channels/components/channels-columns.tsx
  • web/src/features/channels/components/dialogs/balance-query-dialog.tsx
  • web/src/features/channels/lib/__tests__/new-api-balance.test.ts
  • web/src/features/channels/lib/channel-actions.ts
  • web/src/features/channels/lib/channel-utils.ts
  • web/src/features/channels/lib/index.ts
  • web/src/features/channels/lib/new-api-balance.ts
  • web/src/features/channels/types.ts

Comment on lines +70 to +77
func getNewAPIChannelResponse(channel *model.Channel, path string) ([]byte, error) {
baseURL, err := url.Parse(strings.TrimSpace(channel.GetBaseURL()))
if err != nil || (baseURL.Scheme != "http" && baseURL.Scheme != "https") || baseURL.Host == "" || baseURL.User != nil || baseURL.RawQuery != "" || baseURL.Fragment != "" {
return nil, errors.New("invalid New API channel base URL")
}
baseURL.Path = strings.TrimRight(baseURL.Path, "/") + path
baseURL.RawPath = ""
return GetResponseBody(http.MethodGet, baseURL.String(), channel, GetAuthHeader(channel.Key))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Close failed upstream response bodies.

GetResponseBody returns before it closes res.Body when the upstream returns a non-200 status. This new flow calls it for /api/status, then intentionally ignores that error. Repeated status failures can retain connections and file descriptors.

Close the body on every path in GetResponseBody.

Proposed fix
 res, err := client.Do(req)
 if err != nil {
   return nil, err
 }
+defer func() {
+  _ = res.Body.Close()
+}()
 if res.StatusCode != http.StatusOK {
   return nil, fmt.Errorf("status code: %d", res.StatusCode)
 }
 body, err := io.ReadAll(res.Body)
 if err != nil {
   return nil, err
 }
-err = res.Body.Close()
-if err != nil {
-  return nil, err
-}
 return body, nil
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@controller/channel-billing-newapi.go` around lines 70 - 77, Update
GetResponseBody to close res.Body on every execution path, including non-200
upstream responses returned as errors. Ensure the body is closed immediately
after a successful HTTP response is obtained while preserving the existing
response parsing and error behavior used by getNewAPIChannelResponse.

Comment thread controller/channel-billing-newapi.go
Comment thread controller/channel.go Outdated
Comment thread controller/channel.go Outdated
Comment thread web/src/features/channels/lib/channel-actions.ts
Comment thread web/src/features/channels/lib/new-api-balance.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
controller/channel_billing_balance_test.go (1)

37-37: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Move fatal handling out of the HTTP handler goroutine.

httptest.NewServer runs the handler in a server goroutine, and t.Fatalf calls FailNow with runtime.Goexit, which can exit only that goroutine. An unexpected upstream path may then leave the client without a response.

Record the failure with t.Errorf, write an HTTP error, and let the test goroutine assert the client error.

Proposed fix
-			t.Fatalf("unexpected path %s", r.URL.Path)
+			t.Errorf("unexpected path %s", r.URL.Path)
+			http.Error(w, "unexpected path", http.StatusInternalServerError)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@controller/channel_billing_balance_test.go` at line 37, In the httptest
handler, replace t.Fatalf in the unexpected-path branch with t.Errorf, write an
HTTP error response, and return from the handler. In the test goroutine, assert
that the client request reports the resulting error instead of relying on
handler-side fatal termination.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@controller/channel_authz_test.go`:
- Around line 145-149: Deep-copy or snapshot the fields of original.BalanceInfo
before invoking applyChannelBalanceReset in the no-reset test, rather than
relying on the shallow preserved := original copy. Update the assertions to
compare against that independent snapshot while retaining the existing Balance
and BalanceUpdatedTime checks.

---

Outside diff comments:
In `@controller/channel_billing_balance_test.go`:
- Line 37: In the httptest handler, replace t.Fatalf in the unexpected-path
branch with t.Errorf, write an HTTP error response, and return from the handler.
In the test goroutine, assert that the client request reports the resulting
error instead of relying on handler-side fatal termination.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 403afb5c-2407-4fe2-9213-feaa9780d346

📥 Commits

Reviewing files that changed from the base of the PR and between d345f5f and 6fcacec.

📒 Files selected for processing (9)
  • controller/channel-billing-newapi.go
  • controller/channel-billing.go
  • controller/channel.go
  • controller/channel_authz_test.go
  • controller/channel_billing_balance_test.go
  • web/src/features/channels/components/dialogs/balance-query-dialog.tsx
  • web/src/features/channels/lib/__tests__/new-api-balance.test.ts
  • web/src/features/channels/lib/channel-actions.ts
  • web/src/features/channels/lib/new-api-balance.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • web/src/features/channels/lib/tests/new-api-balance.test.ts
  • web/src/features/channels/lib/channel-actions.ts
  • controller/channel-billing-newapi.go
  • web/src/features/channels/lib/new-api-balance.ts
  • web/src/features/channels/components/dialogs/balance-query-dialog.tsx

Comment thread controller/channel_authz_test.go
@lelinhomafx4021-creator

Copy link
Copy Markdown
Author

@seefs001 Could you please review this PR? Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant