Skip to content

fix(profile): stop regenerating the access token when the dialog opens - #6726

Closed
ENCHIGO wants to merge 1 commit into
QuantumNous:mainfrom
ENCHIGO:fix/access-token-no-auto-regenerate
Closed

fix(profile): stop regenerating the access token when the dialog opens#6726
ENCHIGO wants to merge 1 commit into
QuantumNous:mainfrom
ENCHIGO:fix/access-token-no-auto-regenerate

Conversation

@ENCHIGO

@ENCHIGO ENCHIGO commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

⚠️ 提交说明 / PR Notice

Important

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

📝 变更描述 / Description

「访问令牌」弹窗在打开时会自动重新生成令牌:

useEffect(() => {
  if (open && !token) {
    generate()
  }
}, [open, token, generate])

token 是组件内的 useState,每次挂载都是空字符串,所以只要弹窗打开,generate() 必然执行。而 generate() 会让服务端无条件覆盖旧令牌,且 User.AccessToken 的 json tag 是 json:"-",令牌只在生成那一刻返回一次、事后无法读回 —— 也就是说这次误触不可撤销、原值也找不回来

结果是:用户只想点开看一眼,所有仍持有旧令牌的集成就全部失效了。卡片文案「生成和管理您的 API 访问令牌」也强化了"这是个查看入口"的误解。

本 PR 把这个副作用去掉,并让销毁性操作变成需要明确意图的动作:

  1. 移除自动生成的 useEffect
  2. 未持有令牌时展示说明文案,讲清楚"现有令牌无法再次显示、重新生成会立即使其失效",而不是给一个空输入框
  3. 「重新生成」改为先弹二次确认(复用现有 ConfirmDialogdestructive 变体),确认文案说明后果
  4. 生成成功后的展示与复制行为保持不变,并补充"仅显示一次"的提示

未改动任何服务端逻辑与接口契约。

🚀 变更类型 / Type of change

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

🔗 关联任务 / Related Issue

✅ 提交前检查项 / Checklist

  • 人工确认: 我已亲自整理并撰写此描述,没有直接粘贴未经处理的 AI 输出。
    • 说明:本 PR 由 AI 协助完成,描述与代码均经我逐行复核后提交。为如实告知,此项不勾选。
  • 非重复提交: 我已搜索现有的 IssuesPRs,确认不是重复提交。
  • Bug fix 说明: 已提交并关联 Issue 打开「访问令牌」弹窗会立即重置令牌,无任何确认,旧令牌当场失效 #6725。这是"打开弹窗即产生不可逆副作用"的实现缺陷,不是设计取舍。
  • 变更理解: 仅影响个人设置页的访问令牌弹窗。服务端 POST /api/user/token 的行为未改,只是不再被自动触发。
  • 范围聚焦: 仅改动该弹窗组件与其新增文案对应的 7 个语言文件,无其他无关改动。
  • 本地验证: 已运行下方列出的检查。
  • 安全合规: 无凭据或敏感信息;新增文案已按 i18n:sync 流程补齐全部语言,未引入未翻译项。

📸 运行证明 / Proof of Work

$ bun run typecheck
$ tsgo -b
(无输出,退出码 0)

$ bunx oxlint -c .oxlintrc.json src/features/profile/components/dialogs/access-token-dialog.tsx
(无输出,退出码 0)

$ bun run build
Total: 57301.2 kB / 16638.6 kB(构建成功)

$ node scripts/sync-i18n.mjs
en     missing=0  untranslated=0
fr     missing=0  untranslated=0
ja     missing=0  untranslated=0
ru     missing=0  untranslated=0
vi     missing=0  untranslated=0
zh-TW  missing=0  untranslated=0
zh     missing=0  untranslated=0

新增 4 条文案已提供全部 7 个语言的译文,_sync-report.jsonmissingCountuntranslatedCount 保持为 0,与改动前一致。

行为对照

操作 改动前 改动后
打开弹窗 立即重置令牌,旧令牌失效 无副作用,展示说明文案
点击「重新生成」 直接重置 弹出二次确认,说明旧令牌将立即失效
确认重新生成 生成新令牌并展示、可复制,提示仅显示一次
关闭后重新打开 再次重置 无副作用

Summary by CodeRabbit

  • New Features

    • Access-token dialogs now show an explanatory empty state when no token exists.
    • Regenerating a token requires explicit confirmation and clearly warns that the current token will be invalidated.
    • Newly generated tokens are shown once, with loading states and copy controls.
    • Dialog state resets when closed.
  • Localization

    • Added regeneration and security warning translations across English, French, Japanese, Russian, Vietnamese, Simplified Chinese, and Traditional Chinese.

