Skip to content

重构文档中心、账号分组与 API Key 权限管理#127

Merged
james-6-23 merged 30 commits into
james-6-23:mainfrom
DeliciousBuding:rebase-onto-v2.1.3
May 13, 2026
Merged

重构文档中心、账号分组与 API Key 权限管理#127
james-6-23 merged 30 commits into
james-6-23:mainfrom
DeliciousBuding:rebase-onto-v2.1.3

Conversation

@DeliciousBuding
Copy link
Copy Markdown
Contributor

@DeliciousBuding DeliciousBuding commented May 13, 2026

变更概览

本 PR 基于 v2.1.3,对管理后台做了一轮较完整的产品化重构和能力补完,重点不在单点修 bug,而在于把文档中心、账号管理、API Key 管理、仪表盘与基础交互体系整体拉到一个更可用、可维护、可升级的状态。

这次改动可以概括为五条主线:

  1. 重构文档中心 /admin/docs
  2. 完善账号管理与账号分组体系
  3. 完善 API Key 管理与权限范围控制
  4. 优化仪表盘 / 使用统计 / 代理页 / 前端基础设计语言
  5. 收口 review 暴露出的事务性、缓存一致性与升级兼容问题

主要贡献

1. 重构文档中心 /admin/docs

  • 将原先分散的使用说明和 API 文档整合为一个集成式文档中心。
  • 新文档中心统一承载:
    • 快速接入
    • 一键导入 AI 客户端
    • 客户端配置
    • 认证方式
    • 模型 API
    • 管理 API
  • 增加完整的文档交互层:
    • 右侧目录导航
    • 接口文档卡片
    • Try-it 调试
    • 代码复制
    • Markdown 导出
    • 语法高亮
  • 客户端接入说明和导入能力集中整理,覆盖:
    • Codex CLI
    • Claude Code
    • CC Switch
    • Cherry Studio
  • 文档页的布局、卡片、按钮、浅色代码块、字体和 i18n 做了系统整理。
  • docs/API.md 同步补齐,保证仓库文档与站内文档一致。

2. 完善账号管理

  • 新增账号分组能力:
    • GET /api/admin/account-groups
    • POST /api/admin/account-groups
    • PATCH /api/admin/account-groups/:id
    • DELETE /api/admin/account-groups/:id
  • 账号列表新增并打通:
    • 标签
    • 账号分组
    • 分组筛选
    • 批量元数据编辑
    • 列显示设置
  • 调度配置支持更多元数据:
    • tags
    • group_ids
    • proxy_url
    • allowed_api_key_ids
  • 删除账号分组后会同步刷新账号侧数据,避免残留无效 group_ids
  • 使用统计页增加列显示设置,并持久化用户偏好。

3. 完善 API Key 管理

  • API Key 不再只是创建 / 删除,而是支持完整编辑:
    • 名称
    • 额度限制
    • 过期时间
    • 允许账号分组 allowed_group_ids
  • API Key 与账号分组体系打通,可限制某个 key 仅能调度指定分组。
  • PATCH 语义补齐:
    • 字段省略保持原值
    • 0 / null 可清空额度或过期时间
  • 前端补上 API Key 编辑弹窗、分组展示、状态展示、复制和显示交互。

4. 仪表盘、使用统计、代理页与前端基础体验优化

  • 仪表盘重构使用统计卡片,抽出 UsageStatsSummary,将流量、Token、缓存、健康四组信息统一整理。
  • Dashboard 现在能稳定展示并校准:
    • 今日缓存命中率
    • 今日缓存 Token
    • 总缓存命中率
    • 首字延迟
    • 完成延迟
    • 今日错误率
  • 代理页增强:
    • 编辑代理 URL / 标签
    • 批量测试进度
    • 前端 URL 校验
    • 测试失败状态正确回写
  • 新增一批复用组件:
    • ChipInput
    • UsageStatsSummary
    • DocsTOC
    • EndpointDoc
  • 页面设计语言统一了一轮:
    • 卡片顶部留白
    • 列设置弹层
    • 单行筛选布局
    • 编辑弹窗样式
    • 代码面板样式
  • 重新整理字体和代码块表现:
    • 更新等宽字体栈
    • 增强代码面板排版
    • 单独优化浅色模式语法高亮可读性

5. review 后补的稳定性修复

  • 账号调度 PATCH 改为数据库侧单事务更新,避免部分字段成功、部分字段失败的半更新状态。
  • API Key 允许分组在运行态做标准化和集合缓存,避免非法值、重复值和热路径重复分配。
  • 删除账号分组时同步清理 API Key 允许分组。
  • 软删除账号时同步清理账号分组成员关系,避免成员计数和删除校验脏数据。
  • 分组成员计数和删除校验只统计有效账号,不再把已软删除账号算进去。
  • 代理增删改成功后不再因为 reload 失败误报 500。
  • 管理 API 的 Try-it 改为使用 admin key,不再错误预填普通 API key。
  • deeplink 参数做了 URL 编码,主题切换下代码高亮也能正确刷新。
  • docs / try-it / tooltip / usage 等细节文案继续补齐 i18n,减少中英混杂。

升级兼容性

  • SQLite / PostgreSQL 都补了迁移兼容,不只支持新装,也覆盖旧库升级场景。
  • api_keys 新增 allowed_group_ids 字段。
  • 旧的 account_groups / usage_stats_baseline 缺列场景会自动补齐。
  • usage_stats_baseline 现在会同时保留缓存命中统计和首字延迟样本,避免清日志后 Dashboard 相关指标掉空。
  • allowed_group_ids 为空数组仍表示不限制账号分组。
  • quota_limit / quota0null 可清空额度限制。
  • expires_atnullexpires_in_days0 可清空过期时间。

验证

已通过:

  • go test ./admin ./database ./auth ./proxy
  • npm run typecheck --prefix frontend
  • npm run build --prefix frontend
  • git diff --check

页面截图

仪表盘

仪表盘

文档中心

docs

Summary by CodeRabbit

  • New Features

    • Account group CRUD and membership management; API keys can be scoped to allowed groups
    • Account tags, per-account proxy URL, and batch-editing of tags/groups
    • UI: group/tag multi-select chips, UsageStatsSummary component, Docs page with generated API reference
  • Documentation

    • Expanded API docs for account groups, API key fields (allowed groups), completions/responses examples, and admin endpoints
  • Bug Fixes

    • Improved error handling for proxy operations and account/API key updates; UI preserves omitted API key fields on update

