feat: 集成 Cloudflare Turnstile + 按设计稿重构登录后页面 - #32
Merged
Conversation
- 登录/注册/邮箱验证码下发改用 Cloudflare Turnstile 人机验证组件, 默认使用官方测试密钥(始终通过)方便联调;生产环境通过环境变量 TURNSTILE_SITE_KEY / TURNSTILE_SECRET_KEY 覆盖 - 后端:新增 web/backend/turnstile.py 做 siteverify 校验; auth_routes 的 login/register/email-code 流程替换 captcha 校验, 移除 /api/auth/captcha/new,新增 /api/auth/turnstile/sitekey - 前端:新增 Turnstile 组件,LoginForm/RegisterForm 去掉图形验证码, apiClient/auth hook 统一传 turnstile_token;删除 CaptchaImage - 登录后页面按设计稿重构:me/* 改为侧栏布局(AccountLayout), admin/* 从旧 AppNavbar 切到新 SiteLayout,补齐设计稿页头样式 - 抽出 ResearchCard 到独立组件,修复页面模块导出非页面组件的 tsc 报错 验证:tsc 0 报错;next build 26 路由全部预渲染;vitest 74/74 通过; e2e:sitekey 返回测试 key,带 token 登录放行人机验证后校验账密, 缺 token 返回 422 人机验证失败 Co-authored-by: multica-agent <github@multica.ai>
There was a problem hiding this comment.
Pull request overview
该 PR 在 FastAPI 后端鉴权链路与 Next.js 前端认证/登录后页面结构两端同步改造:用 Cloudflare Turnstile 替换原图形验证码,并按设计稿统一登录后个人区与管理后台的页面布局组件化复用。
Changes:
- 后端新增 Turnstile 校验模块与 sitekey 下发接口,并将 login/register/email-code 流程从图形验证码切换为 Turnstile。
- 前端新增 Turnstile 组件并改造登录/注册表单的鉴权参数传递;移除 CaptchaImage。
- 登录后页面按设计稿重构:引入 AccountLayout、抽离 ResearchCard、admin 页面切换到 SiteLayout,并补齐所需图标子集。
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| web/frontend/src/lib/auth.tsx | AuthContext 的 login/register/email-code 入参改为 turnstileToken |
| web/frontend/src/lib/apiClient.ts | authAPI 改为传 turnstile_token,并新增获取 Turnstile sitekey 方法 |
| web/frontend/src/components/site/ResearchCard.tsx | 从 page 模块抽离可复用 ResearchCard,避免 Next 页面导出非页面组件 |
| web/frontend/src/components/site/AccountLayout.tsx | 新增登录后个人区侧栏布局(用户名片 + 导航 + 主内容区) |
| web/frontend/src/components/auth/Turnstile.tsx | 新增 Turnstile 显式渲染组件,支持 reset 与失败提示 |
| web/frontend/src/components/auth/RegisterForm.tsx | 注册流程移除图形验证码,改用 Turnstile token + 邮箱验证码 |
| web/frontend/src/components/auth/LoginForm.tsx | 登录/邮箱验证码登录移除图形验证码,改用 Turnstile token |
| web/frontend/src/components/auth/CaptchaImage.tsx | 删除旧图形验证码组件 |
| web/frontend/src/app/page.tsx | 首页改为引用抽离后的 ResearchCard |
| web/frontend/src/app/me/page.tsx | “我的分析”切换到 AccountLayout,并调整操作按钮/文案 |
| web/frontend/src/app/me/billing/page.tsx | “订阅明细”切换到 AccountLayout,并补齐购买入口 |
| web/frontend/src/app/me/preferences/page.tsx | “账户偏好”切换到 AccountLayout,并修正设置入口为 Link |
| web/frontend/src/app/leaderboard/page.tsx | 榜单页改为引用抽离后的 ResearchCard |
| web/frontend/src/app/admin/users/page.tsx | 管理页从旧 AppNavbar/Footer 切换到 SiteLayout |
| web/frontend/src/app/admin/system-default-provider/page.tsx | 管理页切换到 SiteLayout,并补齐设计稿标题/徽章结构 |
| web/frontend/src/app/admin/llm-config/page.tsx | 管理页切换到 SiteLayout,并补齐设计稿标题/徽章结构 |
| web/frontend/public/lib/font-awesome/css/icons.subset.css | 增补/调整图标子集以匹配新布局所需图标 |
| web/backend/turnstile.py | 新增 Turnstile siteverify 校验逻辑与测试密钥默认值 |
| web/backend/schemas.py | 请求体由 captcha_* 字段切换为 turnstile_token |
| web/backend/auth_routes.py | 移除 /captcha/new,新增 /turnstile/sitekey,并在鉴权流程中使用 Turnstile 校验 |
| .env.example | 增加 Turnstile 相关环境变量示例(后端与 NEXT_PUBLIC 前端注入) |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+63
to
66
| await authAPI.sendEmailCode(emailForCode, turnstileToken); | ||
|
|
||
| onShowToast('验证码已发送到您的邮箱', 'success'); | ||
| setCountdown(60); |
Comment on lines
+62
to
65
| await authAPI.sendEmailCodeForRegister(formData.email, turnstileToken); | ||
|
|
||
| onShowToast('验证码已发送到您的邮箱,请查收', 'success'); | ||
| setCountdown(60); |
Comment on lines
+36
to
+52
| scriptLoadPromise = new Promise<void>((resolve, reject) => { | ||
| const existing = document.querySelector<HTMLScriptElement>( | ||
| `script[src="${SCRIPT_SRC}"]`, | ||
| ); | ||
| if (existing) { | ||
| existing.addEventListener('load', () => resolve()); | ||
| existing.addEventListener('error', () => reject(new Error('Turnstile script load failed'))); | ||
| return; | ||
| } | ||
| const s = document.createElement('script'); | ||
| s.src = SCRIPT_SRC; | ||
| s.async = true; | ||
| s.defer = true; | ||
| s.onload = () => resolve(); | ||
| s.onerror = () => reject(new Error('Turnstile script load failed')); | ||
| document.head.appendChild(s); | ||
| }); |
Comment on lines
+92
to
+95
| getTurnstileSiteKey: async () => { | ||
| try { | ||
| const response = await publicApiClient.get('/api/auth/turnstile/sitekey'); | ||
| return response.data as { sitekey: string }; |
Comment on lines
+7
to
+18
| import os | ||
| from typing import Optional | ||
|
|
||
| import httpx | ||
|
|
||
| # Cloudflare 官方测试密钥(始终通过 / always passes) | ||
| # 详见 https://developers.cloudflare.com/turnstile/troubleshooting/testing/ | ||
| _TEST_SECRET = "1x0000000000000000000000000000000AA" | ||
| _TEST_SITE_KEY = "1x00000000000000000000AA" | ||
|
|
||
| TURNSTILE_SECRET_KEY = os.getenv("TURNSTILE_SECRET_KEY", _TEST_SECRET) | ||
| TURNSTILE_SITE_KEY = os.getenv("TURNSTILE_SITE_KEY", _TEST_SITE_KEY) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
改动
1. Cloudflare Turnstile 替换图形验证码
web/backend/turnstile.py(siteverify 校验,默认用官方测试密钥始终通过,方便联调);auth_routes的 login/register/email-code 流程用 Turnstile 替换 captcha 校验;移除/api/auth/captcha/new,新增/api/auth/turnstile/sitekey。Turnstile.tsx组件(显式渲染、可 reset、dark 主题);LoginForm/RegisterForm去掉图形验证码,改挂 Turnstile;apiClient/useAuth统一传turnstile_token;删除CaptchaImage。1x00000000000000000000AA/ secret1x0000000000000000000000000000000AA),生产环境用环境变量TURNSTILE_SITE_KEY/TURNSTILE_SECRET_KEY覆盖。2. 按设计稿重构登录后系统页面
me/*(我的分析 / 订阅明细 / 账户偏好)改为设计稿的侧栏布局AccountLayout(用户名片 + 纵向导航,窄屏横向滚动)。admin/*(用户管理 / LLM 配置 / 系统默认 Provider)从旧AppNavbar+Footer切到新SiteLayout,补齐设计稿页头(eyebrow + 标题 + 管理员徽章)。ResearchCard到独立组件,修复页面模块导出非页面组件的 tsc 报错。验证
tsc --noEmit:0 报错next build:26 路由全部静态预渲染成功vitest run:74/74 通过GET /api/auth/turnstile/sitekey→ 测试 key;POST /api/auth/captcha/new→ 404;带 token 登录 → 放行人机验证后校验账密(401 用户名或密码错误);不带 token 登录 → 422 人机验证失败。联调说明
当前用 Cloudflare 官方测试密钥,Turnstile widget 会显示但始终通过。要切到真实人机验证,在
.env设置真实的TURNSTILE_SITE_KEY/TURNSTILE_SECRET_KEY与NEXT_PUBLIC_TURNSTILE_SITE_KEY即可,前后端无需改代码。