From e65adaabc7d62935431fb134f52988853694ecae Mon Sep 17 00:00:00 2001 From: xiaocheny214 <187097481+xiaocheny214@users.noreply.github.com> Date: Fri, 14 Aug 2026 23:48:41 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(auth):=20=E5=86=85=E6=B5=8B=E6=9C=9F?= =?UTF-8?q?=E9=97=B4=E5=85=B3=E9=97=AD=E5=85=AC=E5=BC=80=E6=B3=A8=E5=86=8C?= =?UTF-8?q?=EF=BC=8C=E4=BB=85=E4=BF=9D=E7=95=99=E5=B7=B2=E6=9C=89=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 公开注册、注册发码和验证码自动建号都会在内测阶段误开账号。登录入口保留,测试账号由运维预置。 Fixes #326 Co-authored-by: Cursor --- .../src/windup_app/server/user/interface.py | 4 +- .../app/src/windup_app/server/user/service.py | 23 ++-- .../app/src/windup_app/web/api/auth.py | 24 ++-- .../tests/test_auth_registration_closed.py | 30 +++++ backend/tests/test_user_service.py | 45 +++---- frontend/src/app/layout/app-header.test.tsx | 6 +- frontend/src/app/layout/app-header.tsx | 4 +- .../src/features/account-panel/index.test.tsx | 118 +++--------------- frontend/src/features/account-panel/index.tsx | 21 +--- frontend/src/pages/landing/index.test.tsx | 4 +- .../src/pages/landing/marketing-header.tsx | 13 +- 11 files changed, 94 insertions(+), 198 deletions(-) create mode 100644 backend/tests/test_auth_registration_closed.py diff --git a/backend/packages/app/src/windup_app/server/user/interface.py b/backend/packages/app/src/windup_app/server/user/interface.py index 84c22755..ec361a9a 100644 --- a/backend/packages/app/src/windup_app/server/user/interface.py +++ b/backend/packages/app/src/windup_app/server/user/interface.py @@ -46,9 +46,9 @@ def send_verification_code(self, email: str, purpose: str) -> None: @abstractmethod def login_by_code(self, input: LoginByCodeInput) -> LoginResult: - """邮箱+验证码登录,无账号时自动注册。 + """邮箱+验证码登录。内测期间不自动建号。 - :raises windup_common.exceptions.BizException: 验证码错误 / 已过期 / 账号已封禁。 + :raises windup_common.exceptions.BizException: 验证码错误 / 已过期 / 账号不存在 / 账号已封禁。 """ # -- 登出 ------------------------------------------------------------ diff --git a/backend/packages/app/src/windup_app/server/user/service.py b/backend/packages/app/src/windup_app/server/user/service.py index c80dbc27..87dd8551 100644 --- a/backend/packages/app/src/windup_app/server/user/service.py +++ b/backend/packages/app/src/windup_app/server/user/service.py @@ -280,6 +280,9 @@ def login_by_password_with_session( def send_verification_code(self, email: str, purpose: str) -> None: """发送邮箱验证码。""" + if purpose == "register": + raise BizException("内测期间暂不开放注册", code=BizCode.BAD_REQUEST) + # 频率限制 cooldown_key = VERIFY_COOLDOWN_KEY.format(email=email) if self.redis.get(cooldown_key): @@ -315,25 +318,17 @@ def login_by_code(self, input: LoginByCodeInput) -> LoginResult: def login_by_code_with_session( self, session: Session, input: LoginByCodeInput ) -> LoginResult: - """邮箱+验证码登录,无账号自动注册(带 session)。""" + """邮箱+验证码登录(带 session)。内测期间不自动建号。""" # 校验验证码 self._verify_code(input.email, input.code, "login") - # 查找或创建用户 user = session.scalar(select(User).where(User.email == input.email)) if user is None: - user = User(email=input.email, email_verified_at=datetime.now(timezone.utc)) - session.add(user) - session.flush() - # 自动注册送积分 - self._create_credit_account(session, user.id) - logger.info("[WINDUP] 验证码自动注册 | user_id=%s email=%s", user.id, user.email) - else: - if user.status == UserStatus.BANNED: - raise BizException("账号已被封禁", code=BizCode.BAD_REQUEST) - # 标记邮箱已验证 - if user.email_verified_at is None: - user.email_verified_at = datetime.now(timezone.utc) + raise BizException("账号不存在", code=BizCode.NOT_FOUND) + if user.status == UserStatus.BANNED: + raise BizException("账号已被封禁", code=BizCode.BAD_REQUEST) + if user.email_verified_at is None: + user.email_verified_at = datetime.now(timezone.utc) user.last_login_at = datetime.now(timezone.utc) session.flush() diff --git a/backend/packages/app/src/windup_app/web/api/auth.py b/backend/packages/app/src/windup_app/web/api/auth.py index b196a024..8570ac0c 100644 --- a/backend/packages/app/src/windup_app/web/api/auth.py +++ b/backend/packages/app/src/windup_app/web/api/auth.py @@ -111,19 +111,15 @@ class UserOut(BaseModel): @router.post("/register", response_model=Response[TokenResponse]) def register(body: RegisterRequest, session: Session = Depends(get_session)): - """邮箱+验证码+密码注册,注册即登录。""" - result = service.register_by_email_with_session( - session, - type("RegisterInput", (), {"email": body.email, "password": body.password, "code": body.code, "nickname": body.nickname})(), - ) - return Response.success( - TokenResponse( - access_token=result.access_token, - refresh_token=result.refresh_token, - user=result.user, - ), - message="注册成功", - ) + """邮箱+验证码+密码注册。 + + 内测期间关闭公开注册,路由与请求模型保留以便以后重新开放。 + """ + from windup_common.enums.biz_code import BizCode + from windup_common.exceptions import BizException + + del body, session + raise BizException("内测期间暂不开放注册", code=BizCode.BAD_REQUEST) @router.post("/login", response_model=Response[TokenResponse]) @@ -152,7 +148,7 @@ def send_code(body: SendCodeRequest): @router.post("/login-by-code", response_model=Response[TokenResponse]) def login_by_code(body: LoginByCodeRequest, session: Session = Depends(get_session)): - """验证码登录,无账号自动注册。""" + """验证码登录。内测期间不自动注册。""" result = service.login_by_code_with_session( session, type("LoginByCodeInput", (), {"email": body.email, "code": body.code})(), diff --git a/backend/tests/test_auth_registration_closed.py b/backend/tests/test_auth_registration_closed.py new file mode 100644 index 00000000..aa77c335 --- /dev/null +++ b/backend/tests/test_auth_registration_closed.py @@ -0,0 +1,30 @@ +"""内测关闭公开注册:公开建号路径必须被拒绝。""" + +from windup_common.enums.biz_code import BizCode + + +def test_register_endpoint_rejects_public_signup(client): + resp = client.post( + "/auth/register", + json={ + "email": "new@example.com", + "password": "password123", + "code": "123456", + }, + ) + assert resp.status_code == 200 + body = resp.json() + assert body["code"] == BizCode.BAD_REQUEST + assert body["message"] == "内测期间暂不开放注册" + assert body["data"] is None + + +def test_send_code_rejects_register_purpose(client): + resp = client.post( + "/auth/send-code", + json={"email": "new@example.com", "purpose": "register"}, + ) + assert resp.status_code == 200 + body = resp.json() + assert body["code"] == BizCode.BAD_REQUEST + assert body["message"] == "内测期间暂不开放注册" diff --git a/backend/tests/test_user_service.py b/backend/tests/test_user_service.py index e8771584..9ca6f414 100644 --- a/backend/tests/test_user_service.py +++ b/backend/tests/test_user_service.py @@ -282,46 +282,29 @@ def test_login_banned_user(db_session, service, mock_email): # -- 验证码登录测试 ------------------------------------------------------ -def test_login_by_code_new_user(db_session, service, mock_email): - service._redis.get.return_value = "123456" +def test_login_by_code_unknown_email_does_not_create_user(db_session, service, mock_email): + """内测关闭公开注册后,验证码登录不得自动建号。""" + from sqlalchemy import select + service._redis.get.return_value = "123456" input_data = LoginByCodeInput(email="code@example.com", code="123456") - result = service.login_by_code_with_session(db_session, input_data) - assert result.user.email == "code@example.com" - assert result.user.email_verified_at is not None + with pytest.raises(BizException, match="账号不存在") as exc: + service.login_by_code_with_session(db_session, input_data) + from windup_common.enums.biz_code import BizCode -def test_login_by_code_new_user_creates_credit_account(db_session, service, mock_email): - """验证码自动注册时应创建积分账户并赠送初始积分。""" - from sqlalchemy import select - from windup_app.server.quota.model import CreditAccount, CreditTransaction - from windup_common.enums.quota import CreditReason - from windup_framework.config.quota import settings as quota_settings + assert exc.value.code == BizCode.NOT_FOUND + assert db_session.scalar(select(User).where(User.email == "code@example.com")) is None - service._redis.get.return_value = "123456" - input_data = LoginByCodeInput(email="auto@example.com", code="123456") - result = service.login_by_code_with_session(db_session, input_data) - user_id = result.user.id +def test_send_verification_code_rejects_register_purpose(service, mock_email): + service._redis.get.return_value = None - # 验证积分账户已创建 - account = db_session.scalar( - select(CreditAccount).where(CreditAccount.user_id == user_id) - ) - assert account is not None - assert account.balance == quota_settings.register_gift_amount - assert account.total_earned == quota_settings.register_gift_amount + with pytest.raises(BizException, match="内测期间暂不开放注册"): + service.send_verification_code("new@example.com", "register") - # 验证赠送流水已记录 - txn = db_session.scalar( - select(CreditTransaction).where( - CreditTransaction.user_id == user_id, - CreditTransaction.reason == CreditReason.REGISTER_GIFT, - ) - ) - assert txn is not None - assert txn.delta == quota_settings.register_gift_amount + mock_email.send_verification_code.assert_not_called() def test_login_by_code_existing_user(db_session, service, mock_email): diff --git a/frontend/src/app/layout/app-header.test.tsx b/frontend/src/app/layout/app-header.test.tsx index e99819cf..f50c40ba 100644 --- a/frontend/src/app/layout/app-header.test.tsx +++ b/frontend/src/app/layout/app-header.test.tsx @@ -176,7 +176,7 @@ describe('AppHeader', () => { it('为访客提供可发现的登录入口并保留完整站内回跳地址', async () => { renderHeader('/quick-start?mode=fast#brief') - const entry = await screen.findByRole('link', { name: '登录 / 注册' }) + const entry = await screen.findByRole('link', { name: '登录' }) expect(entry.getAttribute('href')).toBe( '/?account=login&returnTo=%2Fquick-start%3Fmode%3Dfast%23brief', ) @@ -192,7 +192,7 @@ describe('AppHeader', () => { fireEvent.click(screen.getByRole('button', { name: '退出登录' })) await waitFor(() => expect(screen.getByTestId('location').textContent).toBe('/')) - expect(await screen.findByRole('link', { name: '登录 / 注册' })).toBeTruthy() + expect(await screen.findByRole('link', { name: '登录' })).toBeTruthy() expect(apis.logout).toHaveBeenCalledWith('rotated-refresh-token') }) @@ -206,7 +206,7 @@ describe('AppHeader', () => { fireEvent.click(screen.getByRole('button', { name: '退出登录' })) await waitFor(() => expect(screen.getByTestId('location').textContent).toBe('/')) - expect(await screen.findByRole('link', { name: '登录 / 注册' })).toBeTruthy() + expect(await screen.findByRole('link', { name: '登录' })).toBeTruthy() }) it('没有昵称时使用邮箱展示账号身份', async () => { diff --git a/frontend/src/app/layout/app-header.tsx b/frontend/src/app/layout/app-header.tsx index 9660b4cd..6130c3d4 100644 --- a/frontend/src/app/layout/app-header.tsx +++ b/frontend/src/app/layout/app-header.tsx @@ -190,10 +190,10 @@ export function AppHeader() { ) : session.state.status === 'guest' ? ( - 登录 / 注册 + 登录 登录 ) : ( diff --git a/frontend/src/features/account-panel/index.test.tsx b/frontend/src/features/account-panel/index.test.tsx index bf83965e..7e46035c 100644 --- a/frontend/src/features/account-panel/index.test.tsx +++ b/frontend/src/features/account-panel/index.test.tsx @@ -94,29 +94,21 @@ describe('AccountPanel', () => { expect(screen.getByRole('dialog', { name: '登录 Windup' })).toBeTruthy() expect(screen.getByRole('heading', { name: '欢迎回来。' })).toBeTruthy() - expect(screen.getByText('未注册的邮箱将在验证后自动创建账号。')).toBeTruthy() + expect(screen.queryByText('未注册的邮箱将在验证后自动创建账号。')).toBeNull() expect(screen.queryByRole('tab', { name: '注册' })).toBeNull() - expect(screen.getByRole('button', { name: '创建账号' })).toBeTruthy() + expect(screen.queryByRole('button', { name: '创建账号' })).toBeNull() await waitFor(() => expect(document.activeElement).toBe(screen.getByLabelText('邮箱'))) }) - it('opens registration as a centered, progressive form', async () => { - renderPanel('/?account=register&returnTo=%2Fworkspace') - - const dialog = screen.getByRole('dialog', { name: '创建 Windup 账号' }) - expect(screen.getByRole('heading', { name: '欢迎来到 Windup' })).toBeTruthy() - expect(dialog.className).toContain('max-w-[34rem]') - expect(screen.getByText('从一个角色开始,')).toBeTruthy() - expect(screen.queryByText('继续搭建,')).toBeNull() - expect(screen.getByTestId('register-fields').className).toContain('auth-register-fields') - expect(screen.queryByRole('tablist', { name: '账号操作' })).toBeNull() - expect(screen.getByLabelText('邮箱')).toBeTruthy() - expect(screen.queryByLabelText('密码')).toBeNull() - expect(screen.queryByLabelText('昵称(选填)')).toBeNull() - expect(screen.queryByLabelText('验证码')).toBeNull() - expect(screen.getByTestId('register-fields').querySelector('[aria-live]')).toBeNull() - expect(screen.getByRole('button', { name: '继续' })).toBeTruthy() + it('opens a closed-registration URL as login and never starts signup', async () => { + const { apis } = renderPanel('/?account=register&returnTo=%2Fworkspace') + + expect(screen.getByRole('dialog', { name: '登录 Windup' })).toBeTruthy() + expect(screen.getByRole('heading', { name: '欢迎回来。' })).toBeTruthy() + expect(screen.queryByRole('button', { name: '创建账号' })).toBeNull() + expect(screen.queryByRole('button', { name: '继续' })).toBeNull() expect(screen.getByRole('button', { name: '登录' })).toBeTruthy() + expect(apis.register).not.toHaveBeenCalled() await waitFor(() => expect(document.activeElement).toBe(screen.getByLabelText('邮箱'))) }) @@ -141,15 +133,15 @@ describe('AccountPanel', () => { const dialog = screen.getByRole('dialog', { name: '登录 Windup' }) const closeButton = screen.getByRole('button', { name: '关闭账号面板' }) - const entrySwitch = screen.getByRole('button', { name: '创建账号' }) + const submitButton = screen.getByRole('button', { name: '登录' }) - entrySwitch.focus() - fireEvent.keyDown(entrySwitch, { key: 'Tab' }) + submitButton.focus() + fireEvent.keyDown(submitButton, { key: 'Tab' }) expect(document.activeElement).toBe(closeButton) closeButton.focus() fireEvent.keyDown(closeButton, { key: 'Tab', shiftKey: true }) - expect(document.activeElement).toBe(entrySwitch) + expect(document.activeElement).toBe(submitButton) expect(dialog.contains(document.activeElement)).toBe(true) }) @@ -266,9 +258,7 @@ describe('AccountPanel', () => { email: 'reader@example.com', code: '123456', }) - expect( - screen.getByText('登录成功。如果这是你首次使用该邮箱,我们已为你创建账号。').textContent, - ).toContain('登录成功。如果这是你首次使用该邮箱,我们已为你创建账号。') + expect(screen.getByText('登录成功,正在继续。').textContent).toContain('登录成功,正在继续。') await act(async () => vi.advanceTimersByTimeAsync(900)) expect(screen.getByTestId('location').textContent).toBe('/projects?view=recent') @@ -348,85 +338,15 @@ describe('AccountPanel', () => { expect(apis.sendCode).not.toHaveBeenCalled() }) - it('preserves registration input when showing a password and returning a step', async () => { - renderPanel('/?account=register') - fireEvent.change(screen.getByLabelText('邮箱'), { target: { value: 'new@example.com' } }) - fireEvent.submit(screen.getByRole('button', { name: '继续' }).closest('form')!) - - const password = await screen.findByLabelText('密码') - expect(password.getAttribute('type')).toBe('password') - fireEvent.click(screen.getByRole('button', { name: '显示密码' })) - expect(password.getAttribute('type')).toBe('text') - - fireEvent.click(screen.getByRole('button', { name: '返回上一步' })) - expect(((await screen.findByLabelText('邮箱')) as HTMLInputElement).value).toBe( - 'new@example.com', - ) - expect(screen.getByTestId('auth-motion-stage').dataset.motionDirection).toBe('backward') - }) - - it('switches account entry only after the current panel exits', async () => { + it('does not expose a signup switch from the login panel', async () => { vi.useFakeTimers() renderPanel('/?account=login&returnTo=%2Fworkspace') - fireEvent.click(screen.getByRole('button', { name: '创建账号' })) - expect(screen.getByRole('dialog', { name: '登录 Windup' })).toBeTruthy() + expect(screen.queryByRole('button', { name: '创建账号' })).toBeNull() expect(screen.getByTestId('location').textContent).toBe('/?account=login&returnTo=%2Fworkspace') - await act(async () => vi.advanceTimersByTimeAsync(520)) - expect(screen.getByRole('dialog', { name: '创建 Windup 账号' })).toBeTruthy() - expect(screen.getByTestId('location').textContent).toBe( - '/?account=register&returnTo=%2Fworkspace', - ) - }) - - it('validates each registration step and reuses the existing register API contract', async () => { - const { apis } = renderPanel('/?account=register&returnTo=%2Fworkspace') - fireEvent.change(screen.getByLabelText('邮箱'), { target: { value: 'new@example.com' } }) - - fireEvent.submit(screen.getByRole('button', { name: '继续' }).closest('form')!) - expect(screen.getByRole('dialog', { name: '创建 Windup 账号' })).toBeTruthy() - expect(screen.getByRole('heading', { name: '为账号加一道保护' })).toBeTruthy() - expect(await screen.findByLabelText('密码')).toBeTruthy() - fireEvent.change(screen.getByLabelText('密码'), { target: { value: 'short' } }) - - fireEvent.submit(screen.getByRole('button', { name: '继续' }).closest('form')!) - expect((await screen.findByRole('alert')).textContent).toContain('密码需为 8–128 位') - expect(apis.register).not.toHaveBeenCalled() - - fireEvent.change(screen.getByLabelText('密码'), { target: { value: 'password-123' } }) - fireEvent.submit(screen.getByRole('button', { name: '继续' }).closest('form')!) - expect(screen.getByRole('dialog', { name: '创建 Windup 账号' })).toBeTruthy() - expect(screen.getByRole('heading', { name: '留下你的称呼' })).toBeTruthy() - expect(await screen.findByLabelText('昵称(选填)')).toBeTruthy() - fireEvent.change(screen.getByLabelText('昵称(选填)'), { - target: { value: 'N'.repeat(51) }, - }) - - fireEvent.submit(screen.getByRole('button', { name: '继续' }).closest('form')!) - expect((await screen.findByRole('alert')).textContent).toContain('昵称不能超过 50 个字符') - - fireEvent.change(screen.getByLabelText('昵称(选填)'), { target: { value: '新用户' } }) - fireEvent.submit(screen.getByRole('button', { name: '继续' }).closest('form')!) - await waitFor(() => - expect(apis.sendCode).toHaveBeenCalledWith({ - email: 'new@example.com', - purpose: 'register', - }), - ) - expect(screen.getByRole('dialog', { name: '创建 Windup 账号' })).toBeTruthy() - expect(screen.getByRole('heading', { name: '确认你的邮箱' })).toBeTruthy() - expect(await screen.findByLabelText('验证码')).toBeTruthy() - fireEvent.change(screen.getByLabelText('验证码'), { target: { value: '123456' } }) - fireEvent.submit(screen.getByRole('button', { name: '创建账号' }).closest('form')!) - await waitFor(() => - expect(apis.register).toHaveBeenCalledWith({ - email: 'new@example.com', - password: 'password-123', - code: '123456', - nickname: '新用户', - }), - ) + expect(screen.getByRole('dialog', { name: '登录 Windup' })).toBeTruthy() + expect(screen.getByTestId('location').textContent).toBe('/?account=login&returnTo=%2Fworkspace') }) }) diff --git a/frontend/src/features/account-panel/index.tsx b/frontend/src/features/account-panel/index.tsx index 212b7803..16b4d067 100644 --- a/frontend/src/features/account-panel/index.tsx +++ b/frontend/src/features/account-panel/index.tsx @@ -230,7 +230,7 @@ export function AccountPanel() { const entry = searchParams.get('account') if (entry !== 'login' && entry !== 'register') return null - return + return } /** 只有面板真正打开时才读取会话,关闭状态不把认证 Context 强加给应用外壳。 */ @@ -374,14 +374,6 @@ function AccountPanelDialog({ entry }: { entry: AccountEntry }) { window.requestAnimationFrame(() => emailInputRef.current?.focus()) } - function switchEntry(nextEntry: AccountEntry) { - leaveWithAnimation(() => { - const next = new URLSearchParams(searchParams) - next.set('account', nextEntry) - setSearchParams(next, { replace: true }) - }) - } - async function sendCode(): Promise { if (isSendingCode || cooldownSeconds > 0) return false if (!EMAIL_PATTERN.test(normalizedEmail)) { @@ -488,7 +480,7 @@ function AccountPanelDialog({ entry }: { entry: AccountEntry }) { successMessage = '账号已创建,正在继续。' } else if (mode === 'code') { await session.loginByCode({ email: normalizedEmail, code }) - successMessage = '登录成功。如果这是你首次使用该邮箱,我们已为你创建账号。' + successMessage = '登录成功,正在继续。' } else { await session.login({ email: normalizedEmail, password }) successMessage = '登录成功,正在继续。' @@ -780,7 +772,7 @@ function AccountPanelDialog({ entry }: { entry: AccountEntry }) { {!isRegister && mode === 'code' && (