…ts extensions

- Add AccountGroup, AccountGroupsResponse, CreateAccountGroupRequest, UpdateAccountGroupRequest
- Add tags/group_ids to AccountRow, allowed_group_ids to APIKeyRow
- Extend UpdateAccountSchedulerRequest with proxy_url, tags, group_ids
- Add cache_hit_rate to AccountUsageDetail
- Add optional cache/latency fields to UsageStats (avg_first_token_ms, today_cache_rate, etc.)
Add editingId/editingName state, handleRenameKey handler calling
api.updateAPIKey(id, { name }), and inline Input+Check/X UI for
renaming API keys directly in the table row.
- Add listAccountGroups, createAccountGroup, updateAccountGroup, deleteAccountGroup
- Add updateAPIKey for name and allowed_group_ids
- Add no-store cache to admin request function
- Prefer Cascadia Mono/Code in --font-mono stack
- Add code-panel, code-panel-header, code-panel-pre, code-inline classes
- Add syntax token colors and shiki wrapper styles
- Add card/button transition animations
Supports a text/meta line above the action buttons area, useful for
showing context like timestamp or status info next to the header.
Supports free-text tag entry (Enter/comma to add), select-from-list
mode with options prop, chips with X to remove, and overflow "+N" badge.
- Edit dialog with URL/label inputs and api.updateProxy call
- Concurrent proxy testing (TEST_ALL_CONCURRENCY=4) with progress
  display showing done/total/failed counts
- Error reporting via toast for all batch operations
- Pencil edit button in each proxy table row
- Create UsageStatsSummary with Flow/Token/Cache/Health metric groups
- Health uses first-token latency as primary metric
- Token billing shows "Today: $X / Total: $X" format
- Remove noisy "0% availability" subtext from available StatCard
- Cache labels: today cache hit rate, today cached tokens, total cache hit rate
Minimal additions to api.ts required by frontend-pages features:
- updateAPIKey(id, { name?, allowed_group_ids? }) for API key rename
- updateProxy now accepts url parameter for proxy edit dialog
Copilot AI review requested due to automatic review settings May 13, 2026 07:09
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 13, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c2846bfd-d134-4bfb-a6b9-d988b36ff1cc

📥 Commits

Reviewing files that changed from the base of the PR and between fb405c5 and ab123ef.

📒 Files selected for processing (4)
  • admin/handler.go
  • admin/image_studio.go
  • database/models.go
  • database/postgres.go

📝 Walkthrough

Walkthrough

Implements multi-tenant account grouping and API-key group scoping end-to-end: DB schema and helpers, admin HTTP handlers, runtime/store enforcement and scheduler integration, API-key persistence/updates, frontend docs/highlighter, UI components/pages, migrations, and tests.

Changes

Database and Runtime Store Account Groups and API Key Scoping

Layer / File(s) Summary
Database Account Groups CRUD, helpers, and migrations
database/account_groups.go, database/helpers.go, database/sqlite.go, database/sqlite_test.go, database/postgres.go
Adds AccountGroup model, list/create/update/delete and membership APIs; JSON helpers for int64/tag slices; SQLite/Postgres migrations for account_groups, account_group_members, and api_keys.allowed_group_ids; tests for migration and delete semantics.
API Key persistence and update helpers
database/postgres.go, database/image_studio.go
Extends APIKeyRow/Input/Update with AllowedGroupIDs, updates scan/insert/update/select to persist allowed_group_ids and provides UpdateAPIKey helpers handling driver differences.
Usage stats baseline and first-token/cache aggregates
database/postgres.go, database/sqlite.go, database/sqlite_test.go
Extends UsageStats, queries, baseline accumulation, and tests to include cache-hit counts and first-token timing aggregates.

Runtime Store and Scheduler

Layer / File(s) Summary
Store: tags/groups, API-key allowed-groups, and scheduler hook
auth/store.go, auth/fast_scheduler.go, auth/fast_scheduler_test.go, auth/session_affinity_test.go
Adds Account.Tags and GroupIDs, methods to apply tags/groups and configure API-key allowed groups, loads memberships during init, sets scheduler group-check callback, and updates selection logic; adds tests ensuring group-scoped selection and session fallback.

Admin API: Handlers, Routing, Responses, and Tests

Layer / File(s) Summary
Account groups HTTP handlers and admin routing
admin/account_groups.go, admin/handler.go
Implements List/Create/Update/Delete handlers with sanitization/validation, enforces max group count, supports force deletion and prunes API key scopes; registers routes under /api/admin.
API key create/update/delete and scheduler metadata
admin/handler.go, admin/responses.go, admin/handler_test.go
Adds allowed_group_ids to create/update requests and responses, validates group references, updates runtime store bindings, extends UpdateAccountScheduler to accept tags/group_ids/proxy_url with DB persistence and runtime overrides, and adds tests for key PATCH behavior and runtime cache refresh.
Proxy and settings error mapping
admin/handler.go, proxy/handler.go
Improves proxy handlers to surface DB-not-found, accepts URL updates, reloads pool and logs failures, and synchronizes API-key allowed groups on key resolution.

Frontend Types, API Client, Hooks, Components, and Pages

