2026-03-23 AI Text Humanizer 线上部署 & 安全加固
一、线上环境打通(上午完成)
问题:Humanize 功能线上 500 错误
- 现象:点击 Humanize 按钮报
Unexpected token 'I', "Internal S"... is not valid JSON - 根因:
setInterval()在 Cloudflare Workers 全局作用域不被允许(异步 I/O 限制),模块加载时就报错 - 修复:删除 setInterval,改成每次请求时 lazy cleanup
- 结果:✅ 线上功能跑通,AI 检测评分从 80 降到 50
Cloudflare 环境变量配置
- ZHIPU_API_KEY 已在 production 环境配好
- 发现 Cloudflare 监听 main 分支,但代码在 master 分支
- 通过 API 手动触发部署解决了分支不匹配问题
- 教训:Cloudflare API Token 存在
config/cloudflare-token.json,搜凭证时应直接扫 config 目录
二、安全加固(下午完成)✅
需求:防止直接调用 API 白嫖免费次数
三层防护全部上线:
1. Cloudflare Turnstile(Bot 防护)
- 在 Cloudflare 控制台创建 Turnstile 实例
- Site Key:
0x4AAAAAACu1U9esqyj_y103 - Secret Key:通过 Cloudflare Pages 环境变量注入
- 前端:
components/ui/turnstile.tsx,managed 模式,页面加载时自动验证,隐藏组件 - 后端:
lib/turnstile.ts,调用 Cloudflare siteverify API 校验 token - 难点:
NEXT_PUBLIC_变量需要在 build 时嵌入,最终通过next.config.ts的env字段解决
2. CORS 白名单
lib/cors.ts:只允许ai-humanizer-b70.pages.dev和 localhost 开发地址- 无 origin/referer 的请求放行(同源直接访问)
3. HMAC 签名验证
lib/signature.ts:Web Crypto API 实现 HMAC-SHA256- 前端:
app/humanize/page.tsx,每次请求生成 timestamp + nonce + HMAC 签名 - 后端:校验签名 + 时间戳新鲜度(5 分钟窗口)+ nonce 防重放
- Secret:
SIGN_SECRET环境变量,前后端共享
4. 结构化日志
lib/logger.ts:JSON 格式日志,包含 requestId、IP、各阶段(parse/cors/signature/turnstile/rate_limit/provider/success/error)- 每个请求有唯一 ID,方便排查问题
API 验证流程
请求 → CORS 检查 → 签名验证 → Turnstile 验证 → 限流检查 → 业务逻辑
部署详情
- Build:
next.config.ts添加env.NEXT_PUBLIC_TURNSTILE_SITE_KEY确保 build 时嵌入 - 环境变量(Cloudflare Pages):
ZHIPU_API_KEY— 智谱 APISIGN_SECRET— HMAC 签名密钥TURNSTILE_SECRET_KEY— Turnstile 后端密钥
- 最终部署地址:https://ai-humanizer-b70.pages.dev
当前产品状态
- 线上地址:https://ai-humanizer-b70.pages.dev/humanize ✅ 可用
- 安全防护:✅ Turnstile + CORS + 签名 三层
- 日志系统:✅ 结构化 JSON 日志
- 部署平台:Cloudflare Pages
- 代码分支:master / main(已同步)
- 打卡进度:5 次(最后 3/21)
三、Google OAuth 登录(晚间完成)✅
技术方案
- NextAuth.js v5 (beta.30) + Google Provider
- JWT Session Strategy:不需要数据库,session 存在 JWT token 里
- Auth 为可选功能,不强制登录,匿名用户照常可用
- 全部兼容 Cloudflare Workers Edge Runtime
新增/修改文件
| 文件 | 说明 |
|------|------|
| app/api/auth/[...nextauth]/route.ts | NextAuth 路由 handler,配置 Google provider + JWT callbacks |
| components/auth-provider.tsx | SessionProvider 包裹组件 |
| components/user-menu.tsx | 登录后显示用户头像、邮箱、登出按钮 |
| components/navbar.tsx | 集成 Sign In 按钮(未登录)+ UserMenu(已登录) |
| middleware.ts | NextAuth middleware(edge runtime) |
| app/layout.tsx | 包裹 AuthProvider |
遇到的坑
- NextAuth v5 废弃了
next-auth/middleware:v5 不再导出default和withAuth,middleware 里无法直接 re-export。最终在 route 里 exportGET/POSThandler,middleware 用withAuth从 auth core 导入 - Route handler 类型冲突:Next.js 15 要求 route export 符合
NextRequest → NextResponse类型,但 NextAuth handler 签名不匹配。用as any绕过 - Edge Runtime 标记缺失:Cloudflare Pages 要求所有非静态路由导出
export const runtime = 'edge',否则@cloudflare/next-on-pages构建失败 - Wrangler 缓存问题:
wrangler pages deploy默认跳过已上传文件(即使内容变了但文件名相同),导致多次部署看起来成功但线上没变化。需要加--skip-caching强制上传全部文件 - Production 分支不匹配:项目没有 git 集成(Source: None),所有 wrangler 部署都是 ad_hoc/preview 类型。需要在 Cloudflare Pages Settings 里手动添加环境变量(API 无权限),且部署到
mainbranch 才能命中 production alias _next-authexport 类型问题:v5 的 handler 不是标准的 Route handler 类型,需要 cast 到any才能通过 Next.js 类型检查
环境变量(Cloudflare Pages)
| 变量 | 用途 |
|------|------|
| GOOGLE_CLIENT_ID | Google OAuth Client ID |
| GOOGLE_CLIENT_SECRET | Google OAuth Client Secret |
| NEXTAUTH_SECRET | JWT 签名密钥 |
| NEXTAUTH_URL | 回调 URL(https://ai-humanizer-b70.pages.dev) |
Google Cloud Console 配置
- OAuth Client ID:
10551764124-pu94ko93v4te55r0eaf4lg03umr8rd4p.apps.googleusercontent.com - Authorized redirect URI:
https://ai-humanizer-b70.pages.dev/api/auth/callback/google
用户体验
- 导航栏右侧:未登录显示 Sign In 按钮 + Get Started 按钮
- 点击 Sign In → Google OAuth 授权 → 跳回 /humanize
- 登录后显示用户头像 + 邮箱 + 登出下拉菜单
- 移动端菜单同样有 Sign In with Google 按钮
四、D1 数据库集成 & NextAuth 修复(2026-03-24)✅
问题:NextAuth 线上 500 错误
- 现象:3/23 晚上配好的 Google 登录,3/24 早上发现
/api/auth/*全部 500 - 根因:Cloudflare Pages 的环境变量被之前的 API 操作覆盖清空了(
NEXTAUTH_SECRET缺失 → MissingSecret 错误) - 修复过程:
- 通过
wrangler pages deployment tail查看生产日志,确认错误是MissingSecret - 用 Cloudflare API 一次 PATCH 全部 7 个环境变量(
plain_text类型) - 重新 build + deploy,验证 csrf/signin/home 全部 200
- POST
/api/auth/signin/google成功 302 到 Google 授权页
- 通过
- 教训:CF Pages API PATCH 是全量替换不是增量更新;生产问题要第一时间看生产日志
D1 数据库绑定
- 数据库:
ai-humanizer-db(ID:8ed3fde1-ce90-4a5c-aa81-ea8bf32bbb16) - 绑定名:
DB - 已创建的表:users、humanization_history、daily_usage(migration:
migrations/0001_init.sql) - 访问层:
lib/db.ts,通过getRequestContext()获取 D1 - 验证:
/api/test-db确认 DB binding 可用、三张表读写正常 - wrangler.toml 中配置了
[[d1_databases]]绑定
用户菜单优化
- 之前:登录后显示 Google 头像 + 邮箱 + Sign Out 按钮
- 现在:
- 点击头像展开下拉菜单
- 显示用户名(从 Google 读取)、邮箱
- 无头像时显示名字首字母渐变圆形
- Humanize 页面 header 也统一了登录状态
最终环境变量(Cloudflare Pages Production)
| 变量 | 用途 |
|------|------|
| GOOGLE_CLIENT_ID | Google OAuth Client ID |
| GOOGLE_CLIENT_SECRET | Google OAuth Client Secret |
| NEXTAUTH_SECRET | JWT 签名密钥 |
| NEXTAUTH_URL | https://ai-humanizer-b70.pages.dev |
| ZHIPU_API_KEY | 智谱 AI API Key |
| SIGN_SECRET | HMAC 签名密钥 |
| TURNSTILE_SECRET_KEY | Cloudflare Turnstile 后端密钥 |
部署踩坑记录
- 环境变量被清空:之前用 PATCH 逐个设置变量,每次覆盖其他变量为空
secret_text类型值不可读:API 返回 success 但实际值可能没写入,改用plain_textNODE_ENV是 reserved binding:不能作为 Pages 环境变量名,会导致 deploy 失败- next build 间歇性失败:
Collecting build traces阶段偶尔 ENOENT,重跑即可 - 子 agent 干扰:超时的子 agent 进程在后台删 .vercel 目录,导致 deploy 间歇性失败
当前产品状态
- 线上地址:https://ai-humanizer-b70.pages.dev ✅
- 安全防护:✅ Turnstile + CORS + 签名 三层
- Google 登录:✅ NextAuth v5 + Google OAuth(已验证完整流程)
- D1 数据库:✅ 绑定成功,三张表可读写
- 用户菜单:✅ 头像 + 用户名 + 下拉菜单
- 日志系统:✅ 结构化 JSON 日志
- 部署平台:Cloudflare Pages
- 代码分支:master
待办
- [x] ~~Google 登录~~ ✅ 2026-03-23
- [x] ~~D1 数据库绑定~~ ✅ 2026-03-24
- [x] ~~用户菜单优化~~ ✅ 2026-03-24
- [ ] OAuth 回调后自动写入 D1 用户表
- [ ] 用户 Dashboard
- [ ] 使用历史记录页面
- [ ] 绑自定义域名
- [ ] 邮箱 + 验证码登录(备选方案)
- [ ] 支付集成(Stripe/PayPal)
- [ ] SEO 优化(meta tags、og tags、sitemap)
- [ ] Product Hunt 发布
- [ ] Reddit/Twitter 推广
- [ ] 博客内容矩阵