The access token dialog regenerated on mount: token is component state, so
it is always empty on open and the effect always fired. Opening the dialog
was therefore enough to replace the token server-side.

That is not recoverable. AccessToken is json:"-" on the user model, so the
value is returned once at creation and can never be read back, and the
server overwrites the old one unconditionally. A user who only wanted to
look at the card broke every integration still holding the old token.

Drop the auto-generate effect, explain up front that the existing token
cannot be shown again, and put the regeneration behind a destructive
confirmation that spells out the consequence.
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The access-token dialog no longer regenerates tokens on open. It now requires confirmation, shows token controls only when a token exists, and displays localized warnings about one-time visibility and invalidation.

Changes

Access Token Safety Flow

Layer / File(s) Summary
Token dialog confirmation flow
web/src/features/profile/components/dialogs/access-token-dialog.tsx
The dialog removes automatic generation, resets confirmation state when closed, adds destructive confirmation, disables regeneration while loading, and conditionally renders token controls or an empty-state explanation.
Regeneration warning translations
web/src/i18n/locales/{en,fr,ja,ru,vi,zh-TW,zh}.json
The locales add messages for regeneration confirmation, one-time token visibility, immediate invalidation, and required integration updates.

Estimated code review effort: 2 (Simple) | ~15 minutes

Poem

A rabbit taps the token door,
“Confirm before you make one more.”
The old key fades, the new shines bright,
Its secret shows just once in sight.
Local words now warn with care,
So no surprise awaits in there.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.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
Linked Issues check ✅ Passed The changes address issue #6725 by removing automatic generation, explaining unavailable tokens, and requiring confirmation before regeneration.
Out of Scope Changes check ✅ Passed The dialog updates and seven localization changes are directly related to the access-token regeneration fix.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main fix: preventing access-token regeneration when the dialog opens.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 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: 1

🧹 Nitpick comments (1)
web/src/features/profile/components/dialogs/access-token-dialog.tsx (1)

91-97: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Mark decorative icons as aria-hidden="true".

Loader2 and RefreshCw (lines 92 and 94) sit next to the descriptive label text ("Generating..." / "Regenerate"). KeyRound (line 131) sits next to the explanatory paragraph. All three icons are decorative.

As per coding guidelines, "装饰性图标使用 aria-hidden="true",重要信息提供文本等价".