Layer / File(s) Summary
Types and API client
frontend/src/types.ts, frontend/src/api.ts
Adds account group types, extends AccountRow and API key/request/response shapes with tags/group_ids/allowed_group_ids; adds api methods for account-groups and updateAPIKey; request helper defaults cache no-store.
Shiki highlighter and docs styling
frontend/package.json, frontend/src/hooks/useHighlighter.ts, frontend/src/index.css
Adds shiki dependency, implement useHighlightedHtml hook with theme detection and LRU HTML cache, and adds code-panel CSS and reduced-motion support.
Documentation UI and content generation
frontend/src/pages/docs/* (Docs.tsx, DocsTOC.tsx, EndpointDoc.tsx, docsContent.ts, quickStartTools.ts)
Adds comprehensive Docs page with TOC, endpoint docs, Try-it dialog, code blocks with highlighting, quick-tools/templates, and markdown generation.
UI components and pages integration
frontend/src/components/ChipInput.tsx, frontend/src/components/UsageStatsSummary.tsx, frontend/src/components/PageHeader.tsx, frontend/src/components/ui/card.tsx, frontend/src/pages/APIKeys.tsx, frontend/src/pages/Accounts.tsx, frontend/src/pages/Proxies.tsx, frontend/src/pages/Dashboard.tsx, frontend/src/pages/Usage.tsx
Adds ChipInput for tags, UsageStatsSummary, PageHeader actionMeta, card spacing tweaks, API Keys page group UI and editing, Accounts page tag/group filtering, batch editing, group manager, column visibility persistence, Proxies page edit/test improvements, Dashboard usage component swap, and Usage column visibility.
Highlighter usage and codeblock components
frontend/src/pages/docs/EndpointDoc.tsx, frontend/src/hooks/useHighlighter.ts
Adds CodeBlock with lazy highlight, TryItDialog for live testing, and MethodBadge/StatusTabs.
Localization and docs
frontend/src/locales/en.json, frontend/src/locales/zh.json, docs/API.md
Adds English/Chinese strings for dashboard metrics, account tags/groups, proxies, API keys, and docs sections; documents account-groups endpoints and expanded API key fields.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Poem

🐰 I nibble code in moonlit stacks,

I group the accounts, tidy the racks,
Keys learn their circles, caches refreshed,
Docs glow bright, components meshed,
Hop—merge, test, and ship the packs!

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 15

Caution

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

⚠️ Outside diff range comments (3)
database/postgres.go (1)

3139-3148: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Missing active-account guard when updating allowed_api_key_ids.

This branch can update soft-deleted accounts because it selects by id only. It should use the same non-deleted predicate as the scheduler-update existence check.

💡 Suggested fix
-    selectQuery := `SELECT credentials FROM accounts WHERE id = $1`
+    selectQuery := `SELECT credentials FROM accounts WHERE id = $1 AND status <> 'deleted' AND COALESCE(error_message, '') <> 'deleted'`
     if db.isSQLite() {
-        selectQuery = `SELECT credentials FROM accounts WHERE id = ?`
+        selectQuery = `SELECT credentials FROM accounts WHERE id = ? AND status <> 'deleted' AND COALESCE(error_message, '') <> 'deleted'`
     } else {
         selectQuery += ` FOR UPDATE`
     }
🤖 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 `@database/postgres.go` around lines 3139 - 3148, The SELECT that reads
credentials (selectQuery) currently only filters by id and thus can lock and
update soft-deleted accounts; change the query to include the same non-deleted
predicate used in the scheduler-update existence check (e.g., add "AND
<non-deleted-predicate>" such as "AND deleted_at IS NULL" or the project's
equivalent) for both the SQLite and non-SQLite branches, preserving the FOR
UPDATE for non-SQLite; ensure the parameter order passed to
tx.QueryRowContext(ctx, selectQuery, id, ...) matches the added predicate
parameters and update references to selectQuery, db.isSQLite(),
tx.QueryRowContext and id accordingly.
frontend/src/pages/Proxies.tsx (1)

179-189: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Count unsuccessful test responses as failures.

Both handlers only react to thrown requests. If the API returns HTTP 200 with { success: false }, the spinner clears but the row/test-all UI records no error and testAllFailed stays wrong.

Also applies to: 208-219

🤖 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 `@frontend/src/pages/Proxies.tsx` around lines 179 - 189, The API handler
currently only treats thrown errors as failures; update the logic after calling
api.testProxy(p.url, p.id, ipApiLang) to explicitly handle result.success ===
false as a failure: when result.success is false, call showToast with
t('proxies.testFailed', { error: result.error || getErrorMessage(new Error('test
failed')) }) (or similar), update setProxies so the row reflects the failed test
(clear or set appropriate test_* fields), and ensure any test-all tracking state
(e.g., testAllFailed/testAllProgress flags) is incremented/updated the same way
as in the catch block; apply the same check-and-failure handling to the other
handler around lines 208-219.
admin/handler.go (1)

723-786: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Make this scheduler PATCH atomic before touching runtime state.

Once UpdateAccountSchedulerConfig and the early ApplyAccount* calls succeed, a later failure in UpdateAccountTags, SetAccountGroups, or UpdateAccountProxyURL returns 500 after part of the DB/runtime state has already changed. Wrap the DB writes in one transaction and only apply runtime mutations after every write succeeds.

🤖 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 `@admin/handler.go` around lines 723 - 786, The DB updates
(UpdateAccountSchedulerConfig, UpdateAccountTags, SetAccountGroups,
UpdateAccountProxyURL) must be executed inside a single transaction and only
after the transaction commits should you call runtime mutations
(store.ApplyAccountSchedulerOverrides, ApplyAccountAllowedAPIKeys,
ApplyAccountTags, ApplyAccountGroups, ApplyAccountProxyURL); change the handler
to begin a transaction, perform all Update*/Set* calls using that tx (or a
transactional wrapper on h.db), rollback on any error (returning the appropriate
500/404), and only after a successful commit invoke the corresponding
h.store.Apply* methods in the same order so runtime state is updated atomically
with the DB.
🧹 Nitpick comments (3)
database/helpers.go (1)

376-386: 💤 Low value

Consider logging marshal errors for observability.

The encode functions silently return "[]" when json.Marshal fails. While marshaling []int64 or []string should rarely fail in practice, logging these errors would help with debugging and monitoring.

Optional: Add error logging
 func encodeInt64SliceJSON(values []int64) string {
 	if len(values) == 0 {
 		return "[]"
 	}
 	b, err := json.Marshal(values)
 	if err != nil {
+		// Consider: log.Printf("failed to marshal int64 slice: %v", err)
 		return "[]"
 	}
 	return string(b)
 }

Similar change for encodeTagsJSON.

Also applies to: 401-411

🤖 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 `@database/helpers.go` around lines 376 - 386, The encodeInt64SliceJSON (and
similarly encodeTagsJSON) functions currently swallow json.Marshal errors and
return "[]"; update them to log the marshal error for observability while
preserving the existing return behavior — add a log (e.g., using the package
logger or log.Printf) in the err branch of encodeInt64SliceJSON and
encodeTagsJSON to record the error and the input value context, then still
return "[]". Ensure you import the chosen logger (or "log") if not already
present.
frontend/src/hooks/useHighlighter.ts (2)

