Skip to content

feat(dashboard): Models 视图增加 API Key 单选筛选 - #5913

Open
ran411285752 wants to merge 2 commits into
QuantumNous:mainfrom
ran411285752:feat/dashboard-api-key-filter
Open

feat(dashboard): Models 视图增加 API Key 单选筛选#5913
ran411285752 wants to merge 2 commits into
QuantumNous:mainfrom
ran411285752:feat/dashboard-api-key-filter

Conversation

@ran411285752

@ran411285752 ran411285752 commented Jul 5, 2026

Copy link
Copy Markdown

⚠️ 提交说明 / PR Notice

📝 变更描述 / Description

在 Dashboard → Models(Model Call Analytics)视图增加 API Key 单选筛选。管理员可搜索并筛选任意 key,普通用户只能搜索并筛选自己的 key。

  • 后端:quota_data 查询增加 token_id 过滤,model/usedata.gocontroller/usedata.go 同步调整。
  • 后端:新增管理员全局 API Key 搜索接口 /api/token/admin/search
  • 前端:Models 筛选弹窗新增 TokenFilterCombobox,使用 Base UI Combobox 实现固定搜索框、输入即触发后端搜索。
  • 搜索体验:model/token.go 中的 SearchUserTokensSearchAllTokens 改为大小写不敏感,并移除模糊搜索 ≥2 字符限制;项目已有分页与 searchHardLimit 控制性能。

🚀 变更类型 / Type of change

  • ✨ 新功能 (New feature)
  • 🐛 Bug 修复 (Bug fix)

🔗 关联任务 / Related Issue

✅ 提交前检查项 / Checklist

  • 范围聚焦: 本 PR 未包含与当前任务无关的代码改动。
  • 本地验证: 已运行 go test ./model ./controller 与前端 tsgo -b
  • 安全合规: 代码中无敏感凭据。

📸 运行证明 / Proof of Work

未筛选数据 筛选后数据 MODEL搜索页面

Summary by CodeRabbit

  • New Features

    • Added API key filtering to model analytics by time range, user, and API key.
    • Added searchable API key selection (with “All API keys” option) and localized empty/search states.
    • Administrators can search API keys across all users with pagination.
  • Improvements

    • API key searches now support case-insensitive prefix matching and more flexible keyword handling.
    • Quota/usage results can be filtered by API key.
    • Added/updated validation and automated test coverage for filtering/search behavior.

@coderabbitai

coderabbitai Bot commented Jul 5, 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

Run ID: 4ac9c497-12b4-442f-8b38-e87a41e2b222

📥 Commits

Reviewing files that changed from the base of the PR and between 73c79bb and 605f77c.

📒 Files selected for processing (19)
  • controller/token.go
  • controller/usedata.go
  • controller/usedata_test.go
  • model/token.go
  • model/usedata.go
  • model/usedata_test.go
  • router/api-router.go
  • web/default/src/features/dashboard/api.ts
  • web/default/src/features/dashboard/components/models/models-filter-dialog.tsx
  • web/default/src/features/dashboard/constants.ts
  • web/default/src/features/dashboard/lib/filters.ts
  • web/default/src/features/dashboard/types.ts
  • web/default/src/features/keys/api.ts
  • web/default/src/i18n/locales/en.json
  • web/default/src/i18n/locales/fr.json
  • web/default/src/i18n/locales/ja.json
  • web/default/src/i18n/locales/ru.json
  • web/default/src/i18n/locales/vi.json
  • web/default/src/i18n/locales/zh.json
🚧 Files skipped from review as they are similar to previous changes (18)
  • web/default/src/features/dashboard/types.ts
  • router/api-router.go
  • web/default/src/features/dashboard/api.ts
  • web/default/src/features/keys/api.ts
  • controller/usedata.go
  • controller/token.go
  • web/default/src/features/dashboard/constants.ts
  • web/default/src/i18n/locales/ja.json
  • web/default/src/i18n/locales/ru.json
  • web/default/src/i18n/locales/en.json
  • controller/usedata_test.go
  • web/default/src/i18n/locales/fr.json
  • web/default/src/i18n/locales/vi.json
  • model/usedata.go
  • web/default/src/i18n/locales/zh.json
  • web/default/src/features/dashboard/components/models/models-filter-dialog.tsx
  • model/usedata_test.go
  • model/token.go

Walkthrough

Adds admin-wide API-key search, token-scoped quota aggregation, and dashboard API-key filtering with localized UI support and tests.