♿️ Proposed fix
-              <Loader2 className='h-4 w-4 animate-spin' />
+              <Loader2 className='h-4 w-4 animate-spin' aria-hidden='true' />
             ) : (
-              <RefreshCw className='h-4 w-4' />
+              <RefreshCw className='h-4 w-4' aria-hidden='true' />
-              <KeyRound className='text-muted-foreground size-5' />
+              <KeyRound className='text-muted-foreground size-5' aria-hidden='true' />

Also applies to: 129-138

🤖 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/src/features/profile/components/dialogs/access-token-dialog.tsx` around
lines 91 - 97, Add aria-hidden="true" to the decorative Loader2 and RefreshCw
icons in the generating/regenerate button and the KeyRound icon in the
explanatory section of the access-token dialog, leaving their existing visual
styling and labels unchanged.

Source: Coding guidelines

🤖 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/src/features/profile/components/dialogs/access-token-dialog.tsx`:
- Around line 84-97: Update the access-token state handling around
useAccessToken and the regenerate Button so the first-generation empty state
uses the destructive variant, while a known existing token uses the default
variant. Base confirmation and empty-state messaging on whether a token is
present or the backend reports one exists, avoiding claims that regeneration
invalidates a current token when no token is known.

---

Nitpick comments:
In `@web/src/features/profile/components/dialogs/access-token-dialog.tsx`:
- Around line 91-97: Add aria-hidden="true" to the decorative Loader2 and
RefreshCw icons in the generating/regenerate button and the KeyRound icon in the
explanatory section of the access-token dialog, leaving their existing visual
styling and labels unchanged.
🪄 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: 9c6940d6-109a-48b3-8519-eabbc4e04640

📥 Commits

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

📒 Files selected for processing (8)
  • web/src/features/profile/components/dialogs/access-token-dialog.tsx
  • web/src/i18n/locales/en.json
  • web/src/i18n/locales/fr.json
  • web/src/i18n/locales/ja.json
  • web/src/i18n/locales/ru.json
  • web/src/i18n/locales/vi.json
  • web/src/i18n/locales/zh-TW.json
  • web/src/i18n/locales/zh.json

Comment on lines +84 to +97
<Button
type='button'
variant={token ? 'default' : 'destructive'}
onClick={() => setConfirmOpen(true)}
disabled={generating}
className='gap-2'
>
{generating ? (
<Loader2 className='h-4 w-4 animate-spin' />
) : (
<RefreshCw className='h-4 w-4' />
)}
{generating ? t('Generating...') : t('Regenerate')}
</Button>

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Files:"
fd -a 'access-token-dialog\.tsx|accessToken|useAccessToken' . | sed 's#^\./##' | head -200

echo
echo "Target file excerpt:"
cat -n web/src/features/profile/components/dialogs/access-token-dialog.tsx | sed -n '1,220p'

Repository: QuantumNous/new-api

Length of output: 6698


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Search access token usages and state:"
rg -n "useAccessToken|accessToken|accessTokenMutation|token" web/src -g '*.ts' -g '*.tsx' | head -300

Repository: QuantumNous/new-api

Length of output: 27609


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "use-access-token hook:"
cat -n web/src/features/profile/hooks/use-access-token.ts | sed -n '1,120p'

echo
echo "profile api token generation:"
cat -n web/src/features/profile/api.ts | sed -n '1,145p'

echo
echo "ConfirmDialog props/usages:"
fd -a 'confirm-dialog\.(tsx|ts)$' web/src | xargs -r cat -n | sed -n '1,220p'
rg -n "ConfirmDialog|destructive" web/src/components web/src/features -g '*.tsx' -g '*.ts' | head -120

echo
echo "profile context excerpt:"
cat -n web/src/features/profile/components/profile-security-card.tsx | sed -n '1,180p'

Repository: QuantumNous/new-api

Length of output: 45372


🌐 Web query:

i18next conditional pluralization ifelse zero one example

💡 Result:

In i18next, pluralization is handled via suffix-based naming conventions rather than explicit if-else logic [1][2]. The framework uses the count variable to determine which suffix to select based on the language's specific plural rules (powered by Intl.PluralRules) [1][3]. To handle specific cases like zero, one, and others, you define keys in your resource file with the appropriate suffixes [1]. Resource Example: { "key_zero": "No items", "key_one": "{{count}} item", "key_other": "{{count}} items" } Usage: i18next.t('key', { count: 0 }); // -> "No items" i18next.t('key', { count: 1 }); // -> "1 item" i18next.t('key', { count: 5 }); // -> "5 items" Key Concepts: - Suffix Resolution: i18next automatically appends suffixes like _zero, _one, or _other to your base key [1][4]. If a _zero entry is provided, i18next will prioritize it when count is 0, overriding the language's default plural rules [1][2]. - Intl.PluralRules: i18next relies on the native Intl.PluralRules API [1]. In environments without this support (e.g., some legacy environments or specific versions of React Native), you must use a polyfill [1][3]. - ICU Message Format: If you require more complex conditional logic (e.g., specific messages for non-plural numbers like 42 or 13), you can use ICU Message Format within your translation strings [2][5]. This allows for syntax like {count, plural, =0 {None} =42 {Answer} one {One} other {# items}} [2][5]. For simple pluralization, stick to the _one/_other or _zero/_one/_other suffix pattern as it is the standard, optimized approach for i18next [1][4].

Citations:


Fix the access token regenerate UI state and confirmation copy.

useAccessToken() starts with token === '' and never tracks whether a token was previously created, so the empty state is ambiguous. This causes the regenerate button to use the non-destructive 'default' variant when a token is present and the destructive 'destructive' variant at the first-generation state.

Also, useAccessToken() cannot distinguish “no token was ever created” from “a token exists but is hidden”, so the confirmation and empty-state copy should not claim that regenerating invalidates a current token. If token remains the only flag, use the absence of a token for the default confirm/empty messaging, and use destruction only when a token is known to exist or the backend reports an existing token.

🤖 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/src/features/profile/components/dialogs/access-token-dialog.tsx` around
lines 84 - 97, Update the access-token state handling around useAccessToken and
the regenerate Button so the first-generation empty state uses the destructive
variant, while a known existing token uses the default variant. Base
confirmation and empty-state messaging on whether a token is present or the
backend reports one exists, avoiding claims that regeneration invalidates a
current token when no token is known.

@ENCHIGO

ENCHIGO commented Aug 15, 2026

Copy link
Copy Markdown
Contributor Author

问题已由 #6749 解决,实现比本 PR 更完整——多了关闭弹窗时清理内存中的令牌,以及生成过程中拦截关闭。本 PR 不再需要,关闭。感谢采纳。

@ENCHIGO ENCHIGO closed this Aug 15, 2026
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