42-79: ⚖️ Poor tradeoff

Note: Theme changes require component re-render.

The effect reads document.documentElement.classList.contains('dark') to determine the theme, but doesn't include theme changes in the dependency array. If the theme switches while the component remains mounted with the same code and lang, the highlighted output won't update until the component re-renders for another reason.

This is likely acceptable behavior since theme changes typically cause broader re-renders, but it's worth documenting if this becomes a user-facing issue.

🤖 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 `@frontend/src/hooks/useHighlighter.ts` around lines 42 - 79, The effect in
useHighlighter.ts reads the DOM theme
(document.documentElement.classList.contains('dark')) but doesn't react to theme
changes, so update the useEffect that wraps getHighlighter()/hl.codeToHtml to
either (A) include a reactive theme signal in its dependency array (derive
isDark into state/prop and add that dependency) or (B) attach a MutationObserver
inside the hook to update a local isDark state when the root class changes and
use that state in the effect; ensure you reference the same cacheKey logic
(cacheKey constructed from isDark, resolvedLang, code), keep cache eviction
using htmlCache and MAX_CACHE_SIZE, and run tuneLightTheme only when
appropriate, and clean up the observer on unmount as well as maintaining the
cancelled flag and setHtml usage.

58-76: 💤 Low value

Consider adding error logging for debugging.

The catch block at line 73 silently sets the HTML to an empty string when highlighting fails. Adding a console.warn or console.error would help developers troubleshoot syntax highlighting issues.

Optional: Add error logging
       } catch {
+        console.warn('Syntax highlighting failed:', { code, resolvedLang })
         setHtml('')
       }
🤖 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 `@frontend/src/hooks/useHighlighter.ts` around lines 58 - 76, The catch block
in the getHighlighter().then(...) call silently swallows highlighting errors;
update the catch to accept the error (e.g., catch (err)) and log it with context
before continuing (use console.warn or a project logger). Include identifying
context such as resolvedLang, isDark, the computed cacheKey (or code length) and
the error object when calling the logger, but keep the existing fallback
behavior of setHtml('') so failures still render safely; modify the catch
associated with getHighlighter, tuneLightTheme, htmlCache and setHtml
accordingly.
🤖 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 `@admin/account_groups.go`:
- Around line 160-166: Replace direct equality checks against sql.ErrNoRows with
errors.Is so wrapped errors are detected: in the UpdateAccountGroup and
DeleteAccountGroup handlers (where UpdateAccountGroup and DeleteAccountGroup are
called) change the conditional from `if err == sql.ErrNoRows` to `if
errors.Is(err, sql.ErrNoRows)` and apply the identical change for the other
handler so not-found errors return HTTP 404 instead of falling through to 500.

In `@admin/handler.go`:
- Around line 3935-3940: Move the call to h.store.SetProxyPoolEnabled so it only
runs after h.store.ReloadProxyPool succeeds: when req.ProxyPoolEnabled is true,
first call h.store.ReloadProxyPool() and return the 500 on error, and only then
call h.store.SetProxyPoolEnabled(true) (and persist the setting if applicable);
when disabling (false) you can set it immediately. This ensures the runtime
state (SetProxyPoolEnabled) is not enabled unless ReloadProxyPool succeeds and
the settings row is persisted; update the handler logic around
SetProxyPoolEnabled, ReloadProxyPool, and req.ProxyPoolEnabled accordingly.
- Around line 4755-4765: The handler currently commits DB changes via
h.db.InsertProxies (and similar DB mutators at the other locations) and then
treats h.store.ReloadProxyPool failures as fatal, returning 500 even though the
DB change succeeded; change this so the HTTP response reflects the DB success:
after calling h.db.InsertProxies (and the analogous update/delete functions at
the other ranges) return success to the client when the DB call succeeds, and
handle ReloadProxyPool failures non-fatally by logging the error (using
processLogger or the existing logger) and optionally scheduling/asynchronously
retrying ReloadProxyPool, but do not call writeError or return 500; update the
blocks referencing h.store.ReloadProxyPool (including the ones around
InsertProxies and the other noted ranges) to implement this pattern.

In `@auth/store.go`:
- Around line 3081-3093: SetAPIKeyAllowedGroups and LoadAPIKeyAllowedGroups are
storing raw IDs which can include non-positive values, duplicates, and unsorted
slices; add a normalization step that filters out <=0 IDs, deduplicates and
sorts the remaining int64s before storing into s.apiKeyAllowedGroups (under
s.apiKeyGroupsMu). Implement a small helper (e.g., normalizeInt64IDs) and call
it from SetAPIKeyAllowedGroups and LoadAPIKeyAllowedGroups instead of using
cloneInt64Slice directly so s.apiKeyAllowedGroups[apiKeyID] always contains a
sorted, unique, positive slice for consistent runtime comparisons.
- Around line 3127-3147: APIKeyAllowsAccount is allocating a new map on every
call; instead cache the allowed-group set per API key (or return an immutable
slice to avoid remapping) so hot-path checks don't reallocate. Modify
GetAPIKeyAllowedGroups to provide either a pointer to a precomputed []int64 or
expose a cached map[apiKeyID]map[groupID]struct{} on the Store (e.g.
store.apiKeyAllowedSets) that is built/updated when API-key groups change, and
then change APIKeyAllowsAccount to look up that cached set (using it directly
for membership tests) rather than rebuilding allowedSet on each call; keep the
existing nil/zero checks and account lock usage (Account.mu) intact.

In `@docs/API.md`:
- Around line 984-987: The API docs for account group-scoped scheduling are
missing new scheduler metadata fields; update the PATCH
/api/admin/accounts/:id/scheduler endpoint documentation to include the new
request/response schema fields proxy_url, tags, and group_ids (and describe
their types and semantics), ensure examples and parameter lists reference these
fields, and mention any validation rules or defaults used by the
scheduler-related handlers (e.g., allowed values for tags, format for proxy_url,
and semantics of group_ids) so the frontend/integrators have a complete
contract.

