帮助文档中心
HeHe App 完整帮助文档 — 从项目搭建到部署上线的全流程指南。
文档概览
本帮助文档基于项目 docs/ 目录下的 9 份核心文档整合而成,覆盖从项目搭建、数据库集成、用户认证、支付接入到生产部署的全流程。文档采用左侧导航 + 右侧内容的经典布局,支持中英文双语切换。
文档结构
快速开始
项目定位、前置条件、环境变量、Mock DB、快速启动、FAQ
项目架构
技术栈、目录结构、多域名路由、中间件链、i18n 国际化
渲染策略
SSR/ISR/SWR 全维度对比、选型决策树、性能数字、routeRules 配置
Supabase 数据库集成
数据库迁移、RLS 策略、Storage、Seed Data、管理员创建、Connection Pooler
Vercel 部署
环境变量、域名配置、子域名路由、SSL、检查清单、预览部署
GitHub 与 CI/CD
GitHub 仓库管理、分支策略、Actions CI/CD、PR 流程、分支保护
用户认证
Email + Google/Facebook/Apple OAuth + 匿名登录、Token 管理、H5 组件
支付集成
Stripe Checkout + 支付策略工厂、Webhook、金额安全、Supabase Stripe 集成
社交分享与反馈
6大平台社交分享、用户评价系统、管理员审批工作流
Cloudflare 配置
DNS 管理、CDN 加速、安全防护、DNS-only 最佳实践
定位与技术栈
项目定位
本项目是单人全栈独立开发者闭环项目脚手架,一人负责开发、维护、上线、测试、运维全流程。在单一 Nuxt 4 代码仓库中同时支撑主站官网(SSR)、管理后台(SPA + PWA)、营销 H5 落地页(ISR)和 REST API 四类运行时。不是 SaaS 多租户产品,而是单人全栈项目的基础骨架。
技术栈
| 分类 | 技术方案 | 说明 |
|---|---|---|
| 前端框架 | Nuxt 4 (Vue 3 + Nitro) | future.compatibilityVersion: 4 |
| 样式方案 | UnoCSS | 原子化 CSS,按需生成 |
| 图片优化 | @nuxt/image | 自动压缩、格式转换、懒加载 |
| 国际化 | @nuxtjs/i18n | 中英文双语,prefix_except_default 策略 |
| PWA | @vite-pwa/nuxt | Admin 后台离线可用,Service Worker 缓存 |
| 数据库 | Supabase PostgreSQL | RLS 行级安全策略,Connection Pooler 连接池 |
| 认证 | Supabase Auth | 邮箱 + Google/Facebook/Apple OAuth + 匿名登录 |
| 支付 | Stripe Checkout + 支付策略工厂 | stripe/paypal/google-pay/apple-iap/manual,Mock/生产双模式 |
| 分析 | Vercel Analytics + APM 中间件 | 请求耗时监控 + Web Vitals |
| 部署 | Vercel | Git 推送自动部署,子域名自适应路由 |
前置条件
| 工具 | 版本要求 | 检查命令 | 用途 |
|---|---|---|---|
| Node.js | ≥ 20.x | node --version | 运行时 |
| npm | ≥ 10.x | npm --version | 包管理 |
| Supabase CLI | ≥ 1.x | supabase --version | 数据库迁移 |
| Git | ≥ 2.x | git --version | 版本控制 |
| Supabase 账号 | - | supabase.com/dashboard | 云数据库 |
| Vercel 账号 | - | vercel.com/dashboard | 部署平台 |
快速启动
# 安装依赖
npm install
# 启动开发服务器(Mock DB 模式,无需数据库)
npm run dev
# 启动 Supabase + 开发服务器
npm run dev:all
# 类型检查
npm run check
# 构建
npm run buildMock DB 模式(MOCK_DB=true)无需 Supabase 账号,所有数据存储在内存中,支持完整的 CRUD、Auth 模拟和链式查询,适合前端 UI 开发和快速原型验证。
渲染策略
| 路由 | 策略 | 说明 |
|---|---|---|
/ | ISR (3600s) | SEO 友好的静态生成,首次构建后增量再生 |
/architecture | ISR (3600s) | 技术架构白皮书 |
/help | ISR (3600s) | 帮助文档中心 |
/h5/** | SWR (600s) | 营销活动页,后台修改后最快 10 分钟更新 |
/admin/** | SPA (ssr: false) | 管理后台纯客户端渲染,隔离 SSR 安全泄露 |
/api/** | no-store | 实时 API 零缓存,每次请求实时响应 |
目录结构与路由
目录结构
hehe-app/
├── app/
│ ├── components/
│ │ ├── admin/ # 管理后台组件(20+ 个,local imports)
│ │ ├── client/ # 客户端网站组件
│ │ ├── h5/ # H5 营销页组件
│ │ └── shared/ # 跨平台共享组件(LanguageSwitcher, SocialShare)
│ ├── composables/ # Vue Composables(9个,auto-imported)
│ ├── pages/
│ │ ├── (admin)/ # 管理后台(SPA, ssr: false)
│ │ ├── (client)/ # 官网 + 架构 + 帮助文档(ISR 3600s)
│ │ └── (h5)/ # H5 营销活动页(ISR 600s)
│ ├── plugins/ # Nuxt 插件(analytics, supabase-auth)
│ ├── types/ # TypeScript 类型定义
│ ├── utils/ # 客户端工具函数
│ └── app.vue
├── locales/ # i18n 翻译文件(zh.json, en.json)
├── server/
│ ├── api/admin/ # 管理后台 API(03.admin 中间件保护)
│ ├── api/v1/ # 公开/用户 API
│ ├── middleware/ # 中间件链(00→01→02→03→04→06)
│ └── utils/ # 服务端工具(db, auth, payment-strategies, storage, api-security)
├── supabase/migrations/ # 数据库迁移(0001-0012,7个文件)
├── public/ # 静态资源(fonts, favicon, og-image)
├── scripts/ # 工具链脚本(10个 .mjs)
└── docs/ # 核心架构文档多域名路由设计
| 域名 | 路径 | 渲染策略 | 说明 |
|---|---|---|---|
| 根域名 | / | ISR (3600s) | |
| 根域名 | /architecture | ISR (3600s) | |
| 根域名 | /help | ISR (3600s) | |
其他子域名 | → /h5/{subdomain}/ | SWR (600s) | |
| 根域名 | /admin/** | SPA (ssr: false) | |
| 根域名 | /api/** | no-store |
中间件执行链
每个请求按编号顺序依次经过这 6 个中间件,形成清晰的安全管道。各中间件职责如下:
| 中间件 | 职责 | 说明 |
|---|---|---|
00.apm | 性能监控 | 记录 /api/ 路径的请求耗时与状态码,异步写入 APM 系统 |
01.subdomain | 子域名路由重写 | 根据 Host 头静默重写路由:主域名→/client,admin子域名→/admin,通配子域名→/h5/{subdomain} |
02.auth | 双模鉴权 | Mock模式(内存用户表)与生产模式(Supabase JWT验证),支持 Bearer/Cookie/device-id 多通道 |
03.admin | 管理员断言 | 拦截 /api/admin/*,验证管理员角色,放行定时任务 x-cron-secret |
04.auth-guard | 用户认证守卫 | 要求支付/订单/存储等敏感端点已登录,匿名用户返回 403 |
05.api-security | API 安全防护 | 8层安全检查:IP黑白名单、国家限制、API Key验证、HMAC签名、端点控制、速率限制 |
环境变量
环境变量清单
| 变量名 | 说明 | 必填 | 备注 |
|---|---|---|---|
NUXT_PUBLIC_SUPABASE_URL | Supabase 项目 URL | 是 | 前端可访问 |
NUXT_PUBLIC_SUPABASE_ANON_KEY | Supabase anon key | 是 | 前端可访问 |
SUPABASE_SERVICE_ROLE_KEY | Supabase service_role key | 是 | 仅服务端,禁止加 NUXT_PUBLIC_ 前缀 |
MOCK_DB | Mock 数据库开关 | 是 | 本地开发 true,生产 false |
SUPABASE_SERVICE_ROLE_KEY 绝对不能加 NUXT_PUBLIC_ 前缀。支付密钥已迁移至 DB 管理,不再通过环境变量传递。
Mock DB 离线开发
项目内置完整的内存 Mock PostgreSQL 适配器,设置 MOCK_DB=true 即可完全离线开发,无需 Supabase 物理数据库。支持链式查询、聚合统计、CRUD 操作和 Auth 模拟。
渲染策略对比
三种渲染模式
SSR、ISR、SWR 三种渲染模式是现代全栈框架的核心能力。三者本质上都是「服务端介入渲染」,核心差异在于何时渲染、缓存多久、谁触发更新。选错渲染策略,轻则 LCP 超标,重则服务器在流量峰值下崩溃。
| 对比维度 | SSR | ISR | SWR |
|---|---|---|---|
| 渲染时机 | 每次请求实时渲染 | 首次渲染并缓存,后台定时刷新 | 过期后返回旧缓存,后台异步刷新 |
| TTFB | 200-800ms(每次) | ~5ms(命中缓存) | ~5ms(命中缓存) |
| 数据新鲜度 | 100% 实时 | 最多延迟 revalidate 秒 | 最多延迟 maxAge 秒 |
| 服务器压力 | 高(每请求计算一次) | 极低(缓存期零计算) | 低(过期后异步一次) |
| SEO 友好度 | 极佳 | 极佳 | 极佳 |
| 适合更新频率 | 秒级 | 小时/天级 | 分钟/小时级 |
| Nuxt 4 配置 | ssr: true | isr: 3600 | swr: 600 |
机制原理
SSR:每次请求实时计算 — 用户请求 → 服务器查 DB/调 API → 渲染 HTML → 返回。每次都要计算。
ISR:首次构建 + 后台静默刷新 — 首次请求渲染并写入边缘缓存(TTL=3600s)。缓存期内直接返回(~5ms),过期后先返回旧缓存,后台触发重新计算。用户无感知,永远不等待。
SWR:过期即用旧缓存 + 后台异步更新 — 首次请求渲染并写入缓存(maxAge=600s)。缓存期内 ~5ms 返回,过期后立即返回旧缓存同时后台异步刷新。与 ISR 行为几乎一致,区别在时间窗口语义。
ISR 和 SWR 在「过期后先返回旧缓存、后台异步刷新」行为上几乎完全一致。核心区别:ISR 适合小时/天级低频更新内容,SWR 适合分钟级中频更新内容。
选型决策树
维度一:数据更新频率
- 秒级变化:SSR(股价、实时弹幕、直播数据)
- 分钟到小时级:SWR(营销活动、商品库存、新闻)
- 小时到天级:ISR(官网介绍、博客、定价页)
维度二:SEO 重要性
- 不重要:SPA(管理后台、用户私有页、订单详情)
- 重要:按数据更新频率三选一(ISR / SWR / SSR)
本项目选型定论
| 页面/场景 | 选型 | 配置 | 理由 |
|---|---|---|---|
| 官网首页 | ISR | isr: 3600 | 每日更新一次足够,极致 LCP |
| H5 活动页 | SWR | swr: 600 | 活动配置随时可改,容忍 10 分钟延迟 |
| 管理后台 | SPA | ssr: false | 无 SEO 需求,强权限隔离 |
| API 接口 | 无渲染 | no-store | 纯 JSON,绝对禁止缓存 |
routeRules 配置速查
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
'/admin/**': { ssr: false }, // 管理后台 SPA,隔离 SSR 泄露
'/h5/**': { swr: 600 }, // H5 SWR,10min 刷新
'/': { isr: 3600 }, // 首页 ISR,1h 刷新
'/architecture': { isr: 3600 }, // 架构白皮书 ISR
'/help': { isr: 3600 }, // 帮助文档 ISR
'/api/**': { cors: true, headers: { 'cache-control': 'no-store, no-cache, must-revalidate' } },
}
})常见误区
Supabase 数据库集成
创建 Supabase 项目
- Supabase Dashboard 中创建新项目
- New Project — 填写项目名称和数据库密码
- Settings → API — 获取项目 URL 和 anon key
- 将 URL 和 key 配置到项目 .env 文件
数据库 Schema 概览
Connection Pooler(Serverless 必配)
Vercel Serverless Functions 每次请求都可能创建新连接,容易打满 Supabase Free 计划的 10 个直连上限。在 Supabase Dashboard → Settings → Database → Connection Pooler 中启用。
| 模式 | 连接上限 | 适用场景 |
|---|---|---|
| 直连 (Direct) | 10 (Free) / 20 (Pro) | 本地开发、Supabase Studio |
| 连接池 (Pooler) | 200 (Free) / 500 (Pro) | Serverless 生产环境 |
使用 Pooler 时需注意:
- 连接字符串端口从 5432 改为 6543
- Pooler 使用 session 模式,支持 prepared statements
- Serverless 中必须使用 Pooler 连接,否则高峰期会耗尽连接
执行数据库迁移
数据库迁移只创建表结构,不包含业务数据。迁移文件按顺序编号,必须依次执行。
方式一:SQL Editor 手动执行(推荐首次)
在 Supabase Dashboard → SQL Editor 中,按顺序复制粘贴迁移文件内容并执行。每次执行一个文件,确认无报错后再执行下一个。执行后进入 Table Editor 确认所有表已创建。
方式二:Supabase CLI 自动推送(推荐)
supabase login
supabase link --project-ref <your-project-ref>
supabase db push填充初始数据(种子数据)
数据库迁移仅创建表结构,不包含业务数据。如果切换为物理数据库而未填充初始数据,H5 页面会因为 campaigns 表为空而白屏。
在 Supabase Dashboard → SQL Editor 中运行以下 SQL 以插入营销活动及商品初始数据:
-- 插入营销活动种子数据(H5 页面依赖 campaigns 表,迁移后必须执行)
-- 来源:supabase/migrations/0003_campaign.sql
INSERT INTO campaigns (subdomain, title, subtitle, badge, color_from, color_to, is_active, cta_text, description, features) VALUES
('ai', '🤖 HEHE AI 协作者首发', '基于先进智能体的全自动化提效工作流上线。立即预约,锁定首月免费体验资格。', '限时 10,000 名', 'from-purple-600', 'to-indigo-600', true, '立即预约', '基于先进智能体的全自动化提效工作流', '[{"icon":"⚡","text":"一键生成"},{"icon":"🔒","text":"安全沙盒"},{"icon":"🌐","text":"全球分发"}]'::jsonb),
('cloud', '☁️ HEHE 云原生企业私有化', '一键输出物理隔离安全沙盒,专为合规与核心系统容灾设计。首发限时 7 折特惠。', '企业专属首发', 'from-blue-600', 'to-cyan-600', true, '立即预约', '专为合规与核心系统容灾设计', '[{"icon":"🛡️","text":"物理隔离"},{"icon":"📊","text":"实时监控"},{"icon":"🔄","text":"自动容灾"}]'::jsonb),
('promo', '🚀 HEHE 全栈单仓极速版', '仅需单人即可撬动完整的全球边缘分发与 Supabase 强类型契约防御。', '开发者特惠季', 'from-rose-600', 'to-orange-600', true, '立即预约', '单人全栈闭环交付', '[{"icon":"🧑💻","text":"单人交付"},{"icon":"💰","text":"降本提效"},{"icon":"🚀","text":"极速上线"}]'::jsonb);
-- 插入商品种子数据(支付功能可选,来源:supabase/migrations/0002_iap.sql)
INSERT INTO products (name, price, tenant_id) VALUES
('HEHE Pro 工具套件', 29.99, 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'),
('HEHE Enterprise 全套方案', 299.00, 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11');
创建管理员账号
数据库迁移完成后,需要配置管理员账号。管理后台已完全切换到 Supabase Auth 认证,管理员通过 Supabase 邮箱登录。
方式一:通过 Dashboard 创建(推荐)
- 进入 Authentication → Users → Add User,填写邮箱和密码,勾选 Auto Confirm User
- 进入 Table Editor → profiles,找到刚创建的用户行,将 role 字段改为 admin
方式二:通过 SQL 快速设置
-- 通过邮箱设置管理员(在 SQL Editor 中执行)
UPDATE profiles
SET role = 'admin'
WHERE id = (
SELECT id FROM auth.users WHERE email = 'your-email@example.com'
);方式三:通过 CLI 脚本创建/更新
项目提供 temp-create-admin.mjs 脚本,可通过 CLI 快速创建或更新内置管理员账号:
node temp-create-admin.mjsOAuth 社交登录配置
在 Supabase Dashboard → Authentication → Providers 中配置第三方登录。
| Provider | 需要配置 | 获取地址 |
|---|---|---|
| Client ID + Client Secret | Google Cloud Console → APIs & Services → Credentials | |
| App ID + App Secret | Meta for Developers → 我的应用 | |
| Apple | Service ID + Key ID + Private Key | Apple Developer → Certificates, Identifiers & Profiles |
配置完成后,将 Supabase Dashboard 提供的 Redirect URL 填入各平台的回调地址白名单。
Supabase Storage
项目内置 Supabase Storage 支持,提供 3 个 Bucket 覆盖全部业务场景,RLS 策略内置于 0001_core.sql 迁移中:
| Bucket | 可见性 | 大小限制 | 允许类型 | 写入权限 |
|---|---|---|---|---|
avatars | 公开 | 2 MB | png / jpeg / gif / webp | 认证用户写自己目录 |
campaign-assets | 公开 | 10 MB | png / jpeg / gif / webp / mp4 | 仅管理员 |
uploads | 私有 | 50 MB | 不限制 | 认证用户写自己目录 |
Storage 路径规范与 RLS 隔离
所有文件路径遵循 {user_id}/{timestamp}_{filename} 格式。RLS 策略通过 (storage.foldername(name))[1] = auth.uid()::text 校验路径首段与用户 uid 一致,实现行级隔离:
- avatars:认证用户可上传/更新/删除自己目录下的文件,所有人可公开读取,管理员全权限
- campaign-assets:仅管理员可写入/更新/删除,所有人可公开读取
- uploads:认证用户仅可读写自己目录下的文件,管理员全权限
混合上传策略
项目采用混合上传模式,兼顾安全与性能:
| 文件大小 | 模式 | 流程 |
|---|---|---|
| < 5 MB | 服务端中转 | 客户端 → POST /api/v1/storage/upload → Nitro 用 service_role 写入 Storage |
| ≥ 5 MB | 客户端直传 | 客户端 → POST /api/v1/storage/signed-url 获取签名 → 直传 Supabase Storage |
Storage API 端点
| 方法 | 路径 | 说明 |
|---|---|---|
POST | /api/v1/storage/upload | 服务端中转上传(小文件) |
POST | /api/v1/storage/signed-url | 生成直传签名 URL(大文件) |
DELETE | /api/v1/storage/{bucket}/{user_id}/{filename} | 删除文件 |
GET | /api/v1/storage/signed-url/{bucket}/{user_id}/{filename} | 获取私有文件临时访问 URL |
所有端点均需认证(@api-auth: user),由 04.auth-guard.ts 中间件统一拦截。
useStorage() Composable
前端使用 useStorage() composable 进行文件操作,自动选择上传模式:
const { upload, remove, getSignedUrl, getPublicUrl } = useStorage()
// 上传文件(自动判断大小,选择中转或直传)
const result = await upload(file, 'avatars')
console.log(result.path, result.publicUrl)
// 删除文件
await remove('avatars', 'user-id/1234_photo.png')
// 获取私有文件临时访问链接
const url = await getSignedUrl('uploads', 'user-id/5678_doc.pdf')
// 获取公开文件 URL(无需 API 请求)
const publicUrl = getPublicUrl('avatars', 'user-id/1234_photo.png')Storage RLS 安全加固
Supabase 对 public bucket 默认创建的 DELETE 策略过于宽松(anon 用户可删除任意文件)。0001_core.sql 添加了 RESTRICTIVE 策略进行加固:
- storage_scope_restrict:限制所有操作只能在 3 个业务 bucket 范围内
- campaign_assets_restrict_delete:campaign-assets bucket 禁止非管理员删除
- uploads_restrict_anon:anon 用户完全禁止访问 uploads bucket
注意:RESTRICTIVE 策略与 PERMISSIVE 策略是 AND 逻辑。Supabase Storage API 的 remove() 在 RLS 阻止删除时返回 error=null, data=[](假成功),需检查文件是否仍存在。
本地 Supabase 开发环境
# 安装 Supabase CLI
npm install -g supabase
# 初始化本地 Supabase
supabase init
# 启动全套服务(PG + Auth + Storage + Studio)
supabase start
# Studio 地址: http://localhost:54323
# API URL: http://localhost:54321
# Anon Key: 在 Studio → Settings → API 中查看本地 Supabase 包含完整的 PostgreSQL、Auth、Storage 和 Studio 界面,适合离线开发和测试。
连接验证与排查
| 检查项 | 命令/位置 | 预期结果 |
|---|---|---|
| 环境变量配置 | cat .env | grep SUPABASE | URL + anon key + service_role key 都已填入 |
| 数据库连接 | npm run test:supabase | 所有表检查通过 |
| Storage 可用 | npm run test:storage | 上传/下载/签名URL 正常 |
| RLS 策略 | Dashboard → SQL Editor | 所有表 ENABLE ROW LEVEL SECURITY |
| 迁移状态 | supabase migration list | 所有迁移文件状态为 Applied |
| 类型生成 | npm run gen:types | 无报错,生成最新类型 |
| OAuth 配置 | Dashboard → Authentication → Providers | 回调 URL 已配置 |
| 帮助文档页面 | 访问 /help | 所有章节内容完整显示 |
迁移同步问题排查
- 迁移未应用:
supabase db push推送本地迁移到远程 - 迁移冲突:
supabase db pull拉取远程 schema,手动合并差异 - 迁移损坏:
supabase migration repair <version> --status applied标记迁移状态 - 重置本地数据库:
supabase db reset清空本地数据库并重新执行所有迁移
新增迁移标准流程
supabase migration new <name>创建新迁移文件- 在生成的 SQL 文件中编写 DDL 语句(CREATE TABLE、RLS 策略等)
supabase db reset本地验证迁移是否正确执行supabase db push推送到远程 Supabase 项目npm run gen:types重新生成 TypeScript 类型
SQL 编写规范:
- 所有表必须启用 RLS + FORCE RLS
- Admin 检查使用
is_admin(auth.uid()),禁止 inline EXISTS 子查询 is_admin()函数使用 SECURITY DEFINER 避免递归权限问题- Money 字段使用 NUMERIC,禁止浮点数
- activity_logs 表只追加不删除,定期归档
- 迁移文件按顺序编号(0001, 0002_iap...),禁止修改已推送的迁移文件
- Serverless 环境必须启用 Connection Pooler,避免直连耗尽连接数
- 迁移文件按顺序编号(0001, 0002...),禁止修改已推送的迁移文件
TypeScript 类型生成
# 生成 Supabase 数据库类型定义
npm run gen:types
# 生成的文件:app/types/database.types.ts
# 每次数据库结构变更后都需要重新生成生产回滚流程
- 数据库回滚:
supabase db push推送回滚迁移文件 - 代码回滚:
git revert <commit-hash>或通过 Vercel Dashboard 回滚到上一个部署 - 验证:确认所有功能正常后,重新生成类型并运行测试
快速参考命令
# 数据库连接测试
npm run test:supabase
# Storage 集成测试
npm run test:storage
# 生成 TypeScript 类型
npm run gen:types
# 推送迁移到远程
supabase db push
# 拉取远程 schema
supabase db pull
# 创建新迁移
supabase migration new my_migration
# 重置本地数据库
supabase db reset
# 生成 RLS 策略 SQL
npm run gen:rls <table> [--admin]用户认证
支持的登录方式
| 方式 | SDK 方法 | API 端点 | 备注 |
|---|---|---|---|
| 邮箱密码 | signInWithPassword | POST /api/v1/auth/login | 需要注册 |
| Google OAuth | signInWithOAuth | provider: google | 海外主流用户 |
| Facebook OAuth | signInWithOAuth | provider: facebook | 东南亚/拉美用户 |
| Apple OAuth | signInWithOAuth | provider: apple | iOS 用户(App Store 审核要求) |
| 匿名 | signInAnonymously + device-id | - | 先浏览后登录,数据可迁移 |
profiles 表核心字段
| 字段 | 类型 | 说明 |
|---|---|---|
id | UUID | 与 auth.users 一对一关联 |
email | TEXT | 用户邮箱 |
username | TEXT | 用户名(可选) |
avatar_url | TEXT | 头像 URL(可选) |
role | TEXT | user / admin,管理员手动设置 |
is_anonymous | BOOLEAN | 是否为匿名用户 |
created_at | TIMESTAMPTZ | 注册时间 |
updated_at | TIMESTAMPTZ | 最后更新时间 |
Token 生命周期
| Token 类型 | 格式 | 有效期 | 传递方式 |
|---|---|---|---|
| Access Token | JWT | 1 hour | Bearer Header 或 Cookie |
| Refresh Token | Opaque | 30 days | 自动刷新 Access Token |
| Device ID | UUID | 持久化 | 匿名用户的标识 |
服务端 API
| 方法 | 端点 | 说明 |
|---|---|---|
POST | /api/v1/auth/login | 统一登录入口(邮箱/OAuth/匿名) |
POST | /api/v1/auth/register | 注册新用户 |
POST | /api/v1/auth/link | 匿名用户绑定邮箱 |
GET | /api/v1/auth/me | 获取当前用户 profile |
PATCH | /api/v1/auth/profile | 更新用户信息 |
POST | /api/v1/auth/logout | 登出 |
Cookie 安全
Auth Cookie 使用 SameSite=Strict + Secure(HTTPS)策略,最大化 CSRF 防护。服务端中间件使用 getUser()(非 getSession())验证 JWT,遵循 Supabase 推荐的最佳实践。
handle_new_user 触发器
新用户注册时,Supabase 自动在 profiles 表中创建对应记录。触发器逻辑:
CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS trigger AS $$
BEGIN
INSERT INTO public.profiles (id, email, role)
VALUES (NEW.id, NEW.email, 'user');
RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
CREATE TRIGGER on_auth_user_created
AFTER INSERT ON auth.users
FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();useAuth() Composable API
前端通过 useAuth() composable 管理认证状态,提供以下方法:
| 方法 | 说明 |
|---|---|
user | 当前用户信息(响应式 ref) |
isLoggedIn | 是否已登录(computed) |
isAnonymous | 是否匿名用户(computed) |
isAdmin | 是否为管理员(computed) |
signUpWithEmail(email, password) | 邮箱注册 |
signInWithEmail(email, password) | 邮箱密码登录 |
signInWithOAuth(provider) | OAuth 登录(google/facebook/apple) |
signInAnonymously() | 匿名登录 |
linkAnonymousToEmail(email, password) | 匿名绑定邮箱 |
signOut() | 登出 |
中间件身份解析链
服务端按顺序执行以下中间件,完成身份解析与权限控制:
- 00.apm:APM 性能监控,记录请求耗时与状态码
- 01.subdomain:子域名自适应路由重写(主域名→/client,admin→/admin,通配子域名→/h5/{subdomain})
- 02.auth:身份解析,优先级:Bearer Header → Cookie(sb-access-token)→ 匿名 device-id
- 03.admin:管理员断言守卫,拦截 /api/admin/*,非 admin 返回 403
- 04.auth-guard:用户认证守卫,支付/订单等敏感端点要求已登录,匿名用户返回 403
- 05.api-security:API 安全策略:IP 黑白名单、国家限制、API Key、HMAC 签名、端点控制、速率限制
OAuth Provider 申请指南
Google OAuth:
- 前往 Google Cloud Console → APIs & Services → Credentials
- 创建 OAuth 2.0 Client ID,应用类型选择 Web application
- 在 Authorized redirect URIs 中添加 Supabase Dashboard 提供的回调 URL
- 将 Client ID 和 Client Secret 填入 Supabase Dashboard → Authentication → Providers → Google
Facebook OAuth:
- 前往 Facebook Developers → My Apps → Create App
- 添加 Facebook Login 产品,配置 Valid OAuth Redirect URIs
- 将 App ID 和 App Secret 填入 Supabase Dashboard → Authentication → Providers → Facebook
Apple OAuth:
- 前往 Apple Developer → Certificates, Identifiers & Profiles → Services IDs
- 创建 Service ID 并启用 Sign in with Apple
- 配置回调域名为 Supabase 提供的 auth/v1/callback,下载 Private Key 填入 Supabase Dashboard
匿名→绑定数据迁移
匿名用户登录后可以绑定正式账号,服务端会自动将匿名期间的设备 ID 关联数据迁移到正式用户 ID 下。迁移策略:
- 匿名用户的所有数据以 device_id 为标识
- 绑定正式账号后,触发 link_identity() 将 device_id 映射到 user_id
- 绑定后的查询自动使用 user_id,无缝衔接
H5 前端组件
H5 营销页面提供完整的登录/用户组件:
H5UserBar:用户状态栏,显示头像/昵称/登出按钮H5LoginModal:登录弹窗,支持邮箱/Google/Facebook/Apple/匿名五种方式- 两个组件均支持 i18n 中英文双语,通过 t() 函数切换语言
常见认证问题
Vercel 部署
部署流程
- 将项目代码推送到 GitHub
- Vercel Dashboard → 导入 Git 仓库
- 配置生产环境变量(所有 .env 中的变量)
- 设置 Framework Preset 为 Nuxt.js,Node.js Version ≥ 20.x
- 部署完成,自动分配 vercel.app 域名
生产环境变量配置
在 Vercel Dashboard → 你的项目 → Settings → Environment Variables 中逐项配置:
| 变量名 | 说明 | 是否必须 |
|---|---|---|
MOCK_DB | 关闭 Mock 沙盒,设为 false | 必须 |
SUPABASE_URL | Supabase 项目 URL(服务端) | 必须 |
SUPABASE_SERVICE_ROLE_KEY | Supabase 服务端密钥(禁止加 NUXT_PUBLIC_ 前缀) | 必须 |
NUXT_PUBLIC_SUPABASE_URL | Supabase URL(前端公开) | 必须 |
NUXT_PUBLIC_SUPABASE_ANON_KEY | Supabase anon 公钥(前端公开) | 必须 |
SUPABASE_SERVICE_ROLE_KEY 绝对不能加 NUXT_PUBLIC_ 前缀。只有 NUXT_PUBLIC_ 前缀的变量才会暴露给浏览器端。支付密钥已迁移至 DB。
⚠️ 环境变量修改后需重新部署才能生效。可推送一个空 commit 触发:
git commit --allow-empty -m "chore: redeploy for env update" && git push渲染策略
| 路由 | 策略 | 说明 |
|---|---|---|
/ | ISR (3600s) | SEO 友好的静态生成,首次构建后增量再生 |
/architecture | ISR (3600s) | 技术架构白皮书 |
/help | ISR (3600s) | 帮助文档中心 |
/h5/** | SWR (600s) | 营销活动页,后台修改后最快 10 分钟更新 |
/admin/** | SPA (ssr: false) | 管理后台纯客户端渲染,隔离 SSR 安全泄露 |
/api/** | no-store | 实时 API 零缓存,每次请求实时响应 |
域名配置
| 类型 | 域名 | 记录类型 | 值 | 说明 |
|---|---|---|---|---|
主域名 | example.com | A | 76.76.21.21 | Vercel IP |
通配符 | *.example.com | CNAME | cname.vercel-dns.com | 子域名转发 |
站点 URL 与子域名路由
项目采用零配置方案,站点 URL 自动适配不同环境:
| 环境 | URL 来源 | 示例 |
|---|---|---|
| 本地开发 | 默认值 | http://localhost:3000 |
| Vercel Preview | VERCEL_URL(自动注入) | https://hehe-app-git-main.vercel.app |
| Vercel Production | VERCEL_URL(绑定域名后自动) | https://yourdomain.com |
子域名路由中间件自动派生逻辑:
- server/middleware/01.subdomain-rewrite.ts 自动从运行环境中提取根域名,在 Vercel 绑定自定义域名后自动激活通配符 H5 子域名,无需手动设置。
- 本地开发子域名需要配置 ROOT_DOMAIN=yourdomain.localhost 环境变量,并配合本地 host 映射。
- 可配置最高优先级的 NUXT_PUBLIC_BASE_URL 环境变量,显式覆盖站点根路径。
部署检查清单
- ✅ 所有 .env 变量已在 Vercel Dashboard → Environment Variables 中配置
- ✅ Supabase 项目 URL + anon key + service_role key 已填入
- ✅ MOCK_DB 设置为 false
- ✅ 管理员账号已通过 Supabase Dashboard 或 temp-create-admin.mjs 脚本创建
- ✅ Stripe 密钥已配置(如需支付功能)
- ✅ npm run gen:types 已执行
- ✅ supabase db push 已执行(所有迁移已应用)
- ✅ npm run check 通过
- ✅ npm run build 通过(无 prerender 错误)
- ✅ DNS 通配符记录已配置(*.domain.com → cname.vercel-dns.com)
Vercel Analytics & Speed Insights
Vercel 提供内置的性能监控与流量分析工具:
安装统计与监控依赖:
# 安装 Vercel 性能分析与监控包
npm i @vercel/analytics @vercel/speed-insights在 app.vue 中引入并挂载(Nuxt 4 集成方式):
<script setup lang="ts">
import { Analytics } from '@vercel/analytics/nuxt'
import { SpeedInsights } from '@vercel/speed-insights/nuxt'
</script>
<template>
<div>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
<!-- 挂载监控组件 -->
<Analytics />
<SpeedInsights />
</div>
</template>- Analytics:在 Vercel Dashboard → Analytics 中一键启用,自动收集 PV、UV、访问来源及设备类型。
- Speed Insights:监控 Core Web Vitals 指标(如 LCP、CLS、INP),为产品性能优化提供高价值建议。
- Hobby 计划包含基础的分析数据,Pro 计划提供更长的历史保留期及多维度高级数据。
Stripe Webhook 配置
支付功能需要在 Stripe Dashboard 和 Vercel 中配置 Webhook:
- 在 Stripe Dashboard → Developers → Webhooks → Add endpoint
- Endpoint URL 设置为
https://your-domain.com/api/v1/payments/webhook - 选择要监听的事件:
payment_intent.succeeded,payment_intent.payment_failed - 获取 Webhook Signing Secret 并填入管理后台「业务运营 → 支付管理」→ Stripe Webhook Secret
- 本地测试:
stripe listen --forward-to localhost:3000/api/v1/payments/webhook
Vercel CLI 部署(可选)
除了 Dashboard 导入,也可以通过 Vercel CLI 部署:
# 安装 Vercel CLI
npm i -g vercel
# 登录并部署
vercel login
vercel
# 生产部署
vercel --prodPreview Deployments(预览部署)
每次推送代码到非 main 分支或创建 Pull Request 时,Vercel 都会自动创建一个独立的预览部署。
预览部署的典型开发流程示例:
# 1. 检出新功能分支
git checkout -b feature/new-campaign
# 2. 开发完毕后提交并推送到 GitHub
git add .
git commit -m "feat: design new H5 campaign page"
git push origin feature/new-campaign
# → Vercel 会自动监听该推送,并生成带有该分支名称的预览 URL预览环境特点及数据库隔离:
- PR 关联:Vercel 会自动在 GitHub 的 PR 评论中提供该环境的预览链接。
- 独立配置:预览环境默认复用生产环境变量。如需测试数据库隔离,可在 Vercel 后台为 Preview 环境配置专门的测试数据库 API 凭证,或在 Pro 团队计划中使用 Supabase Branching。
- 合并上线:预览环境验证通过后,将 PR 合并到 main 分支,即会自动触发生产环境的增量更新。
Vercel 计划选择
| 计划 | 价格 | 适合场景 |
|---|---|---|
| Hobby | 免费 | 个人项目,每日 100GB 带宽,ISR 缓存 3600s 上限 |
| Pro | $20/月 | 商业项目,1TB 带宽,更长 ISR 缓存,团队协作 |
| Enterprise | 定制 | 企业级,无限带宽,专属支持,SLA 保障 |
常见部署问题
GitHub 与 CI/CD
前置准备
使用 GitHub 进行代码托管与 CI/CD 流程:
- 注册 GitHub 账号,并推荐在本地配置 Git 身份信息:
- 安装 GitHub CLI(推荐),方便通过命令行管理 PR 与仓库:
git config --global user.name "Your Name"
git config --global user.email "your-email@example.com"brew install gh
gh auth login创建与推送仓库
将本地项目关联并推送到 GitHub 远程私有仓库:
- 在 GitHub 上创建一个名为
hehe-app的私有仓库(Private)。 - 在本地项目根目录下,运行以下命令进行关联并推送:
# 初始化 git 并设置默认分支为 main
git init
git branch -M main
# 关联远程仓库
git remote add origin https://github.com/your-username/hehe-app.git
# 首次推送代码
git add .
git commit -m "Initial commit: Nuxt 4 + Supabase + Vercel"
git push -u origin main.gitignore 配置
确保敏感和不必要的构建文件不被提交。项目根目录下应包含完整的 .gitignore:
# 依赖与临时文件
node_modules/
.nuxt/
.output/
.data/
.nitro/
.cache/
# 环境变量 (严禁提交敏感密钥)
.env
.env.*
!.env.example
# 构建产物与平台临时目录
dist/
.vercel/.env 文件包含 Supabase 和 Stripe 密钥,绝对不能提交到 GitHub。如果误提交,需要立即在服务商后台重置/轮换所有密钥,并使用 git-filter-repo 彻底清理 Git 历史。
分支策略
| 分支 | 说明 |
|---|---|
main | 生产分支,自动部署到 Vercel Production |
feature/* | 功能分支,合并到 main 后自动预览部署 |
fix/* | 修复分支,同 feature 流程 |
hotfix/* | 紧急修复,直接基于 main 创建 |
GitHub Actions CI
在 .github/workflows/ci.yml 中配置自动类型检查和构建验证:
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20', cache: 'npm' }
- run: npm ci
- run: npm run check
- run: npm run buildCommit 消息规范
遵循 Conventional Commits 规范:
| 类型 | 说明 | 示例 |
|---|---|---|
feat | 新功能 | feat: add user avatar upload |
fix | Bug 修复 | fix: resolve login redirect loop |
docs | 文档更新 | docs: update API reference |
refactor | 代码重构 | refactor: extract auth middleware |
chore | 构建/工具变更 | chore: update dependencies |
分支保护建议
- main 分支必须通过 PR 合并
- 合并前必须通过 CI 检查
- 禁止直接 push 到 main
- 代码审查至少 1 人 Approve
与 Vercel 联动
GitHub 仓库与 Vercel 深度集成:
- Push main → Production:推送到 main 分支自动触发 Vercel 生产部署
- Push branch → Preview:推送到任意分支自动创建 Vercel 预览部署
- PR → Preview:每个 PR 自动生成独立预览 URL,方便 Code Review 时验证
- .gitattributes 配置统一 LF 换行符,避免 Windows/Mac/Linux 跨平台差异
PR 模板
在 .github/pull_request_template.md 中创建 PR 模板:
## 变更说明
## 变更类型
- [ ] 新功能
- [ ] Bug 修复
- [ ] 文档更新
- [ ] 重构
## 测试
## 检查清单
- [ ] 代码通过 type check (npm run check)
- [ ] 代码通过 build (npm run build)
- [ ] API 安全扫描通过 (npm run test:api-safety)
- [ ] 相关文档已更新Pull Request 工作流与合并
PR 是单仓或多团队协作中保证主干代码质量的核心工作流:
- 创建 PR:在 GitHub 推送 feature 分支后,点击仓库顶部的 Compare & pull request,根据模板填写 Title 和 Description。
- 状态检查:等待 GitHub Actions 自动运行,确认编译与类型检查(Status checks)全部变为绿色通过状态。
- 选择合并方式:
- Merge commit:保留所有细分 commit 提交记录及分支线(推荐,最完整)。
- Squash and merge:将分支上的所有提交压缩合并为一个 clean commit 写入主干(适合碎片化小功能)。
- Rebase and merge:采用变基的形式保持一条线性的主干提交历史。
- 触发上线:点击 Confirm merge 合并后,Vercel 将自动接管并将代码部署上线至生产环境。
安全实践
如果密钥被意外提交到 Git 仓库:
- 立即在 Supabase/Stripe Dashboard 中撤销泄露的密钥
- 使用 git-filter-repo 清理 Git 历史中的敏感信息
- 在 GitHub → Settings → Secrets 中重新生成密钥
- 强制推送清理后的历史(需团队协调)
- .gitattributes 配置统一 LF 换行,避免跨平台差异
- GitHub Secrets 存储 CI 所需的敏感环境变量
- 定期审查仓库的 Collaborator 和 Access Token 权限
常见 GitHub 问题
支付集成
支付流程
双模式运行
| 模式 | 配置 | 行为 |
|---|---|---|
| Mock 模式 | MOCK_DB=true | 返回模拟支付数据,不调用 Stripe API,适合前端开发 |
| 生产模式 | MOCK_DB=false | 调用真实 Stripe API,支持一次性付款 + 订阅制(subscription) |
支付策略工厂
支付系统采用策略工厂模式(server/utils/payment-strategies/),统一接口、按需扩展:
| 文件 | 职责 | 说明 |
|---|---|---|
types.ts | 策略接口定义 | PaymentStrategy 接口:createSession + verifyWebhook |
factory.ts | 策略工厂 | 按 provider 名获取策略实例,支持 stripe / paypal / google-pay / apple-iap / manual |
stripe.ts | Stripe 策略 | Stripe Checkout Session + Webhook 签名验证 |
Stripe Webhook 事件:checkout.session.completed、charge.refunded
双通道回调:前端 success_url 跳转 + 服务端 Webhook 异步通知,双重保障订单状态同步。
Supabase 内置 Stripe 集成方案
Supabase 提供两套官方 Stripe 数据集成方案,定位不同:
| 方案 | 原理 | 适用场景 |
|---|---|---|
Stripe Sync Engine | Webhook + 定时回填,将 Stripe 数据同步到本地 Postgres 表(stripe.customers/subscriptions/invoices) | 账单分析、MRR、流失率统计 |
Stripe FDW | Foreign Data Wrapper,实时将 SQL 翻译为 Stripe API 调用 | 偶尔的简单查询 |
本项目选择直接调用 Stripe SDK 处理支付流程;如仅需账单数据分析,可使用 Supabase Dashboard → Integrations → Stripe Sync Engine 一键安装。
orders 表 RLS 策略
- orders_user_own:用户可查看和创建自己的订单(SELECT + INSERT 合并)
- orders_admin_all:管理员可查看所有订单
- 只有管理员可更新订单状态
- 订单金额由服务端计算,前端不可篡改
usePayment() Composable
前端通过 usePayment() composable 管理支付流程:
| 方法 | 说明 |
|---|---|
createAndRedirect(params) | 创建订单并跳转 Stripe Checkout |
checkOrderStatus(orderId) | 查询订单状态 |
pollOrderStatus(orderId) | 轮询订单状态 |
Checkout Session 参数
创建 Stripe Checkout Session 的关键参数:
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line_items: [{
price_data: {
currency: 'usd',
product_data: { name: 'Product Name' },
unit_amount: Math.round(amount * 100), // 美元转美分
},
quantity: 1,
}],
mode: 'payment',
success_url: `${origin}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${origin}/cancel`,
metadata: { orderId: order.id },
})金额安全
- 数据库层:amount NUMERIC(10, 2) CHECK (amount >= 0)
- API 层:Zod 校验 amount 为正数,最多两位小数
- Stripe 层:服务端计算最终金额,不信任客户端传值
- 所有金额操作记录到 activity_logs
Admin 管理端 API
管理员可通过以下 API 管理订单和查看收入:
| 方法 | 端点 | 说明 |
|---|---|---|
GET | /api/admin/orders | 查询所有订单(支持状态筛选、分页) |
PATCH | /api/admin/orders/:id | 更新订单状态(退款/完成) |
GET | /api/admin/revenue | 收入统计(按日/周/月汇总) |
审计日志
所有支付相关操作(创建订单、确认支付、退款)都会记录到 activity_logs 表。该表为 append-only,不删除不修改,确保操作可追溯。
测试卡号
| 卡号 | 品牌 | 结果 |
|---|---|---|
4242424242424242 | Visa | 支付成功 |
4000000000000002 | Visa | 支付被拒 |
4000000000003220 | Visa | 需要 3D Secure |
Stripe 账号注册与配置
- 前往 dashboard.stripe.com/register 注册账号
- 在 Developers → API keys 中获取 Secret key 和 Publishable key
- 测试模式使用以 sk_test_ 开头的密钥,生产模式使用 sk_live_
- 将密钥配置到管理后台「业务运营 → 支付管理」(已迁移至 DB,无需 .env)
Webhook 签名验证
服务端使用 Stripe SDK 的 constructEvent() 方法验证 Webhook 签名,防止伪造回调。关键代码:
// Webhook 签名已迁移至策略层(server/utils/payment-strategies/stripe.ts),
// 从 system_configs.payment_secrets 读取密钥,自动完成签名验证。
const strategy = getPaymentStrategy('stripe')
const result = await strategy.verifyWebhook(rawBody, signature)常见支付问题
社交分享与反馈
社交分享组件
纯前端实现,零后端依赖,支持 6 大主流社交平台:
| 平台 | 协议/URL | 说明 |
|---|---|---|
| 微信 | weixin:// 协议 | 微信内置浏览器自动识别 |
| 微博 | https://service.weibo.com/share/share.php | URL 参数传递 |
https://connect.qq.com/widget/shareqq/index.html | URL 参数传递 | |
| 复制链接 | navigator.clipboard | Clipboard API |
用户反馈系统
前后端完整实现,支持星级评分(1-5)+ 文字评论 + 管理员回复。
feedbacks表存储用户评价(评分 1-5 + 文字评论)- 支持按内容 ID 查询评价列表
- 前端使用
ReviewSection组件展示 - 管理员可在后台审核评价
| 方法 | 端点 | 说明 |
|---|---|---|
GET | /api/v1/feedback | 获取某内容的评价列表 |
POST | /api/v1/feedback | 提交评价(评分 + 评论) |
DELETE | /api/v1/feedback/:id | 删除自己的评价 |
社交分享支持的平台
分享组件支持以下 6 大主流社交平台(纯前端实现,零后端依赖):
- WhatsApp:
https://wa.me/?text=协议,移动端自动唤起 App - Facebook:
https://www.facebook.com/sharer/sharer.php?u=分享链接 - Twitter/X:
https://twitter.com/intent/tweet?url=带文本的分享 - Telegram:
https://t.me/share/url?url=Telegram 分享 - 微信:
weixin://协议,微信内置浏览器自动识别 - 复制链接:
navigator.clipboard.writeText()Clipboard API
H5ReviewSection 组件
H5 营销页面使用 H5ReviewSection 组件展示用户评价,包含以下功能:
- 评分分布条形图(1-5 星统计)
- 评价列表(支持分页加载)
- 管理员回复展示
- 写评价表单(登录后可用)
- 完整的 i18n 中英文支持
与现有模块集成
反馈系统与以下模块协同工作:
- 认证模块:登录用户可提交评价,匿名用户仅可查看
- 支付模块:购买后可自动邀请用户评价
- Admin 后台:管理员审核、回复、删除评价
反馈审批工作流
管理员在 Admin 后台可以查看所有评价,通过/驳回/回复。建议对同一 IP/用户做提交频率限制(例如每小时最多 3 条)。
常见反馈问题
Cloudflare 配置
本项目部署在 Vercel 上。推荐用法:Cloudflare 仅做 DNS 管理(灰色云朵 DNS-only),由 Vercel 处理 CDN 和安全。
添加站点到 Cloudflare
- 登录 Cloudflare Dashboard,点击 Add a site
- 输入域名,选择 Free 计划,Cloudflare 自动扫描并导入现有 DNS 记录
- Cloudflare 分配两个 Nameservers(如 anna.ns.cloudflare.com / bob.ns.cloudflare.com)
- 去域名注册商(Namecheap/GoDaddy 等)将 Nameservers 改为 Cloudflare 提供的地址
- 等待 DNS 传播(5-30 分钟),回到 Cloudflare 点击 Check nameservers 验证
DNS 记录配置
| 类型 | 名称 | 记录类型 | 值 | 代理状态 |
|---|---|---|---|---|
主域名 | @ | A | 76.76.21.21 | DNS-only(灰色) |
通配符 | * | CNAME | cname.vercel-dns.com | DNS-only(灰色) |
www | www | CNAME | cname.vercel-dns.com | DNS-only(灰色) |
DNS 验证命令
# 检查 NS 记录是否指向 Cloudflare
dig yourdomain.com NS
# 检查子域名 CNAME
dig admin.yourdomain.com CNAME
# 检查通配符记录
dig test123.yourdomain.com CNAME
# 检查 HTTPS 响应头
curl -I https://yourdomain.com
# 查看 SSL 证书详情
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com为什么不使用代理模式
- Vercel 自带全球 CDN 和边缘缓存,无需 Cloudflare 代理
- 双层代理会增加延迟,且可能导致 HTTPS 证书问题
- DNS-only 模式保留了 Cloudflare 的 DNS 管理和安全功能,但不拦截流量
安全功能(Free 计划)
- 开启 DNSSEC(域名系统安全扩展)
- 配置 SSL/TLS 为 Full (strict) 模式
- 开启 Bot Fight Mode(机器人防护)
- 设置 Rate Limiting(速率限制)保护 API
- 配置 WAF 规则(Web 应用防火墙)
SSL/TLS 配置
在 Cloudflare → SSL/TLS 中,推荐选择 Full (strict) 模式:
- Flexible:客户端到 Cloudflare 加密,Cloudflare 到源站明文(不安全)
- Full:两端都加密,但不验证源站证书
- Full (strict):两端加密 + 验证源站证书(推荐)
同时开启 Always Use HTTPS 强制所有 HTTP 请求跳转到 HTTPS。
进入 SSL/TLS → Edge Certificates,建议开启:
- Always Use HTTPS:自动将 HTTP 重定向到 HTTPS
- Automatic HTTPS Rewrites:自动修复混合内容警告
DDoS 与 Bot 防护
Cloudflare Free 计划自带以下安全功能(无需额外配置):
- DDoS 防护:不限量的 Layer 3/4/7 DDoS 攻击缓解
- Bot Fight Mode:进入 Security → Bots 开启,自动挑战可疑 Bot
- WAF 规则:自动拦截 SQL 注入、XSS 跨站脚本、路径遍历等常见攻击
- 合法搜索引擎爬虫(Googlebot、Bingbot)自动放行
缓存规则与 Page Rules
DNS-only 模式下 Cloudflare 不缓存内容(由 Vercel CDN 处理)。如需额外配置:
- 静态资源(/public/):可设置 Edge TTL 为 1 天
- API 路由(/api/):设置不缓存(Bypass Cache)
- Rate Limiting 规则:保护 /api/ 端点,例如每 IP 每分钟 60 次请求
纯 DNS vs Vercel Nameservers
| 方案 | DNS 管理 | CDN | 安全防护 | 适用场景 |
|---|---|---|---|---|
| Cloudflare DNS-only | Cloudflare | Vercel | Cloudflare DDoS/WAF | 需要额外安全防护 |
| Vercel Nameservers | Vercel | Vercel | Vercel 基础防护 | 简单部署,无额外需求 |
常见 Cloudflare 问题
本地开发
快速启动
# 安装依赖
npm install
# 启动开发服务器(Mock DB 模式,无需数据库)
npm run dev
# 启动 Supabase + 开发服务器
npm run dev:all
# 类型检查
npm run check
# 构建
npm run build本地 Supabase 实例
# 初始化本地 Supabase
supabase init
# 启动全套服务(PG + Auth + Storage + Studio)
supabase start
# Studio 地址: http://localhost:54323
# API URL: http://localhost:54321
# Anon Key: 在 Studio → Settings → API 中查看可用脚本
| 命令 | 说明 |
|---|---|
npm run dev | 启动开发服务器(Mock DB 内存数据库) |
npm run dev:all | 并发启动 Supabase 本地服务 + Nuxt 开发服务器 |
npm run check | TypeScript 类型检查(vue-tsc --noEmit) |
npm run build | 生产构建(含 prerender + Vercel preset) |
npm run gen:types | 生成 Supabase 数据库 TypeScript 类型定义 |
npm run gen:types:local | 从本地 Supabase 生成类型 |
npm run db:push | 推送数据库迁移到远程并重新生成类型 |
npm run test:api-safety | API 鉴权安全扫描(验证 @api-auth 声明与中间件行为一致性) |
npm run test:supabase | Supabase 连接 + 表 + 桶 + 迁移状态健康检查 |
npm run test:storage | Storage 全链路集成测试(上传/公开URL/签名URL/RLS) |
npm run test:payment-strategies | 支付策略工厂测试(stripe/paypal/google-pay/apple-iap/manual) |
npm run test:unit | Vitest 单元测试 |
npm run test:e2e | Playwright E2E 测试 |
npm run gen:rls <table> [--admin] | 生成 RLS 策略 SQL |
npm run scaffold <name> | 脚手架生成器:API 路由 + 页面组件 |
npm run seed:demo | 插入演示数据(活动/商品/用户) |
代码生成器
CRUD API 生成器:npm run gen:crud <resource>
自动生成完整的 CRUD API 端点(GET 列表/GET 详情/POST 创建/PATCH 更新/DELETE 删除),包含 Zod 校验、defineRouteMeta 元数据和 sendSuccess 响应格式。
脚手架生成器:npm run scaffold <name>
一键生成 API + Page 完整模块,包括服务端 API 端点和前端页面组件,减少重复代码编写。
RLS 策略生成器:npm run gen:rls <table> [--admin]
为指定表生成 RLS 行级安全策略 SQL,加 --admin 参数生成管理员策略。
API 规范
统一响应格式
项目所有 API 使用统一的响应格式,通过 sendSuccess() 和 createError() 工具函数返回。错误信息在服务端使用英文,前端展示层通过 t() 翻译。
// 成功响应格式
{
"success": true,
"data": { ... }, // 业务数据
"message": "操作成功" // 可选提示信息
}
// 分页响应格式
{
"success": true,
"data": [ ... ], // 数据列表
"total": 100, // 总记录数
"page": 1, // 当前页码
"pageSize": 20 // 每页数量
}
// 错误响应格式
{
"success": false,
"error": "错误描述", // 英文错误信息
"code": "ERROR_CODE" // 错误码(可选)
}API 鉴权声明
每个 API 文件顶部通过注释声明鉴权级别,供 test:api-safety 扫描器自动验证:
// @api-auth: admin → 管理员专用,03.admin 中间件强制验证
// @api-auth: user → 需登录用户,04.auth-guard 中间件验证
// @api-auth: public → 公开接口,无需认证| 鉴权级别 | 中间件 | 说明 |
|---|---|---|
admin | 03.admin + 04.auth-guard | 管理员专用,需登录 + admin 角色 |
user | 04.auth-guard | 需登录用户,匿名用户返回 403 |
public | 无 | 公开接口,无需认证 |
Zod 输入校验
所有 API 使用 Zod 进行输入校验,格式示例:
import { z } from 'zod'
const bodySchema = z.object({
title: z.string().min(1).max(200),
status: z.enum(['active', 'inactive']).optional(),
page: z.coerce.number().int().min(1).default(1),
pageSize: z.coerce.number().int().min(1).max(100).default(20),
})
const body = await readValidatedBody(event, bodySchema.parse)OpenAPI 文档
项目内置 OpenAPI 3.1.0 文档支持,每个路由通过 defineRouteMeta 声明元数据。
| 端点 | 说明 | 访问方式 |
|---|---|---|
/_openapi.json | OpenAPI 3.1.0 原始 JSON | 直接访问 |
/_scalar | Scalar 交互式文档(紫色主题) | 直接访问 |
/_swagger | Swagger UI 文档 | 直接访问 |
国际化配置
i18n 策略
项目使用 @nuxtjs/i18n 模块,采用 prefix_except_default 策略:默认语言(中文)URL 不加前缀,英文加 /en 前缀。Admin 后台和帮助文档页面使用硬编码中文,不走 i18n。
// nuxt.config.ts 中 i18n 配置
i18n: {
strategy: 'prefix_except_default', // 默认语言不加前缀
defaultLocale: 'zh', // 默认中文
locales: [
{ code: 'zh', iso: 'zh-CN', file: 'zh.json' },
{ code: 'en', iso: 'en-US', file: 'en.json' },
],
detectBrowserLanguage: {
useCookie: true,
cookieKey: 'i18n_locale',
redirectOnRoot: true,
},
}语言检测优先级
系统按以下优先级自动检测用户语言:
- URL 路径前缀(如 /en/about)
- Cookie(i18n_locale)
- 浏览器语言(navigator.language)
- 时区判断(亚洲时区 → zh,其他 → en)
- 兜底默认 zh
使用方式
// 在 <script setup> 中使用
const { t, locale } = useI18n()
// 模板中使用
<h1>{{ t('home.title') }}</h1>
// SEO 中需要响应式
useSeoMeta({
title: () => t('home.seoTitle'),
})
// 切换语言
locale.value = locale.value === 'zh' ? 'en' : 'zh'翻译文件结构
locales/ 目录下 zh.json 和 en.json 的 key 结构完全一致,分为以下命名空间:
| 命名空间 | 覆盖范围 |
|---|---|
common | 通用 UI 文案(按钮、提示、占位符等) |
nav / header | 导航栏和页面头部 |
home | 首页各区块 |
architecture | 技术架构页面 |
hero | 首页 Hero 区域 |
tasks | 任务看板 |
h5 | H5 营销页 |
userBar | 用户状态栏 |
login | 登录弹窗 |
review | 评价组件 |
share | 社交分享 |
新增页面 i18n 规范
- 将所有用户可见文字提取到 locales/zh.json 和 locales/en.json 中,使用命名空间 key(如 tasks.title)
- 在 <script setup> 中使用 const { t } = useI18n()
- 模板中使用 t('key') 获取翻译文本
- SEO meta 中使用 () => t('key') 保持响应式
- Admin 页面((admin)/)和帮助文档(/help)保持硬编码中文,无需 i18n