From 8dc372242e1d0e6ab651ad2a670c46eff79f8f24 Mon Sep 17 00:00:00 2001 From: jiahao6635 Date: Mon, 17 Aug 2026 15:42:51 +0800 Subject: [PATCH] fix(dashboard): hide update action without permission --- CHANGELOG.md | 1 + .../src/components/AvatarDropdown.test.tsx | 53 +++++++++++++++++++ dashboard/src/components/AvatarDropdown.tsx | 25 +++++---- 3 files changed, 68 insertions(+), 11 deletions(-) create mode 100644 dashboard/src/components/AvatarDropdown.test.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f2a3ba4..aacecb0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ### 修复 +- 隐藏无应用更新权限用户头像菜单中的“检查更新”入口 - 插件工具使用中文等非 ASCII 名称时 LLM 调用失败:主流 API 要求工具名匹配 `^[a-zA-Z0-9_-]{1,64}$`,现自动将非法名称转写为合法拼音名(`pypinyin` 缺失时退回下划线替换),冲突追加 `_2`/`_3` 后缀,并在工具描述前缀 `[原名: …]` 保留原名映射;`config_json.plugins` 配置键与插件内部仍使用原始名称,路由不受影响 - 修复聊天页在"生成中"时于输入框持续打字导致消息列表上下轻微抖动的问题:输入框高度测量改为在离屏克隆节点上进行,不再瞬态改变页面布局 diff --git a/dashboard/src/components/AvatarDropdown.test.tsx b/dashboard/src/components/AvatarDropdown.test.tsx new file mode 100644 index 00000000..5731f6d0 --- /dev/null +++ b/dashboard/src/components/AvatarDropdown.test.tsx @@ -0,0 +1,53 @@ +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { MemoryRouter } from "react-router-dom"; +import { describe, expect, it, vi } from "vitest"; +import type { OctopUser } from "../api/modules/auth"; +import { CurrentUserProvider } from "../hooks/useCurrentUser"; +import AvatarDropdown from "./AvatarDropdown"; + +function buildUser( + role: OctopUser["role"], + permissions: string[] = [], +): OctopUser { + return { + id: 1, + username: role, + role, + display_name: role === "admin" ? "Admin User" : "Regular User", + locale: "en", + permissions, + }; +} + +async function openAccountMenu(user: OctopUser) { + const pointer = userEvent.setup(); + render( + + + + + , + ); + + await pointer.click( + screen.getByRole("button", { name: new RegExp(user.display_name!) }), + ); +} + +describe(" update access", () => { + it("hides Check for updates from users without update permission", async () => { + await openAccountMenu(buildUser("user")); + + expect(screen.queryByText("account.checkUpdates")).not.toBeInTheDocument(); + }); + + it.each([ + ["an admin", buildUser("admin")], + ["an explicitly authorized user", buildUser("user", ["update"])], + ])("shows Check for updates to %s", async (_label, user) => { + await openAccountMenu(user); + + expect(await screen.findByText("account.checkUpdates")).toBeInTheDocument(); + }); +}); diff --git a/dashboard/src/components/AvatarDropdown.tsx b/dashboard/src/components/AvatarDropdown.tsx index c1095955..319f23ed 100644 --- a/dashboard/src/components/AvatarDropdown.tsx +++ b/dashboard/src/components/AvatarDropdown.tsx @@ -32,6 +32,7 @@ import { preferencesApi } from "../api/modules/preferences"; import { clearAuthToken } from "../api/request"; import { applyGuestLocale, applyUserLocale } from "../utils/locale"; import { apiErrorMessage } from "../utils/apiError"; +import { userCan } from "../utils/permissions"; import { MIN_PASSWORD_LENGTH, passwordPolicyIssue, @@ -256,17 +257,19 @@ export default function AvatarDropdown({ {t("account.changePassword")} - + {userCan(user, "update") && ( + + )}