- 未注册的邮箱将在验证后自动创建账号。 + 内测期间仅支持已有账号登录。

)} @@ -805,13 +797,6 @@ function AccountPanelDialog({ entry }: { entry: AccountEntry }) { - -

- {isRegister ? '已有账号?' : '还没有账号?'}{' '} - -

diff --git a/frontend/src/pages/landing/index.test.tsx b/frontend/src/pages/landing/index.test.tsx index 3190cb52..bd2864f5 100644 --- a/frontend/src/pages/landing/index.test.tsx +++ b/frontend/src/pages/landing/index.test.tsx @@ -56,9 +56,7 @@ describe('LandingPage', () => { expect(screen.getByRole('link', { name: '登录' }).getAttribute('href')).toBe( '/?account=login&returnTo=%2Fworkspace', ) - expect(screen.getByRole('link', { name: '注册' }).getAttribute('href')).toBe( - '/?account=register&returnTo=%2Fworkspace', - ) + expect(screen.queryByRole('link', { name: '注册' })).toBeNull() // Header 只处理账号入口,Hero 与收尾负责把用户带进创作。 const creationLinks = screen.getAllByRole('link', { name: '开始创作' }) expect(creationLinks).toHaveLength(2) diff --git a/frontend/src/pages/landing/marketing-header.tsx b/frontend/src/pages/landing/marketing-header.tsx index 755aae9e..7f3c8946 100644 --- a/frontend/src/pages/landing/marketing-header.tsx +++ b/frontend/src/pages/landing/marketing-header.tsx @@ -7,11 +7,6 @@ const loginEntry = `/?${new URLSearchParams({ returnTo: '/workspace', })}` -const registerEntry = `/?${new URLSearchParams({ - account: 'register', - returnTo: '/workspace', -})}` - const sections = [ ['#capabilities', '产品能力'], ['#workflow', '制作流程'], @@ -73,15 +68,9 @@ export function MarketingHeader() { <> - 登录 - - - 注册 + 登录 )} From 7601f2a80329c70d990991c1559f8bff7be61de8 Mon Sep 17 00:00:00 2001 From: xiaocheny214 <187097481+xiaocheny214@users.noreply.github.com> Date: Fri, 14 Aug 2026 23:58:43 +0800 Subject: [PATCH 2/2] style(auth): format closed-registration helper copy oxfmt requires the login helper line to stay on one line. This unblocks Frontend CI format:check on #327. Co-authored-by: Cursor --- frontend/src/features/account-panel/index.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/frontend/src/features/account-panel/index.tsx b/frontend/src/features/account-panel/index.tsx index 16b4d067..0acaef56 100644 --- a/frontend/src/features/account-panel/index.tsx +++ b/frontend/src/features/account-panel/index.tsx @@ -771,9 +771,7 @@ function AccountPanelDialog({ entry }: { entry: AccountEntry }) { )} {!isRegister && mode === 'code' && ( -

- 内测期间仅支持已有账号登录。 -

+

内测期间仅支持已有账号登录。

)}