In `@frontend/src/components/ChipInput.tsx`:
- Around line 72-79: The bug is that handleChange splits on commas and calls
addChip multiple times while closing over a stale draft/value, causing chips to
be lost; fix by updating state and adding chips using functional updaters so
each addition sees the latest state: in handleChange, when v.includes(',')
iterate trimmed parts, call addChip (or the underlying setChips) using its
functional form (e.g., setChips(prev => [...prev, partTrimmed])) for each part,
then call setDraft(lastPart) (or setDraft(() => lastPart)) — ensure addChip
either delegates to the functional setChips or is changed to accept/apply the
functional updater so no closure over the old draft occurs.

In `@frontend/src/pages/Accounts.tsx`:
- Around line 1451-1455: After deleting a group you refresh groups but not
accounts, leaving account.group_ids containing the deleted id; update the flow
in the deletion handler (around showToast, setEditGroupIds, setGroupFilter,
resetGroupDraft, reloadGroups) to also refresh accounts by calling await
reloadAccounts() after await reloadGroups(), or alternatively update the local
accounts state by mapping over accounts and removing the deleted group.id from
each account.group_ids so editors and subsequent updateAccountScheduler calls
see fresh/valid data.

In `@frontend/src/pages/APIKeys.tsx`:
- Around line 758-760: The badge currently shows "none" when allowed_group_ids
is an empty array (ids variable), but an empty array means "no restriction" (all
groups). Update the render logic in the APIKeys component where ids is used (the
if (ids.length === 0) branch) to display the "all groups" translation instead of
'apiKeys.allowedGroupsNone' — e.g., use t('apiKeys.allowedGroupsAll') or an
equivalent "all" label (with fallback to a plain "All groups" string) so the UI
reflects the backend semantics.