Changes

Token search and quota filtering

Layer / File(s) Summary
Admin token search flow
model/token.go, controller/token.go, router/api-router.go
Adds case-insensitive fuzzy token search, global pagination, masking, and an admin-protected endpoint.
Quota aggregation token filtering
model/usedata.go, controller/usedata.go, model/usedata_test.go, controller/usedata_test.go
Adds optional token_id filtering to quota queries and validates filtered, unfiltered, and user-scoped results.
Dashboard filter state and API wiring
web/default/src/features/keys/api.ts, web/default/src/features/dashboard/{api.ts,types.ts,constants.ts}, web/default/src/features/dashboard/lib/filters.ts
Adds admin API-key lookup and propagates token_id through dashboard filters and requests.
Dashboard API-key filter control
web/default/src/features/dashboard/components/models/models-filter-dialog.tsx
Adds searchable admin/self API-key selection and binds it to dashboard filtering.
Localized API-key filter strings
web/default/src/i18n/locales/*.json
Adds API-key labels, search text, empty-state text, and updated analytics filter descriptions in six locales.

Estimated code review effort: 3 (Moderate) | ~35 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Admin
  participant Router
  participant TokenController
  participant TokenModel
  Admin->>Router: GET /api/token/admin/search
  Router->>TokenController: SearchAllTokens(c)
  TokenController->>TokenModel: SearchAllTokens(keyword, token, offset, limit)
  TokenModel-->>TokenController: tokens, total
  TokenController-->>Admin: masked token response
Loading
sequenceDiagram
  participant User
  participant TokenFilterCombobox
  participant KeysApi
  participant ModelsFilter
  User->>TokenFilterCombobox: open and enter search
  TokenFilterCombobox->>KeysApi: search API keys
  KeysApi-->>TokenFilterCombobox: API-key options
  User->>TokenFilterCombobox: select API key
  TokenFilterCombobox->>ModelsFilter: onValueChange(token_id)
Loading

Possibly related PRs

Suggested reviewers: fux-dev, seefs001

Poem

A rabbit hops through tokens bright,
And filters quotas left and right. 🐰
Keys now search and dashboards show,
Six languages help the signals flow.
Hop, hop—cleaner stats today! ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR only adds API Key filtering; issue #5432 also asks for broader time-range aggregation, metrics, sorting, and CSV export. Implement the full dashboard aggregation and reporting requirements from #5432, or split this change into a narrower issue that matches the current scope.
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 (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately states that the Models dashboard now supports single-select API Key filtering.
Out of Scope Changes check ✅ Passed The changes align with the API Key filtering scope and related backend/frontend support; no unrelated code changes stand out.
✨ 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: 4

🧹 Nitpick comments (9)
controller/usedata_test.go (1)

29-87: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Same require/assert convention gap as model/usedata_test.go.

All assertions here (e.g., Lines 39-42, 55, 69-71, 86) use require even where a failure wouldn't cascade into a panic. Consider assert for these value checks, keeping require for setup and the initial status/decoding guard in decodeQuotaDatesResponse.

As per coding guidelines: "New or substantially rewritten Go backend tests must use github.com/stretchr/testify/require for setup and fatal assertions, and github.com/stretchr/testify/assert for non-fatal value checks."

🤖 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/usedata_test.go` around lines 29 - 87, The tests in
GetAllQuotaDates and GetUserQuotaDates are using require for routine value
checks that should be non-fatal. Update these assertions to use assert for the
response content checks, while keeping require only for setup and the fatal
decode/guard path in decodeQuotaDatesResponse. Use the existing test function
names to locate the affected assertions and keep the require/assert split
consistent with model/usedata_test.go and the Go testing guideline.

Source: Coding guidelines

model/usedata_test.go (1)

36-75: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use assert for non-fatal value checks, require only for fatal setup/guards.

Every assertion in this new file uses require, including plain value equality checks (e.g., Lines 42-43, 56-57, 70) that aren't guarding subsequent code from a panic. Per the project's testing guideline, only setup and fatal assertions (like the require.Len gates before slice indexing) should use require; use assert for the rest so a single mismatch doesn't mask other failures in the same test.

As per coding guidelines: "New or substantially rewritten Go backend tests must use github.com/stretchr/testify/require for setup and fatal assertions, and github.com/stretchr/testify/assert for non-fatal value checks."

🤖 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 `@model/usedata_test.go` around lines 36 - 75, The new tests in
GetAllQuotaDatesByTokenID, GetQuotaDataByUserIdWithTokenID, and
GetQuotaDataByUsernameWithTokenID use require for non-fatal value comparisons
that should not stop the rest of the test. Keep require for setup and guard
checks that protect later indexing, but switch the plain equality/empty checks
on rows fields to assert so the tests can report multiple failures in one run.

Source: Coding guidelines

model/token.go (1)

192-244: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Cap SearchAllTokens the same way as SearchUserTokens

SearchAllTokens still does an unbounded Count() across the whole token table. Apply the same Limit(maxTokens) cap here so admin-side search doesn’t turn into a full-table scan on every input change. The LIKE-building block is also duplicated with SearchUserTokens; extracting that helper would keep the two paths in sync.

🤖 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 `@model/token.go` around lines 192 - 244, SearchAllTokens currently performs an
uncapped Count() over the entire Token table, which can cause full-table scans
on each search. Update SearchAllTokens to apply the same maxTokens cap used by
SearchUserTokens before counting and querying, and keep the existing
sanitizeLikePattern/LIKE behavior intact. If possible, factor the duplicated
LIKE-building logic shared with SearchUserTokens into a helper so both paths
stay consistent.
web/default/src/features/keys/api.ts (1)

57-70: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider deduplicating searchApiKeys/searchAdminApiKeys.

Introduces a new exported searchAdminApiKeys helper that builds query parameters from keyword, token, p, and size, calls the admin search endpoint (/api/token/admin/search), and returns the response payload. The body is identical to searchApiKeys apart from the endpoint path. Extracting a shared buildSearchParams/internal helper parameterized by path would remove the duplication.

♻️ Proposed refactor
+function buildSearchQueryParams(params: SearchApiKeysParams): URLSearchParams {
+  const { keyword = '', token = '', p, size } = params
+  const queryParams = new URLSearchParams()
+  if (keyword) queryParams.set('keyword', keyword)
+  if (token) queryParams.set('token', token)
+  if (p != null) queryParams.set('p', String(p))
+  if (size != null) queryParams.set('size', String(size))
+  return queryParams
+}
+
 export async function searchApiKeys(
   params: SearchApiKeysParams
 ): Promise<GetApiKeysResponse> {
-  const { keyword = '', token = '', p, size } = params
-  const queryParams = new URLSearchParams()
-  if (keyword) queryParams.set('keyword', keyword)
-  if (token) queryParams.set('token', token)
-  if (p != null) queryParams.set('p', String(p))
-  if (size != null) queryParams.set('size', String(size))
+  const queryParams = buildSearchQueryParams(params)
   const res = await api.get(`/api/token/search?${queryParams.toString()}`)
   return res.data
 }

 export async function searchAdminApiKeys(
   params: SearchApiKeysParams
 ): Promise<GetApiKeysResponse> {
-  const { keyword = '', token = '', p, size } = params
-  const queryParams = new URLSearchParams()
-  if (keyword) queryParams.set('keyword', keyword)
-  if (token) queryParams.set('token', token)
-  if (p != null) queryParams.set('p', String(p))
-  if (size != null) queryParams.set('size', String(size))
+  const queryParams = buildSearchQueryParams(params)
   const res = await api.get(`/api/token/admin/search?${queryParams.toString()}`)
   return res.data
 }
🤖 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 `@web/default/src/features/keys/api.ts` around lines 57 - 70, The new
searchAdminApiKeys helper duplicates the query-building logic already used by
searchApiKeys, so factor that shared work into a common internal helper such as
buildSearchParams or a path-parameterized search function. Update searchApiKeys
and searchAdminApiKeys in api.ts to reuse the same parameter construction and
only vary the endpoint path, keeping the exported API behavior unchanged.
web/default/src/features/dashboard/components/models/models-filter-dialog.tsx (4)

236-250: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Possible duplicate selection indicator inside ComboboxItem.

ComboboxItem (per web/default/src/components/ui/combobox.tsx) already renders a built-in ItemIndicator/check icon when an item is selected. Rendering a manual Check here as children may produce two visible checkmarks for the selected item.

🤖 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
`@web/default/src/features/dashboard/components/models/models-filter-dialog.tsx`
around lines 236 - 250, The selected-state checkmark is being rendered twice
inside the models filter combobox option list. Update the `options.map`
rendering in `models-filter-dialog.tsx` to rely on `ComboboxItem`’s built-in
selection indicator from `web/default/src/components/ui/combobox.tsx` instead of
adding a manual `Check` icon as a child, and keep only the label content inside
each item.

117-121: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Destructured props deviate from the file's established convention.

TokenFilterCombobox({ value, onValueChange, isAdmin }: TokenFilterComboboxProps) destructures its props, whereas ModelsFilter(props: ModelsFilterProps) in the same file consistently accesses props.xxx directly. As per coding guidelines, "Do not destructure objects unless necessary, especially component props; prefer direct property access such as props.xxx for clarity."

🤖 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
`@web/default/src/features/dashboard/components/models/models-filter-dialog.tsx`
around lines 117 - 121, The TokenFilterCombobox component is destructuring props
instead of following the file’s direct-access convention used by ModelsFilter.
Update TokenFilterCombobox to accept a single props object and reference its
fields with props.value, props.onValueChange, and props.isAdmin throughout the
component so the prop handling style stays consistent.

Source: Coding guidelines


117-125: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

No debounce on the search input — every keystroke triggers a backend request.

handleInputValueChange updates keyword synchronously on every keystroke, and keyword is part of the useQuery queryKey, so each character typed fires a new request to searchAdminApiKeys/searchApiKeys. Consider debouncing the keyword before it flows into the query key.

Also applies to: 206-209

🤖 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
`@web/default/src/features/dashboard/components/models/models-filter-dialog.tsx`
around lines 117 - 125, The search input in TokenFilterCombobox is updating the
query key on every keystroke, causing searchAdminApiKeys/searchApiKeys to fire
per character. Add a debounced version of keyword (or debounce
handleInputValueChange) and use that debounced value in the useQuery queryKey
and searchParams so typing does not trigger immediate backend requests. Keep the
existing TokenFilterCombobox and useQuery wiring intact, but ensure only the
debounced search term drives the fetch.

109-256: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider extracting TokenFilterCombobox into its own module.

This is a fully self-contained ~140-line component embedded in an already large file. As per coding guidelines, "Keep files reasonably small; when a single file grows beyond about 200 lines, consider extracting subcomponents or custom hooks."

🤖 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
`@web/default/src/features/dashboard/components/models/models-filter-dialog.tsx`
around lines 109 - 256, The TokenFilterCombobox component is self-contained and
should be moved out of the large models-filter-dialog.tsx file into its own
module to keep the parent file smaller and improve maintainability. Extract
TokenFilterCombobox (and any closely related types/hooks it relies on, such as
TokenFilterComboboxProps and its internal state logic) into a dedicated
component file, then import it back into the dialog so the existing behavior
stays unchanged.

Source: Coding guidelines

web/default/src/features/dashboard/lib/filters.ts (1)

155-171: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider reusing DashboardFilters type instead of duplicating fields inline.

The filters parameter type here duplicates a subset of DashboardFilters fields structurally rather than referencing the interface. If DashboardFilters changes (e.g., field renamed or type changed), this inline type won't automatically stay in sync, and a mismatch would only surface as a silent structural-typing pass rather than a compile error at the right location.

♻️ Suggested refactor
 export function buildQueryParams(
   timeRange: { start_timestamp: number; end_timestamp: number },
-  filters?: { time_granularity?: TimeGranularity; username?: string; token_id?: number }
+  filters?: Pick<DashboardFilters, 'time_granularity' | 'username' | 'token_id'>
 ): {
   start_timestamp: number
   end_timestamp: number
   default_time: string
   username?: string
   token_id?: number
 } {
🤖 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 `@web/default/src/features/dashboard/lib/filters.ts` around lines 155 - 171,
Refactor buildQueryParams to reuse the existing DashboardFilters type for the
filters parameter instead of the inline object shape. Update the function
signature to reference DashboardFilters (or a Pick based on it if you want to
keep only the needed fields) so changes to time_granularity, username, or
token_id stay aligned automatically. Keep the existing runtime behavior inside
buildQueryParams unchanged.
🤖 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
`@web/default/src/features/dashboard/components/models/models-filter-dialog.tsx`:
- Line 233: The filter input is not programmatically associated with the token
label, so update `TokenFilterCombobox` and `ComboboxInput` to accept and forward
an `id` prop to the underlying input. Make sure the control rendered in
`models-filter-dialog.tsx` receives `id="token_id"` so it matches the existing
`Label htmlFor="token_id"` and remains accessible to assistive technologies.
- Around line 170-181: The `TokenFilterCombobox` in `models-filter-dialog.tsx`
is still deriving `selectedLabel` only from `tokenMap`, so a preselected
`filters.token_id` can दिख as “All API keys” when its option is not in the
current search results. Update the label resolution logic to use the existing
per-ID lookup (`getApiKey` from `web/default/src/features/keys/api.ts`) as a
fallback when `selectedValue` is not `__all__`, and keep the `useEffect`/input
reset behavior driven by that resolved label.

In `@web/default/src/i18n/locales/vi.json`:
- Line 2733: The Vietnamese locale entries for the newly added API key strings
are still using English text, so update the translations in vi.json for both "No
API key found." and "Search API keys..." to proper Vietnamese values, matching
the intent of the corresponding zh.json entries. Locate these strings in the
locale map and replace the current English values with translated Vietnamese
text so the UI is fully localized.
- Line 1913: The Vietnamese translation for the model analytics filter string is
missing the “API key” clause. Update the entry in vi.json for the “Filter the
model analytics view by time range, user and API key.” key so it includes all
three filters, and keep the wording aligned with the surrounding i18n strings.

---

Nitpick comments:
In `@controller/usedata_test.go`:
- Around line 29-87: The tests in GetAllQuotaDates and GetUserQuotaDates are
using require for routine value checks that should be non-fatal. Update these
assertions to use assert for the response content checks, while keeping require
only for setup and the fatal decode/guard path in decodeQuotaDatesResponse. Use
the existing test function names to locate the affected assertions and keep the
require/assert split consistent with model/usedata_test.go and the Go testing
guideline.

In `@model/token.go`:
- Around line 192-244: SearchAllTokens currently performs an uncapped Count()
over the entire Token table, which can cause full-table scans on each search.
Update SearchAllTokens to apply the same maxTokens cap used by SearchUserTokens
before counting and querying, and keep the existing sanitizeLikePattern/LIKE
behavior intact. If possible, factor the duplicated LIKE-building logic shared
with SearchUserTokens into a helper so both paths stay consistent.

In `@model/usedata_test.go`:
- Around line 36-75: The new tests in GetAllQuotaDatesByTokenID,
GetQuotaDataByUserIdWithTokenID, and GetQuotaDataByUsernameWithTokenID use
require for non-fatal value comparisons that should not stop the rest of the
test. Keep require for setup and guard checks that protect later indexing, but
switch the plain equality/empty checks on rows fields to assert so the tests can
report multiple failures in one run.

In
`@web/default/src/features/dashboard/components/models/models-filter-dialog.tsx`:
- Around line 236-250: The selected-state checkmark is being rendered twice
inside the models filter combobox option list. Update the `options.map`
rendering in `models-filter-dialog.tsx` to rely on `ComboboxItem`’s built-in
selection indicator from `web/default/src/components/ui/combobox.tsx` instead of
adding a manual `Check` icon as a child, and keep only the label content inside
each item.
- Around line 117-121: The TokenFilterCombobox component is destructuring props
instead of following the file’s direct-access convention used by ModelsFilter.
Update TokenFilterCombobox to accept a single props object and reference its
fields with props.value, props.onValueChange, and props.isAdmin throughout the
component so the prop handling style stays consistent.
- Around line 117-125: The search input in TokenFilterCombobox is updating the
query key on every keystroke, causing searchAdminApiKeys/searchApiKeys to fire
per character. Add a debounced version of keyword (or debounce
handleInputValueChange) and use that debounced value in the useQuery queryKey
and searchParams so typing does not trigger immediate backend requests. Keep the
existing TokenFilterCombobox and useQuery wiring intact, but ensure only the
debounced search term drives the fetch.
- Around line 109-256: The TokenFilterCombobox component is self-contained and
should be moved out of the large models-filter-dialog.tsx file into its own
module to keep the parent file smaller and improve maintainability. Extract
TokenFilterCombobox (and any closely related types/hooks it relies on, such as
TokenFilterComboboxProps and its internal state logic) into a dedicated
component file, then import it back into the dialog so the existing behavior
stays unchanged.

In `@web/default/src/features/dashboard/lib/filters.ts`:
- Around line 155-171: Refactor buildQueryParams to reuse the existing
DashboardFilters type for the filters parameter instead of the inline object
shape. Update the function signature to reference DashboardFilters (or a Pick
based on it if you want to keep only the needed fields) so changes to
time_granularity, username, or token_id stay aligned automatically. Keep the
existing runtime behavior inside buildQueryParams unchanged.

In `@web/default/src/features/keys/api.ts`:
- Around line 57-70: The new searchAdminApiKeys helper duplicates the
query-building logic already used by searchApiKeys, so factor that shared work
into a common internal helper such as buildSearchParams or a path-parameterized
search function. Update searchApiKeys and searchAdminApiKeys in api.ts to reuse
the same parameter construction and only vary the endpoint path, keeping the
exported API behavior unchanged.
🪄 Autofix (Beta)

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

Run ID: 2b2dea44-635d-4e67-baac-5aa542b5492f

📥 Commits

Reviewing files that changed from the base of the PR and between 1ae7574 and 0f730f7.

📒 Files selected for processing (19)
  • controller/token.go
  • controller/usedata.go
  • controller/usedata_test.go
  • model/token.go
  • model/usedata.go
  • model/usedata_test.go
  • router/api-router.go
  • web/default/src/features/dashboard/api.ts
  • web/default/src/features/dashboard/components/models/models-filter-dialog.tsx
  • web/default/src/features/dashboard/constants.ts
  • web/default/src/features/dashboard/lib/filters.ts
  • web/default/src/features/dashboard/types.ts
  • web/default/src/features/keys/api.ts
  • web/default/src/i18n/locales/en.json
  • web/default/src/i18n/locales/fr.json
  • web/default/src/i18n/locales/ja.json
  • web/default/src/i18n/locales/ru.json
  • web/default/src/i18n/locales/vi.json
  • web/default/src/i18n/locales/zh.json

Comment thread web/default/src/features/dashboard/components/models/models-filter-dialog.tsx Outdated
Comment thread web/default/src/i18n/locales/vi.json Outdated
Comment thread web/default/src/i18n/locales/vi.json Outdated
@ran411285752

Copy link
Copy Markdown
Author

本 PR 仅实现 #5432 中的 Models 视图 API Key 单选筛选部分(管理员可筛选任意 key,普通用户只能筛选自己的 key),并不覆盖 #5432 中提到的完整 dashboard analytics 范围(如按 channel/token/model 聚合、汇总指标、排序、CSV 导出等)。如有需要,后续可在此基础上继续扩展。

@ran411285752
ran411285752 force-pushed the feat/dashboard-api-key-filter branch from 0f730f7 to 73c79bb Compare July 15, 2026 14:08

@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

🧹 Nitpick comments (1)
web/default/src/features/keys/api.ts (1)

57-70: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider extracting the shared parameter-building logic.

The logic for building the URLSearchParams is identical to the adjacent searchApiKeys function. You can extract this into a helper function to avoid duplicating the query parameter construction.

♻️ Proposed refactor
+function buildSearchQueryParams(params: SearchApiKeysParams): string {
+  const { keyword = '', token = '', p, size } = params
+  const queryParams = new URLSearchParams()
+  if (keyword) queryParams.set('keyword', keyword)
+  if (token) queryParams.set('token', token)
+  if (p != null) queryParams.set('p', String(p))
+  if (size != null) queryParams.set('size', String(size))
+  return queryParams.toString()
+}
+
 // Admin-only: search all users' API keys by keyword or token (with pagination)
 export async function searchAdminApiKeys(
   params: SearchApiKeysParams
 ): Promise<GetApiKeysResponse> {
-  const { keyword = '', token = '', p, size } = params
-  const queryParams = new URLSearchParams()
-  if (keyword) queryParams.set('keyword', keyword)
-  if (token) queryParams.set('token', token)
-  if (p != null) queryParams.set('p', String(p))
-  if (size != null) queryParams.set('size', String(size))
-  const res = await api.get(`/api/token/admin/search?${queryParams.toString()}`)
+  const res = await api.get(`/api/token/admin/search?${buildSearchQueryParams(params)}`)
   return res.data
 }

(Note: You can then reuse this buildSearchQueryParams helper to simplify searchApiKeys above it as well).

🤖 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 `@web/default/src/features/keys/api.ts` around lines 57 - 70, Extract the
duplicated URLSearchParams construction from searchAdminApiKeys and the adjacent
searchApiKeys into a shared buildSearchQueryParams helper. Have both functions
use the helper while preserving the existing keyword, token, p, and size
inclusion behavior.
🤖 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 `@model/token.go`:
- Around line 230-234: Define and initialize maxTokens within SearchAllTokens
before the baseQuery.Limit(maxTokens).Count call, matching the initialization
and value used by SearchUserTokens. Keep the existing capped count and
pagination behavior unchanged.

---

Nitpick comments:
In `@web/default/src/features/keys/api.ts`:
- Around line 57-70: Extract the duplicated URLSearchParams construction from
searchAdminApiKeys and the adjacent searchApiKeys into a shared
buildSearchQueryParams helper. Have both functions use the helper while
preserving the existing keyword, token, p, and size inclusion behavior.
🪄 Autofix (Beta)

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

Run ID: ea6c18e9-4fff-4c39-ba1a-755be1fd9044

📥 Commits

Reviewing files that changed from the base of the PR and between 0f730f7 and 73c79bb.

📒 Files selected for processing (19)
  • controller/token.go
  • controller/usedata.go
  • controller/usedata_test.go
  • model/token.go
  • model/usedata.go
  • model/usedata_test.go
  • router/api-router.go
  • web/default/src/features/dashboard/api.ts
  • web/default/src/features/dashboard/components/models/models-filter-dialog.tsx
  • web/default/src/features/dashboard/constants.ts
  • web/default/src/features/dashboard/lib/filters.ts
  • web/default/src/features/dashboard/types.ts
  • web/default/src/features/keys/api.ts
  • web/default/src/i18n/locales/en.json
  • web/default/src/i18n/locales/fr.json
  • web/default/src/i18n/locales/ja.json
  • web/default/src/i18n/locales/ru.json
  • web/default/src/i18n/locales/vi.json
  • web/default/src/i18n/locales/zh.json
🚧 Files skipped from review as they are similar to previous changes (15)
  • web/default/src/features/dashboard/types.ts
  • web/default/src/features/dashboard/constants.ts
  • router/api-router.go
  • web/default/src/features/dashboard/api.ts
  • controller/token.go
  • web/default/src/features/dashboard/lib/filters.ts
  • web/default/src/i18n/locales/vi.json
  • model/usedata_test.go
  • controller/usedata.go
  • web/default/src/i18n/locales/ru.json
  • controller/usedata_test.go
  • web/default/src/i18n/locales/fr.json
  • web/default/src/i18n/locales/en.json
  • web/default/src/features/dashboard/components/models/models-filter-dialog.tsx
  • web/default/src/i18n/locales/zh.json

Comment thread model/token.go
Why:
- 数据看板 Models 视图需要按 API Key 单选筛选,管理员可查看任意 key,普通用户只能查看自己的 key。
- 官方 issue QuantumNous#5432 仍在开放,QuantumNous#938 已关闭为 not planned。

What:
- 后端:在 quota_data 查询中增加 token_id 过滤(model/usedata.go、controller/usedata.go)。
- 后端:新增管理员全局 API Key 搜索 SearchAllTokens 及 /api/token/admin/search 路由;SearchAllTokens 的 COUNT 与 SearchUserTokens 一致用 maxTokens 封顶,避免 admin 全局搜索触发全表扫描。
- 前端:Models 筛选弹窗新增 TokenFilterCombobox,使用 Base UI Combobox 实现可搜索单选。
- 前端:DashboardFilters 增加 token_id,API 层与 i18n 同步更新(补全 vi.json 翻译)。
- 搜索体验:将 API Key 搜索改为大小写不敏感,并移除模糊搜索 ≥2 字符限制;项目已有分页、searchHardLimit 等更合理的性能控制手段。
- 选择器细节(按 CodeRabbit 反馈完善):搜索输入防抖 300ms;ComboboxInput 透传 id 与 Label htmlFor 关联,修复可访问性;预选中的 key 不在当前搜索结果时按 id 兜底取名称,避免输入框错误回退显示“全部 API 密钥”;移除 ComboboxItem 中与内置选中指示器重复的手动勾选图标。

Impact:
- 仅影响 Dashboard Models 视图与 API Key 搜索行为,Flow/User Analytics 视图未改动。
- 计费与日志口径未改动(仅调整筛选维度与 UI 表现)。
@ran411285752
ran411285752 force-pushed the feat/dashboard-api-key-filter branch from 73c79bb to 605f77c Compare July 15, 2026 14:33
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.

建议增强数据看板:支持按时间范围、渠道、令牌、模型聚合统计 Tokens、额度和调用次数

1 participant