From 1170f4398837068f67b3ab5e783be9529b0d9974 Mon Sep 17 00:00:00 2001 From: PiEgg Date: Thu, 21 May 2026 20:33:46 +0800 Subject: [PATCH] feat(plugin-verification): migrate client to client-v2 (#9515) --- .../client-v2/src/PluginSettingsManager.ts | 3 +- .../__tests__/PluginSettingsManager.test.ts | 19 + .../src/__tests__/PoweredBy.test.tsx} | 31 +- .../nocobase-buildin-plugin-auth.test.tsx | 147 ++-- .../src/__tests__/useCurrentRoles.test.tsx | 100 +++ .../client-v2/src/components/PoweredBy.tsx | 71 ++ .../core/client-v2/src/components/README.md | 314 ++++++++ .../client-v2/src/components/README.zh-CN.md | 312 ++++++++ .../src}/components/SwitchLanguage.tsx | 30 +- .../src/components/form/DialogFormLayout.tsx | 111 +++ .../src/components/form/DrawerFormLayout.tsx | 45 +- .../src/components/form/PasswordInput.tsx | 211 ++++++ .../src/components/form/RemoteSelect.tsx | 137 ++++ .../client-v2/src/components/form/index.tsx | 3 + .../src/components/form/table/Table.tsx | 3 +- .../src/components/form/table/styles.ts | 19 + .../core/client-v2/src/components/index.ts | 2 + .../admin-layout/AdminLayoutComponent.tsx | 5 +- .../src/nocobase-buildin-plugin/index.tsx | 86 ++- packages/core/flow-engine/src/utils/index.ts | 3 + .../core/flow-engine/src/utils/randomId.ts | 48 ++ .../@nocobase/plugin-api-keys/client-v2.d.ts | 2 + .../@nocobase/plugin-api-keys/client-v2.js | 1 + .../@nocobase/plugin-api-keys/package.json | 2 + .../client-v2/__tests__/ApiKeysPage.test.ts | 111 +++ .../plugin-api-keys/src/client-v2/index.tsx | 10 + .../plugin-api-keys/src/client-v2/locale.ts | 21 + .../src/client-v2/pages/ApiKeysPage.tsx | 307 ++++++++ .../src/client-v2/pages/ExpiresField.tsx | 91 +++ .../plugin-api-keys/src/client-v2/plugin.tsx | 33 + .../@nocobase/plugin-auth-sms/client-v2.d.ts | 2 + .../@nocobase/plugin-auth-sms/client-v2.js | 1 + .../@nocobase/plugin-auth-sms/package.json | 2 + .../client-v2/__tests__/SmsSignInForm.test.ts | 57 ++ .../src/client-v2/forms/SmsAdminSettings.tsx | 44 ++ .../src/client-v2/forms/SmsSignInForm.tsx | 96 +++ .../plugin-auth-sms/src/client-v2/index.tsx | 10 + .../plugin-auth-sms/src/client-v2/locale.ts | 27 + .../plugin-auth-sms/src/client-v2/plugin.tsx | 24 + .../src/client-v2/__tests__/hooks.test.tsx | 128 +++- .../src/client-v2/__tests__/plugin.test.tsx | 73 +- .../client-v2/components/PoweredByLite.tsx | 45 -- .../plugin-auth/src/client-v2/hooks.ts | 45 +- .../src/client-v2/pages/AuthLayout.tsx | 6 +- .../client-v2/pages/AuthenticatorsPage.tsx | 42 +- .../plugin-auth/src/client-v2/plugin.tsx | 12 +- .../src/client-v2/pages/FileStoragePage.tsx | 9 +- .../plugin-users/src/client-v2/plugin.tsx | 2 + .../user-center/ChangePasswordItemModel.tsx | 134 ++++ .../plugin-verification/client-v2.d.ts | 2 + .../plugin-verification/client-v2.js | 1 + .../plugin-verification/package.json | 2 + .../client-v2/__tests__/VerifiersPage.test.ts | 188 +++++ .../client-v2/components/VerificationCode.tsx | 130 ++++ .../client-v2/components/VerifierSelect.tsx | 102 +++ .../src/client-v2/index.ts | 20 + .../src/client-v2/locale.ts | 35 + .../src/client-v2/otp-sms-provider-manager.ts | 43 ++ .../sms/AdminSettingsForm.tsx | 73 ++ .../otp-verification/sms/BindForm.tsx | 44 ++ .../otp-verification/sms/VerificationForm.tsx | 50 ++ .../client-v2/otp-verification/sms/index.ts | 28 + .../sms/providers/AliyunSettings.tsx | 65 ++ .../sms/providers/TencentSettings.tsx | 75 ++ .../src/client-v2/pages/VerifiersPage.tsx | 415 +++++++++++ .../src/client-v2/plugin.tsx | 71 ++ .../src/client-v2/user-center/MyVerifiers.tsx | 305 ++++++++ .../VerificationUserCenterItemModel.tsx | 53 ++ .../src/client-v2/verification-manager.ts | 56 ++ yarn.lock | 694 ++---------------- 70 files changed, 4530 insertions(+), 959 deletions(-) rename packages/{plugins/@nocobase/plugin-auth/src/client-v2/__tests__/PoweredByLite.test.tsx => core/client-v2/src/__tests__/PoweredBy.test.tsx} (74%) create mode 100644 packages/core/client-v2/src/__tests__/useCurrentRoles.test.tsx create mode 100644 packages/core/client-v2/src/components/PoweredBy.tsx create mode 100644 packages/core/client-v2/src/components/README.md create mode 100644 packages/core/client-v2/src/components/README.zh-CN.md rename packages/{plugins/@nocobase/plugin-auth/src/client-v2 => core/client-v2/src}/components/SwitchLanguage.tsx (56%) create mode 100644 packages/core/client-v2/src/components/form/DialogFormLayout.tsx create mode 100644 packages/core/client-v2/src/components/form/PasswordInput.tsx create mode 100644 packages/core/client-v2/src/components/form/RemoteSelect.tsx create mode 100644 packages/core/flow-engine/src/utils/randomId.ts create mode 100644 packages/plugins/@nocobase/plugin-api-keys/client-v2.d.ts create mode 100644 packages/plugins/@nocobase/plugin-api-keys/client-v2.js create mode 100644 packages/plugins/@nocobase/plugin-api-keys/src/client-v2/__tests__/ApiKeysPage.test.ts create mode 100644 packages/plugins/@nocobase/plugin-api-keys/src/client-v2/index.tsx create mode 100644 packages/plugins/@nocobase/plugin-api-keys/src/client-v2/locale.ts create mode 100644 packages/plugins/@nocobase/plugin-api-keys/src/client-v2/pages/ApiKeysPage.tsx create mode 100644 packages/plugins/@nocobase/plugin-api-keys/src/client-v2/pages/ExpiresField.tsx create mode 100644 packages/plugins/@nocobase/plugin-api-keys/src/client-v2/plugin.tsx create mode 100644 packages/plugins/@nocobase/plugin-auth-sms/client-v2.d.ts create mode 100644 packages/plugins/@nocobase/plugin-auth-sms/client-v2.js create mode 100644 packages/plugins/@nocobase/plugin-auth-sms/src/client-v2/__tests__/SmsSignInForm.test.ts create mode 100644 packages/plugins/@nocobase/plugin-auth-sms/src/client-v2/forms/SmsAdminSettings.tsx create mode 100644 packages/plugins/@nocobase/plugin-auth-sms/src/client-v2/forms/SmsSignInForm.tsx create mode 100644 packages/plugins/@nocobase/plugin-auth-sms/src/client-v2/index.tsx create mode 100644 packages/plugins/@nocobase/plugin-auth-sms/src/client-v2/locale.ts create mode 100644 packages/plugins/@nocobase/plugin-auth-sms/src/client-v2/plugin.tsx delete mode 100644 packages/plugins/@nocobase/plugin-auth/src/client-v2/components/PoweredByLite.tsx create mode 100644 packages/plugins/@nocobase/plugin-users/src/client-v2/user-center/ChangePasswordItemModel.tsx create mode 100644 packages/plugins/@nocobase/plugin-verification/client-v2.d.ts create mode 100644 packages/plugins/@nocobase/plugin-verification/client-v2.js create mode 100644 packages/plugins/@nocobase/plugin-verification/src/client-v2/__tests__/VerifiersPage.test.ts create mode 100644 packages/plugins/@nocobase/plugin-verification/src/client-v2/components/VerificationCode.tsx create mode 100644 packages/plugins/@nocobase/plugin-verification/src/client-v2/components/VerifierSelect.tsx create mode 100644 packages/plugins/@nocobase/plugin-verification/src/client-v2/index.ts create mode 100644 packages/plugins/@nocobase/plugin-verification/src/client-v2/locale.ts create mode 100644 packages/plugins/@nocobase/plugin-verification/src/client-v2/otp-sms-provider-manager.ts create mode 100644 packages/plugins/@nocobase/plugin-verification/src/client-v2/otp-verification/sms/AdminSettingsForm.tsx create mode 100644 packages/plugins/@nocobase/plugin-verification/src/client-v2/otp-verification/sms/BindForm.tsx create mode 100644 packages/plugins/@nocobase/plugin-verification/src/client-v2/otp-verification/sms/VerificationForm.tsx create mode 100644 packages/plugins/@nocobase/plugin-verification/src/client-v2/otp-verification/sms/index.ts create mode 100644 packages/plugins/@nocobase/plugin-verification/src/client-v2/otp-verification/sms/providers/AliyunSettings.tsx create mode 100644 packages/plugins/@nocobase/plugin-verification/src/client-v2/otp-verification/sms/providers/TencentSettings.tsx create mode 100644 packages/plugins/@nocobase/plugin-verification/src/client-v2/pages/VerifiersPage.tsx create mode 100644 packages/plugins/@nocobase/plugin-verification/src/client-v2/plugin.tsx create mode 100644 packages/plugins/@nocobase/plugin-verification/src/client-v2/user-center/MyVerifiers.tsx create mode 100644 packages/plugins/@nocobase/plugin-verification/src/client-v2/user-center/VerificationUserCenterItemModel.tsx create mode 100644 packages/plugins/@nocobase/plugin-verification/src/client-v2/verification-manager.ts diff --git a/packages/core/client-v2/src/PluginSettingsManager.ts b/packages/core/client-v2/src/PluginSettingsManager.ts index 9f209b6c5b4..82b95c40937 100644 --- a/packages/core/client-v2/src/PluginSettingsManager.ts +++ b/packages/core/client-v2/src/PluginSettingsManager.ts @@ -432,7 +432,7 @@ export class PluginSettingsManager = BaseAppli return null; } - const { title, aclSnippet, key, menuKey, name, ...others } = page; + const { title, aclSnippet, key, menuKey, name, icon, ...others } = page; return { ...others, @@ -443,6 +443,7 @@ export class PluginSettingsManager = BaseAppli name, title, label: title, + icon: this.renderIcon(icon), path: this.getRoutePath(name), sort: page.sort, isAllow, diff --git a/packages/core/client-v2/src/__tests__/PluginSettingsManager.test.ts b/packages/core/client-v2/src/__tests__/PluginSettingsManager.test.ts index 9bc5edf51a4..e6abf3c4740 100644 --- a/packages/core/client-v2/src/__tests__/PluginSettingsManager.test.ts +++ b/packages/core/client-v2/src/__tests__/PluginSettingsManager.test.ts @@ -76,6 +76,25 @@ describe('PluginSettingsManager v2', () => { expect(app.router.get('admin.settings.demo.advanced')).toMatchObject({ path: 'advanced' }); }); + it('should render string icon on both menu and page tab via renderIcon', () => { + // Previously `renderPage` spread the raw `icon` string straight to the antd + // Menu item, which displayed "LockOutlinedTitle" as text. Both `renderMenuItem` + // and `renderPage` must coerce string icon names to React elements. + const app = createMockClient(); + + app.pluginSettingsManager.addMenuItem({ key: 'demo', title: 'Demo', icon: 'TeamOutlined' }); + app.pluginSettingsManager.addPageTabItem({ + menuKey: 'demo', + key: 'index', + title: 'Overview', + icon: 'LockOutlined', + }); + + const list = app.pluginSettingsManager.getList(); + expect(React.isValidElement(list[0].icon)).toBe(true); + expect(React.isValidElement(list[0].children?.[0].icon)).toBe(true); + }); + it('should support componentLoader on page item', () => { const app = createMockClient(); const componentLoader = async () => ({ diff --git a/packages/plugins/@nocobase/plugin-auth/src/client-v2/__tests__/PoweredByLite.test.tsx b/packages/core/client-v2/src/__tests__/PoweredBy.test.tsx similarity index 74% rename from packages/plugins/@nocobase/plugin-auth/src/client-v2/__tests__/PoweredByLite.test.tsx rename to packages/core/client-v2/src/__tests__/PoweredBy.test.tsx index 39cc94b4eba..12aa30775d2 100644 --- a/packages/plugins/@nocobase/plugin-auth/src/client-v2/__tests__/PoweredByLite.test.tsx +++ b/packages/core/client-v2/src/__tests__/PoweredBy.test.tsx @@ -7,26 +7,26 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import { createMockClient, Plugin } from '@nocobase/client-v2'; import { render, screen, waitFor } from '@testing-library/react'; import React from 'react'; +import { createMockClient } from '../MockApplication'; +import { Plugin } from '../Plugin'; +import PoweredBy from '../components/PoweredBy'; -import PoweredByLite from '../components/PoweredByLite'; - -class PoweredByLiteRoutePlugin extends Plugin { +class PoweredByRoutePlugin extends Plugin { async load() { this.router.add('root', { path: '/', - Component: PoweredByLite, + Component: PoweredBy, }); } } class MockCustomBrandPlugin extends Plugin {} -const renderPoweredByLite = async (plugins: any[] = [], appInfoData: Record = { version: '1.2.3' }) => { +const renderPoweredBy = async (plugins: any[] = [], appInfoData: Record = { version: '1.2.3' }) => { const app = createMockClient({ - plugins: [PoweredByLiteRoutePlugin as any, ...plugins], + plugins: [PoweredByRoutePlugin as any, ...plugins], }); app.apiMock.onGet('app:getInfo').reply(200, { @@ -43,21 +43,24 @@ const renderPoweredByLite = async (plugins: any[] = [], appInfoData: Record { +describe('PoweredBy', () => { afterEach(() => { vi.restoreAllMocks(); }); it('should render the default brand when custom-brand is not installed', async () => { - const { container } = await renderPoweredByLite(); + const { container } = await renderPoweredBy(); expect(screen.getByRole('link', { name: 'NocoBase' })).toHaveAttribute('href', 'https://www.nocobase.com'); expect(container).toHaveTextContent('Powered by NocoBase'); + // The `.nb-brand` className is reserved for the custom-brand HTML branch + // so downstream stylesheets can selectively target customised content + // without leaking onto the default footer. expect(container.querySelector('.nb-brand')).not.toBeInTheDocument(); }); it('should render custom-brand HTML and replace appVersion', async () => { - const { container } = await renderPoweredByLite([ + const { container } = await renderPoweredBy([ [ MockCustomBrandPlugin, { @@ -77,7 +80,7 @@ describe('PoweredByLite', () => { }); it('should not render undefined appVersion when app version is unavailable', async () => { - const { container } = await renderPoweredByLite( + const { container } = await renderPoweredBy( [ [ MockCustomBrandPlugin, @@ -98,7 +101,11 @@ describe('PoweredByLite', () => { }); it('should escape custom-brand appVersion placeholder', async () => { - const { container } = await renderPoweredByLite( + // Defence in depth: even if the back-end ever returns a tampered + // `app:getInfo` payload, the version string must be HTML-escaped + // before being interpolated into the custom-brand template — never + // produce a live `