In `@frontend/src/pages/Docs.tsx`:
- Around line 189-203: The new hardcoded Chinese strings in Docs.tsx (e.g., the
button labels '已复制', '复制链接', '打开客户端', and the expand button text using expanded
and setExpanded) must be replaced with localized strings from the app's
i18n/translation utility (e.g., use the project's t(...) or useTranslation hook)
so /docs is fully translatable; update the Button labels and the expand toggle
to call the translation function (create keys like docs.copy.copied,
docs.copy.copyLink, docs.openClient, docs.viewFullLink and
docs.collapseFullLink) and apply the same change to the other occurrences around
the file (also check the region noted 780-897) so no UI text is hardcoded.
- Around line 1071-1085: The admin EndpointDoc components are being seeded with
the downstream API key (activeKey) and full allKeys list, causing admin requests
to prefill with the wrong credential type; in the adminEndpoints map, find the
admin credential from allKeys (e.g., const adminSeed = allKeys.find(k => k.type
=== 'admin') or filter allKeys to type 'admin') and pass that as the apiKey prop
(and replace allKeys prop with the filtered admin-only list if needed) when
rendering EndpointDoc so the dialog pre-fills X-Admin-Key with an admin
credential instead of a downstream API key.

In `@frontend/src/pages/docs/EndpointDoc.tsx`:
- Around line 63-74: The button labels and tooltip strings (e.g., the aria-label
values '已复制' and '复制' used near the handleCopy call and the conditional icon
render in EndpointDoc.tsx, and the similar hardcoded Chinese strings between
lines 226-281) must be moved to the app i18n system: replace the hardcoded
Chinese text with translation keys and call the project's existing translation
helper (e.g., the t function or useTranslation hook used elsewhere) to render
localized strings (e.g., t('docs.copy'), t('docs.copied')), import/use that
helper in EndpointDoc, and update any referenced keys in the locale files for
both Chinese and English so the labels (aria-label and visible text) are
localized across locales.

In `@frontend/src/pages/docs/quickStartTools.ts`:
- Around line 140-143: The current placeholder substitution uses
base.split('{address}').join(address).split('{key}').join(key) which injects raw
values and can break URLs; update the substitution to URL-encode the dynamic
values first (use encodeURIComponent on address and key) and then replace
'{address}' and '{key}' in base so the generated protocol/query links remain
valid; locate the substitution logic in the function that returns this base
string and replace the join arguments with the encoded variables.

In `@frontend/src/pages/Guide.tsx`:
- Around line 29-54: The client tools array CLIENT_TOOLS in Guide.tsx currently
contains hardcoded user-facing strings (id, name, badge, blurb, glyph, tone);
replace those literal labels with locale keys and call the i18n translator
(e.g., t(...) or the project's i18n lookup) when rendering so the strings come
from locale resource keys (create keys like guide.client.codex.name,
guide.client.codex.blurb, guide.client.codex.badge, etc. and use them in the
component that renders CLIENT_TOOLS); apply the same change for all other
hardcoded copy in this file referenced by the reviewer (lines ~259-436) so
headings, labels and help text are routed through the same i18n keys and
translation function.

In `@frontend/src/pages/Proxies.tsx`:
- Around line 16-23: The batch-add flow is not using validateProxyInput(), so
malformed/unsupported proxy URLs get submitted; update the add/batch add handler
to run validateProxyInput(url) for each entered URL (reuse the same allowed
protocols: http, https, socks5, socks5h) and block submission if any URL fails
validation, surfacing per-item validation errors to the user instead of sending
to the server; ensure the same validation logic used in edit mode
(validateProxyInput) is invoked before the network request and returns a clear
client-side error for each invalid entry.

---

Outside diff comments:
In `@admin/handler.go`:
- Around line 723-786: The DB updates (UpdateAccountSchedulerConfig,
UpdateAccountTags, SetAccountGroups, UpdateAccountProxyURL) must be executed
inside a single transaction and only after the transaction commits should you
call runtime mutations (store.ApplyAccountSchedulerOverrides,
ApplyAccountAllowedAPIKeys, ApplyAccountTags, ApplyAccountGroups,
ApplyAccountProxyURL); change the handler to begin a transaction, perform all
Update*/Set* calls using that tx (or a transactional wrapper on h.db), rollback
on any error (returning the appropriate 500/404), and only after a successful
commit invoke the corresponding h.store.Apply* methods in the same order so
runtime state is updated atomically with the DB.

In `@database/postgres.go`:
- Around line 3139-3148: The SELECT that reads credentials (selectQuery)
currently only filters by id and thus can lock and update soft-deleted accounts;
change the query to include the same non-deleted predicate used in the
scheduler-update existence check (e.g., add "AND <non-deleted-predicate>" such
as "AND deleted_at IS NULL" or the project's equivalent) for both the SQLite and
non-SQLite branches, preserving the FOR UPDATE for non-SQLite; ensure the
parameter order passed to tx.QueryRowContext(ctx, selectQuery, id, ...) matches
the added predicate parameters and update references to selectQuery,
db.isSQLite(), tx.QueryRowContext and id accordingly.

In `@frontend/src/pages/Proxies.tsx`:
- Around line 179-189: The API handler currently only treats thrown errors as
failures; update the logic after calling api.testProxy(p.url, p.id, ipApiLang)
to explicitly handle result.success === false as a failure: when result.success
is false, call showToast with t('proxies.testFailed', { error: result.error ||
getErrorMessage(new Error('test failed')) }) (or similar), update setProxies so
the row reflects the failed test (clear or set appropriate test_* fields), and
ensure any test-all tracking state (e.g., testAllFailed/testAllProgress flags)
is incremented/updated the same way as in the catch block; apply the same
check-and-failure handling to the other handler around lines 208-219.

---

Nitpick comments:
In `@database/helpers.go`:
- Around line 376-386: The encodeInt64SliceJSON (and similarly encodeTagsJSON)
functions currently swallow json.Marshal errors and return "[]"; update them to
log the marshal error for observability while preserving the existing return
behavior — add a log (e.g., using the package logger or log.Printf) in the err
branch of encodeInt64SliceJSON and encodeTagsJSON to record the error and the
input value context, then still return "[]". Ensure you import the chosen logger
(or "log") if not already present.

In `@frontend/src/hooks/useHighlighter.ts`:
- Around line 42-79: The effect in useHighlighter.ts reads the DOM theme
(document.documentElement.classList.contains('dark')) but doesn't react to theme
changes, so update the useEffect that wraps getHighlighter()/hl.codeToHtml to
either (A) include a reactive theme signal in its dependency array (derive
isDark into state/prop and add that dependency) or (B) attach a MutationObserver
inside the hook to update a local isDark state when the root class changes and
use that state in the effect; ensure you reference the same cacheKey logic
(cacheKey constructed from isDark, resolvedLang, code), keep cache eviction
using htmlCache and MAX_CACHE_SIZE, and run tuneLightTheme only when
appropriate, and clean up the observer on unmount as well as maintaining the
cancelled flag and setHtml usage.
- Around line 58-76: The catch block in the getHighlighter().then(...) call
silently swallows highlighting errors; update the catch to accept the error
(e.g., catch (err)) and log it with context before continuing (use console.warn
or a project logger). Include identifying context such as resolvedLang, isDark,
the computed cacheKey (or code length) and the error object when calling the
logger, but keep the existing fallback behavior of setHtml('') so failures still
render safely; modify the catch associated with getHighlighter, tuneLightTheme,
htmlCache and setHtml accordingly.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 19aeb195-df70-40bb-a0b6-8b5d23668c49

📥 Commits

Reviewing files that changed from the base of the PR and between 71d0ee5 and 28d317c.

⛔ Files ignored due to path filters (1)
  • frontend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (40)
  • admin/account_groups.go
  • admin/handler.go
  • admin/handler_test.go
  • admin/responses.go
  • auth/fast_scheduler.go
  • auth/fast_scheduler_test.go
  • auth/session_affinity_test.go
  • auth/store.go
  • database/account_groups.go
  • database/helpers.go
  • database/image_studio.go
  • database/postgres.go
  • database/sqlite.go
  • database/sqlite_test.go
  • docs/API.md
  • frontend/package.json
  • frontend/src/App.tsx
  • frontend/src/api.ts
  • frontend/src/components/ChipInput.tsx
  • frontend/src/components/Layout.tsx
  • frontend/src/components/PageHeader.tsx
  • frontend/src/components/UsageStatsSummary.tsx
  • frontend/src/components/ui/card.tsx
  • frontend/src/hooks/useHighlighter.ts
  • frontend/src/index.css
  • frontend/src/locales/en.json
  • frontend/src/locales/zh.json
  • frontend/src/pages/APIKeys.tsx
  • frontend/src/pages/Accounts.tsx
  • frontend/src/pages/Dashboard.tsx
  • frontend/src/pages/Docs.tsx
  • frontend/src/pages/Guide.tsx
  • frontend/src/pages/Proxies.tsx
  • frontend/src/pages/Usage.tsx
  • frontend/src/pages/docs/DocsTOC.tsx
  • frontend/src/pages/docs/EndpointDoc.tsx
  • frontend/src/pages/docs/docsContent.ts
  • frontend/src/pages/docs/quickStartTools.ts
  • frontend/src/types.ts
  • proxy/handler.go

Comment thread admin/account_groups.go
Comment thread admin/handler.go Outdated
Comment thread admin/handler.go
Comment thread auth/store.go
Comment thread auth/store.go
Comment thread frontend/src/pages/Docs.tsx
Comment thread frontend/src/pages/docs/EndpointDoc.tsx Outdated
Comment thread frontend/src/pages/docs/quickStartTools.ts
Comment on lines +29 to +54
const CLIENT_TOOLS: ClientTool[] = [
{
id: 'codex',
name: 'Codex CLI',
badge: 'Responses',
blurb: 'Write config.toml and auth.json for the OpenAI Responses wire API.',
glyph: 'CX',
tone: 'bg-emerald-500/10 text-emerald-600 dark:text-emerald-400',
},
{
id: 'claude',
name: 'Claude Code',
badge: 'Anthropic',
blurb: 'Use environment variables or settings.json for the Messages endpoint.',
glyph: 'CC',
tone: 'bg-orange-500/10 text-orange-600 dark:text-orange-400',
},
{
id: 'cc-switch',
name: 'CC Switch',
badge: 'Deeplink',
blurb: 'Launch the desktop switcher and import this server as a provider.',
glyph: 'CS',
tone: 'bg-fuchsia-500/10 text-fuchsia-600 dark:text-fuchsia-400',
},
]
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Reintroduce i18n binding for Guide copy.

The page now hardcodes user-facing strings, which regresses localization support (especially Chinese) across this admin flow. Please route labels/headings/help text back through locale keys.

Also applies to: 259-436

🤖 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 `@frontend/src/pages/Guide.tsx` around lines 29 - 54, The client tools array
CLIENT_TOOLS in Guide.tsx currently contains hardcoded user-facing strings (id,
name, badge, blurb, glyph, tone); replace those literal labels with locale keys
and call the i18n translator (e.g., t(...) or the project's i18n lookup) when
rendering so the strings come from locale resource keys (create keys like
guide.client.codex.name, guide.client.codex.blurb, guide.client.codex.badge,
etc. and use them in the component that renders CLIENT_TOOLS); apply the same
change for all other hardcoded copy in this file referenced by the reviewer
(lines ~259-436) so headings, labels and help text are routed through the same
i18n keys and translation function.

Comment thread frontend/src/pages/Proxies.tsx
@DeliciousBuding DeliciousBuding marked this pull request as draft May 13, 2026 07:36
@DeliciousBuding DeliciousBuding changed the title 完善 API Key 与账号分组管理 重构文档中心并完善账号与 API Key 管理 May 13, 2026
@DeliciousBuding DeliciousBuding marked this pull request as ready for review May 13, 2026 09:40
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
docs/API.md (1)

1242-1272: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

JSON 示例里包含行内注释,示例不可直接使用。

"id": 1, // 可选... 不是合法 JSON;复制后会解析失败。建议把注释放到代码块外。

🛠️ Suggested fix
 {
   "url": "http://proxy.example.com:8080",
-  "id": 1, // 可选,用于持久化测试结果
+  "id": 1,
   "lang": "zh-CN"
 }

在代码块下补一句说明:id 可选,用于持久化测试结果。

🤖 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 `@docs/API.md` around lines 1242 - 1272, The JSON example for the POST
/api/admin/proxies/test endpoint contains an inline comment inside the code
block ("id": 1, // 可选...), which is invalid JSON; remove the inline comment from
the code block and add a plain-text note immediately below the block stating
that the id field in the POST /api/admin/proxies/test request is optional and is
used to persist test results (e.g., "注意:id 为可选字段,用于持久化测试结果。"). Ensure the code
block only contains valid JSON and the explanatory sentence references the "id"
field and the POST /api/admin/proxies/test endpoint.
🤖 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 `@docs/API.md`:
- Around line 653-657: 两个缺少语言标记的 fenced code block(内容分别以 "rt_xxxxxx1 /
rt_xxxxxx2 / rt_xxxxxx3" 开头和以 "data: {\"type\":\"progress\"...}" / "data:
{\"type\":\"complete\"...}" 开头)触发 MD040 lint 错误;为这两个 code block 在起始 ```
后添加语言标记(例如 ```text)以标明内容类型并修复 lint 报错,保持原有块内内容不变并对文档中相同的另一个块(内容如上)做相同修改。

In `@frontend/src/hooks/useHighlighter.ts`:
- Around line 8-29: getHighlighter currently sets a singleton highlighterPromise
which, if the dynamic imports fail, will remain rejected and "poison" future
calls; modify getHighlighter (and the similar logic at lines 78-103) to attach a
.catch handler to the Promise that resets highlighterPromise to null (or
undefined) before rethrowing the error so subsequent calls can retry
initialization, and ensure the original error is propagated after resetting the
singleton.

In `@frontend/src/pages/docs/EndpointDoc.tsx`:
- Around line 472-493: The Try-it UI should be disabled for endpoints with path
parameters or non-JSON payloads; update the render logic around the Button and
TryItDialog (symbols: Button, setTryOpen, TryItDialog, method, path,
defaultBody, apiKey, baseUrl, allKeys) to only show them when the path contains
no ":" (i.e., !path.includes(':')) and the payload is JSON-compatible for
methods that accept bodies (implement a small isJson check that attempts
JSON.parse on defaultBody and only allow for POST/PUT/PATCH when parse succeeds,
while allowing GET/DELETE with empty/defaultBody); wrap both the Button and
TryItDialog in that guard so the dialog never submits unsupported baseUrl+path
or non-JSON payloads.

---

Outside diff comments:
In `@docs/API.md`:
- Around line 1242-1272: The JSON example for the POST /api/admin/proxies/test
endpoint contains an inline comment inside the code block ("id": 1, // 可选...),
which is invalid JSON; remove the inline comment from the code block and add a
plain-text note immediately below the block stating that the id field in the
POST /api/admin/proxies/test request is optional and is used to persist test
results (e.g., "注意:id 为可选字段,用于持久化测试结果。"). Ensure the code block only contains
valid JSON and the explanatory sentence references the "id" field and the POST
/api/admin/proxies/test endpoint.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 02266c09-7018-46f4-ac33-70333ee98985

📥 Commits

Reviewing files that changed from the base of the PR and between 28d317c and 647f833.

📒 Files selected for processing (19)
  • admin/account_groups.go
  • admin/handler.go
  • auth/store.go
  • database/account_groups.go
  • database/postgres.go
  • database/sqlite.go
  • database/sqlite_test.go
  • docs/API.md
  • frontend/src/components/ChipInput.tsx
  • frontend/src/hooks/useHighlighter.ts
  • frontend/src/locales/en.json
  • frontend/src/locales/zh.json
  • frontend/src/pages/APIKeys.tsx
  • frontend/src/pages/Accounts.tsx
  • frontend/src/pages/Docs.tsx
  • frontend/src/pages/Proxies.tsx
  • frontend/src/pages/docs/EndpointDoc.tsx
  • frontend/src/pages/docs/docsContent.ts
  • frontend/src/pages/docs/quickStartTools.ts
✅ Files skipped from review due to trivial changes (1)
  • frontend/src/locales/zh.json
🚧 Files skipped from review as they are similar to previous changes (6)
  • frontend/src/components/ChipInput.tsx
  • auth/store.go
  • admin/account_groups.go
  • admin/handler.go
  • frontend/src/pages/Proxies.tsx
  • frontend/src/pages/APIKeys.tsx

Comment thread docs/API.md Outdated
Comment thread frontend/src/hooks/useHighlighter.ts
Comment thread frontend/src/pages/docs/EndpointDoc.tsx
@DeliciousBuding DeliciousBuding changed the title 重构文档中心并完善账号与 API Key 管理 重构文档中心、账号分组与 API Key 权限管理 May 13, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
frontend/src/pages/docs/EndpointDoc.tsx (2)

213-254: ⚡ Quick win

Abort in-flight Try-it requests on close/resend to prevent late state writes.

handleSend issues fetch without an AbortController. If the user closes the dialog mid-request or clicks Send again before the previous call resolves, the prior promise still runs to completion and writes status/response/duration after the dialog has unmounted or for a stale invocation, producing flicker and out-of-order updates in the response panel. Tying a controller into both the open-effect cleanup and the start of each send avoids those races without changing the happy path.

♻️ Suggested cleanup with AbortController
   const [duration, setDuration] = useState<number | null>(null);
+  const abortRef = useRef<AbortController | null>(null);

   useEffect(() => {
     if (open) {
       setBody(defaultBody);
       setToken(apiKey);
       setResponse("");
       setStatus(null);
       setDuration(null);
     }
+    return () => {
+      abortRef.current?.abort();
+      abortRef.current = null;
+    };
   }, [open, defaultBody, apiKey]);

   const handleSend = async () => {
+    abortRef.current?.abort();
+    const controller = new AbortController();
+    abortRef.current = controller;
     setLoading(true);
     ...
       const res = await fetch(url, {
         method,
         headers,
         body: isGet ? undefined : body.trim() || undefined,
+        signal: controller.signal,
       });
       ...
     } catch (e) {
+      if (controller.signal.aborted) return;
       setDuration(Math.round(performance.now() - start));
       setResponse(`Error: ${e instanceof Error ? e.message : String(e)}`);
     } finally {
-      setLoading(false);
+      if (!controller.signal.aborted) setLoading(false);
     }
   };

useRef is already imported at Line 1.

🤖 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 `@frontend/src/pages/docs/EndpointDoc.tsx` around lines 213 - 254, handleSend
currently calls fetch without an AbortController, causing previous requests to
update state after unmount or when a newer send starts; create a controller
stored in a ref (e.g., requestControllerRef) and before starting a new request
abort any existing controller and replace it, pass controller.signal to fetch in
handleSend, and ensure the dialog/component cleanup effect aborts the active
controller on unmount; additionally, when catching errors inside handleSend,
treat an AbortError (or e.name === "AbortError") as a no-op so you don't call
setStatus/setResponse for aborted requests, and clear the controller ref after
the request completes.

103-114: 💤 Low value

Styling check uses lang instead of the resolved language.

resolvedLang (Line 24–30) derives the highlight language from label when lang is omitted, but the conditional that switches between text-[13px] and text-sm still gates on the raw lang prop. A block invoked as <CodeBlock label="x.json" content={...} /> is highlighted as JSON but rendered with the bash-sized font, producing a subtle visual mismatch in the response examples that already pass lang separately.

♻️ Use `resolvedLang` for size selection
       {highlightedHtml ? (
         <div
-          className={`code-panel-pre shiki-wrapper ${lang === "json" ? "text-[13px]" : "text-sm"}`}
+          className={`code-panel-pre shiki-wrapper ${resolvedLang === "json" ? "text-[13px]" : "text-sm"}`}
           dangerouslySetInnerHTML={{ __html: highlightedHtml }}
         />
       ) : (
         <pre
-          className={`code-panel-pre ${lang === "json" ? "text-[13px]" : "text-sm"}`}
+          className={`code-panel-pre ${resolvedLang === "json" ? "text-[13px]" : "text-sm"}`}
         >
           <code>{content}</code>
         </pre>
       )}
🤖 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 `@frontend/src/pages/docs/EndpointDoc.tsx` around lines 103 - 114, The
font-size conditional is using the raw prop lang instead of the computed
resolvedLang (which may derive from label), so update the className ternaries in
both branches that render highlightedHtml (the div with dangerouslySetInnerHTML)
and the fallback <pre><code> branch to use resolvedLang instead of lang; locate
the resolvedLang variable (lines ~24–30) and swap the conditionals that choose
"text-[13px]" vs "text-sm" to base on resolvedLang so JSON-labelled content gets
the correct size when highlighted.
🤖 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.

Nitpick comments:
In `@frontend/src/pages/docs/EndpointDoc.tsx`:
- Around line 213-254: handleSend currently calls fetch without an
AbortController, causing previous requests to update state after unmount or when
a newer send starts; create a controller stored in a ref (e.g.,
requestControllerRef) and before starting a new request abort any existing
controller and replace it, pass controller.signal to fetch in handleSend, and
ensure the dialog/component cleanup effect aborts the active controller on
unmount; additionally, when catching errors inside handleSend, treat an
AbortError (or e.name === "AbortError") as a no-op so you don't call
setStatus/setResponse for aborted requests, and clear the controller ref after
the request completes.
- Around line 103-114: The font-size conditional is using the raw prop lang
instead of the computed resolvedLang (which may derive from label), so update
the className ternaries in both branches that render highlightedHtml (the div
with dangerouslySetInnerHTML) and the fallback <pre><code> branch to use
resolvedLang instead of lang; locate the resolvedLang variable (lines ~24–30)
and swap the conditionals that choose "text-[13px]" vs "text-sm" to base on
resolvedLang so JSON-labelled content gets the correct size when highlighted.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d9694e25-8178-4427-9f37-d3b4883626a2

📥 Commits

Reviewing files that changed from the base of the PR and between 647f833 and fb405c5.

📒 Files selected for processing (9)
  • database/account_groups.go
  • database/sqlite_test.go
  • docs/API.md
  • frontend/src/hooks/useHighlighter.ts
  • frontend/src/pages/Docs.tsx
  • frontend/src/pages/Proxies.tsx
  • frontend/src/pages/docs/EndpointDoc.tsx
  • frontend/src/pages/docs/docsContent.ts
  • frontend/src/pages/docs/quickStartTools.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • frontend/src/hooks/useHighlighter.ts
  • frontend/src/pages/Docs.tsx
  • frontend/src/pages/docs/quickStartTools.ts
  • frontend/src/pages/Proxies.tsx
  • database/sqlite_test.go

@james-6-23
Copy link
Copy Markdown
Owner

真不错啊哈哈哈

Replaces direct == comparison with errors.Is to correctly handle
wrapped errors from database drivers.
@james-6-23 james-6-23 merged commit 507b0b1 into james-6-23:main May 13, 2026
5 of 6 checks passed
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.

3 participants