HELP CENTER
📚 Nuxt 4🗄️ Supabase▲ Vercel🔐 Auth

帮助文档中心

HeHe App 完整帮助文档 — 从项目搭建到部署上线的全流程指南。

v1.0
1.0
最后更新
2026-06
00

文档概览

本帮助文档基于项目 docs/ 目录下的 9 份核心文档整合而成,覆盖从项目搭建、数据库集成、用户认证、支付接入到生产部署的全流程。文档采用左侧导航 + 右侧内容的经典布局,支持中英文双语切换。

文档结构

01

快速开始

项目定位、前置条件、环境变量、Mock DB、快速启动、FAQ

02

项目架构

技术栈、目录结构、多域名路由、中间件链、i18n 国际化

03

渲染策略

SSR/ISR/SWR 全维度对比、选型决策树、性能数字、routeRules 配置

04

Supabase 数据库集成

数据库迁移、RLS 策略、Storage、Seed Data、管理员创建、Connection Pooler

05

Vercel 部署

环境变量、域名配置、子域名路由、SSL、检查清单、预览部署

06

GitHub 与 CI/CD

GitHub 仓库管理、分支策略、Actions CI/CD、PR 流程、分支保护

07

用户认证

Email + Google/Facebook/Apple OAuth + 匿名登录、Token 管理、H5 组件

08

支付集成

Stripe Checkout + 支付策略工厂、Webhook、金额安全、Supabase Stripe 集成

09

社交分享与反馈

6大平台社交分享、用户评价系统、管理员审批工作流

10

Cloudflare 配置

DNS 管理、CDN 加速、安全防护、DNS-only 最佳实践

01

定位与技术栈

项目定位

本项目是单人全栈独立开发者闭环项目脚手架,一人负责开发、维护、上线、测试、运维全流程。在单一 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/nuxtAdmin 后台离线可用,Service Worker 缓存
数据库Supabase PostgreSQLRLS 行级安全策略,Connection Pooler 连接池
认证Supabase Auth邮箱 + Google/Facebook/Apple OAuth + 匿名登录
支付Stripe Checkout + 支付策略工厂stripe/paypal/google-pay/apple-iap/manual,Mock/生产双模式
分析Vercel Analytics + APM 中间件请求耗时监控 + Web Vitals
部署VercelGit 推送自动部署,子域名自适应路由

前置条件

工具版本要求检查命令用途
Node.js≥ 20.xnode --version运行时
npm≥ 10.xnpm --version包管理
Supabase CLI≥ 1.xsupabase --version数据库迁移
Git≥ 2.xgit --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 build

Mock DB 模式(MOCK_DB=true)无需 Supabase 账号,所有数据存储在内存中,支持完整的 CRUD、Auth 模拟和链式查询,适合前端 UI 开发和快速原型验证。

渲染策略

路由策略说明
/ISR (3600s)SEO 友好的静态生成,首次构建后增量再生
/architectureISR (3600s)技术架构白皮书
/helpISR (3600s)帮助文档中心
/h5/**SWR (600s)营销活动页,后台修改后最快 10 分钟更新
/admin/**SPA (ssr: false)管理后台纯客户端渲染,隔离 SSR 安全泄露
/api/**no-store实时 API 零缓存,每次请求实时响应
02

目录结构与路由

目录结构

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)
根域名/architectureISR (3600s)
根域名/helpISR (3600s)
其他子域名→ /h5/{subdomain}/SWR (600s)
根域名/admin/**SPA (ssr: false)
根域名/api/**no-store

中间件执行链

0000.apm
0101.subdomain
0202.auth
0303.admin
0404.auth-guard
0505.api-security

每个请求按编号顺序依次经过这 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-securityAPI 安全防护8层安全检查:IP黑白名单、国家限制、API Key验证、HMAC签名、端点控制、速率限制
03

环境变量

环境变量清单

变量名说明必填备注
NUXT_PUBLIC_SUPABASE_URLSupabase 项目 URL前端可访问
NUXT_PUBLIC_SUPABASE_ANON_KEYSupabase anon key前端可访问
SUPABASE_SERVICE_ROLE_KEYSupabase service_role key仅服务端,禁止加 NUXT_PUBLIC_ 前缀
MOCK_DBMock 数据库开关本地开发 true,生产 false
⚠️
安全红线

SUPABASE_SERVICE_ROLE_KEY 绝对不能加 NUXT_PUBLIC_ 前缀。支付密钥已迁移至 DB 管理,不再通过环境变量传递。

Mock DB 离线开发

项目内置完整的内存 Mock PostgreSQL 适配器,设置 MOCK_DB=true 即可完全离线开发,无需 Supabase 物理数据库。支持链式查询、聚合统计、CRUD 操作和 Auth 模拟。

04

渲染策略对比

三种渲染模式

SSR、ISR、SWR 三种渲染模式是现代全栈框架的核心能力。三者本质上都是「服务端介入渲染」,核心差异在于何时渲染、缓存多久、谁触发更新。选错渲染策略,轻则 LCP 超标,重则服务器在流量峰值下崩溃。

对比维度SSRISRSWR
渲染时机每次请求实时渲染首次渲染并缓存,后台定时刷新过期后返回旧缓存,后台异步刷新
TTFB200-800ms(每次)~5ms(命中缓存)~5ms(命中缓存)
数据新鲜度100% 实时最多延迟 revalidate 秒最多延迟 maxAge 秒
服务器压力高(每请求计算一次)极低(缓存期零计算)低(过期后异步一次)
SEO 友好度极佳极佳极佳
适合更新频率秒级小时/天级分钟/小时级
Nuxt 4 配置ssr: trueisr: 3600swr: 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)

本项目选型定论

页面/场景选型配置理由
官网首页ISRisr: 3600每日更新一次足够,极致 LCP
H5 活动页SWRswr: 600活动配置随时可改,容忍 10 分钟延迟
管理后台SPAssr: 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' } },
  }
})

常见误区

Q: ISR 缓存过期,用户会看到空白页或等待吗?
Q: ISR 和 SWR 本质区别是什么?
Q: SSR 在什么情况下是唯一正确选择?
05

Supabase 数据库集成

创建 Supabase 项目

  1. Supabase Dashboard 中创建新项目
  2. New Project — 填写项目名称和数据库密码
  3. Settings → API — 获取项目 URL 和 anon key
  4. 将 URL 和 key 配置到项目 .env 文件

数据库 Schema 概览

auth.users
Supabase 内置用户表
▼ handle_new_user() trigger
profiles
用户档案 (0001 必选)
tasks
业务任务 (0001 必选)
activity_logs
审计日志 (0001 必选)
products
商品 (0002_iap 可选)
orders
订单 (0002_iap 可选)
payment_configs
支付配置 (0002_iap 可选)
campaigns
营销活动 + 留资 (0003)
feedbacks
用户评价 (0004 可选)
system_configs
系统配置 (0005 可选)
questionnaire_sessions
问卷会话 (0006 可选)
ai_reports
AI 报告 (0006 可选)
cron
定时任务 (0099 可选)

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 创建(推荐)

  1. 进入 Authentication → Users → Add User,填写邮箱和密码,勾选 Auto Confirm User
  2. 进入 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.mjs

OAuth 社交登录配置

在 Supabase Dashboard → Authentication → Providers 中配置第三方登录。

Provider需要配置获取地址
GoogleClient ID + Client SecretGoogle Cloud Console → APIs & Services → Credentials
FacebookApp ID + App SecretMeta for Developers → 我的应用
AppleService ID + Key ID + Private KeyApple Developer → Certificates, Identifiers & Profiles

配置完成后,将 Supabase Dashboard 提供的 Redirect URL 填入各平台的回调地址白名单。

Supabase Storage

项目内置 Supabase Storage 支持,提供 3 个 Bucket 覆盖全部业务场景,RLS 策略内置于 0001_core.sql 迁移中:

Bucket可见性大小限制允许类型写入权限
avatars公开2 MBpng / jpeg / gif / webp认证用户写自己目录
campaign-assets公开10 MBpng / 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 SUPABASEURL + 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 清空本地数据库并重新执行所有迁移

新增迁移标准流程

  1. supabase migration new <name> 创建新迁移文件
  2. 在生成的 SQL 文件中编写 DDL 语句(CREATE TABLE、RLS 策略等)
  3. supabase db reset 本地验证迁移是否正确执行
  4. supabase db push 推送到远程 Supabase 项目
  5. 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
# 每次数据库结构变更后都需要重新生成

生产回滚流程

  1. 数据库回滚:supabase db push 推送回滚迁移文件
  2. 代码回滚:git revert <commit-hash> 或通过 Vercel Dashboard 回滚到上一个部署
  3. 验证:确认所有功能正常后,重新生成类型并运行测试

快速参考命令

# 数据库连接测试
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]
06

用户认证

支持的登录方式

方式SDK 方法API 端点备注
邮箱密码signInWithPasswordPOST /api/v1/auth/login需要注册
Google OAuthsignInWithOAuthprovider: google海外主流用户
Facebook OAuthsignInWithOAuthprovider: facebook东南亚/拉美用户
Apple OAuthsignInWithOAuthprovider: appleiOS 用户(App Store 审核要求)
匿名signInAnonymously + device-id-先浏览后登录,数据可迁移

profiles 表核心字段

字段类型说明
idUUID与 auth.users 一对一关联
emailTEXT用户邮箱
usernameTEXT用户名(可选)
avatar_urlTEXT头像 URL(可选)
roleTEXTuser / admin,管理员手动设置
is_anonymousBOOLEAN是否为匿名用户
created_atTIMESTAMPTZ注册时间
updated_atTIMESTAMPTZ最后更新时间

Token 生命周期

Token 类型格式有效期传递方式
Access TokenJWT1 hourBearer Header 或 Cookie
Refresh TokenOpaque30 days自动刷新 Access Token
Device IDUUID持久化匿名用户的标识

服务端 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:

  1. 前往 Google Cloud Console → APIs & Services → Credentials
  2. 创建 OAuth 2.0 Client ID,应用类型选择 Web application
  3. 在 Authorized redirect URIs 中添加 Supabase Dashboard 提供的回调 URL
  4. 将 Client ID 和 Client Secret 填入 Supabase Dashboard → Authentication → Providers → Google

Facebook OAuth:

  1. 前往 Facebook Developers → My Apps → Create App
  2. 添加 Facebook Login 产品,配置 Valid OAuth Redirect URIs
  3. 将 App ID 和 App Secret 填入 Supabase Dashboard → Authentication → Providers → Facebook

Apple OAuth:

  1. 前往 Apple Developer → Certificates, Identifiers & Profiles → Services IDs
  2. 创建 Service ID 并启用 Sign in with Apple
  3. 配置回调域名为 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() 函数切换语言

常见认证问题

Q: 邮箱注册后用户无法登录?
Q: OAuth 回调报错 Invalid redirect URL?
Q: Token 过期后接口返回 401 报错?
Q: 设备绑定邮箱后匿名历史数据会丢失吗?
07

Vercel 部署

部署流程

  1. 将项目代码推送到 GitHub
  2. Vercel Dashboard → 导入 Git 仓库
  3. 配置生产环境变量(所有 .env 中的变量)
  4. 设置 Framework Preset 为 Nuxt.js,Node.js Version ≥ 20.x
  5. 部署完成,自动分配 vercel.app 域名

生产环境变量配置

在 Vercel Dashboard → 你的项目 → Settings → Environment Variables 中逐项配置:

变量名说明是否必须
MOCK_DB关闭 Mock 沙盒,设为 false必须
SUPABASE_URLSupabase 项目 URL(服务端)必须
SUPABASE_SERVICE_ROLE_KEYSupabase 服务端密钥(禁止加 NUXT_PUBLIC_ 前缀)必须
NUXT_PUBLIC_SUPABASE_URLSupabase URL(前端公开)必须
NUXT_PUBLIC_SUPABASE_ANON_KEYSupabase 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 友好的静态生成,首次构建后增量再生
/architectureISR (3600s)技术架构白皮书
/helpISR (3600s)帮助文档中心
/h5/**SWR (600s)营销活动页,后台修改后最快 10 分钟更新
/admin/**SPA (ssr: false)管理后台纯客户端渲染,隔离 SSR 安全泄露
/api/**no-store实时 API 零缓存,每次请求实时响应

域名配置

类型域名记录类型说明
主域名example.comA76.76.21.21Vercel IP
通配符*.example.comCNAMEcname.vercel-dns.com子域名转发

站点 URL 与子域名路由

项目采用零配置方案,站点 URL 自动适配不同环境:

环境URL 来源示例
本地开发默认值http://localhost:3000
Vercel PreviewVERCEL_URL(自动注入)https://hehe-app-git-main.vercel.app
Vercel ProductionVERCEL_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:

  1. 在 Stripe Dashboard → Developers → Webhooks → Add endpoint
  2. Endpoint URL 设置为 https://your-domain.com/api/v1/payments/webhook
  3. 选择要监听的事件:payment_intent.succeeded, payment_intent.payment_failed
  4. 获取 Webhook Signing Secret 并填入管理后台「业务运营 → 支付管理」→ Stripe Webhook Secret
  5. 本地测试: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 --prod

Preview 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 保障

常见部署问题

Q: 部署后页面白屏 / 500 错误?
Q: API 路由返回 404?
Q: 子域名不生效?
Q: 环境变量修改后不生效?
Q: ISR/SWR 缓存不更新?
Q: 构建超时或内存不足?
08

GitHub 与 CI/CD

前置准备

使用 GitHub 进行代码托管与 CI/CD 流程:

  1. 注册 GitHub 账号,并推荐在本地配置 Git 身份信息:
  2. git config --global user.name "Your Name"
    git config --global user.email "your-email@example.com"
  3. 安装 GitHub CLI(推荐),方便通过命令行管理 PR 与仓库:
  4. brew install gh
    gh auth login

创建与推送仓库

将本地项目关联并推送到 GitHub 远程私有仓库:

  1. 在 GitHub 上创建一个名为 hehe-app 的私有仓库(Private)。
  2. 在本地项目根目录下,运行以下命令进行关联并推送:
  3. # 初始化 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 build

Commit 消息规范

遵循 Conventional Commits 规范:

类型说明示例
feat新功能feat: add user avatar upload
fixBug 修复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 是单仓或多团队协作中保证主干代码质量的核心工作流:

  1. 创建 PR:在 GitHub 推送 feature 分支后,点击仓库顶部的 Compare & pull request,根据模板填写 Title 和 Description。
  2. 状态检查:等待 GitHub Actions 自动运行,确认编译与类型检查(Status checks)全部变为绿色通过状态。
  3. 选择合并方式:
    • Merge commit:保留所有细分 commit 提交记录及分支线(推荐,最完整)。
    • Squash and merge:将分支上的所有提交压缩合并为一个 clean commit 写入主干(适合碎片化小功能)。
    • Rebase and merge:采用变基的形式保持一条线性的主干提交历史。
  4. 触发上线:点击 Confirm merge 合并后,Vercel 将自动接管并将代码部署上线至生产环境。

安全实践

⚠️
密钥泄露应急处理

如果密钥被意外提交到 Git 仓库:

  1. 立即在 Supabase/Stripe Dashboard 中撤销泄露的密钥
  2. 使用 git-filter-repo 清理 Git 历史中的敏感信息
  3. 在 GitHub → Settings → Secrets 中重新生成密钥
  4. 强制推送清理后的历史(需团队协调)
  • .gitattributes 配置统一 LF 换行,避免跨平台差异
  • GitHub Secrets 存储 CI 所需的敏感环境变量
  • 定期审查仓库的 Collaborator 和 Access Token 权限

常见 GitHub 问题

Q: git push 报 Permission denied?
Q: CI 构建失败?
Q: 误删了文件/分支怎么恢复?
09

支付集成

支付流程

1用户点击支付
2POST /api/v1/payments/create 创建订单
3跳转 Stripe Checkout 托管页
4用户完成支付
5双通道回调更新订单状态

双模式运行

模式配置行为
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.tsStripe 策略Stripe Checkout Session + Webhook 签名验证

Stripe Webhook 事件:checkout.session.completed、charge.refunded

双通道回调:前端 success_url 跳转 + 服务端 Webhook 异步通知,双重保障订单状态同步。

Supabase 内置 Stripe 集成方案

Supabase 提供两套官方 Stripe 数据集成方案,定位不同:

方案原理适用场景
Stripe Sync EngineWebhook + 定时回填,将 Stripe 数据同步到本地 Postgres 表(stripe.customers/subscriptions/invoices)账单分析、MRR、流失率统计
Stripe FDWForeign 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,不删除不修改,确保操作可追溯。

测试卡号

卡号品牌结果
4242424242424242Visa支付成功
4000000000000002Visa支付被拒
4000000000003220Visa需要 3D Secure

Stripe 账号注册与配置

  1. 前往 dashboard.stripe.com/register 注册账号
  2. 在 Developers → API keys 中获取 Secret key 和 Publishable key
  3. 测试模式使用以 sk_test_ 开头的密钥,生产模式使用 sk_live_
  4. 将密钥配置到管理后台「业务运营 → 支付管理」(已迁移至 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)

常见支付问题

Q: Mock 模式下支付返回什么?
Q: Webhook 收不到回调?
10

社交分享与反馈

社交分享组件

纯前端实现,零后端依赖,支持 6 大主流社交平台:

平台协议/URL说明
微信weixin:// 协议微信内置浏览器自动识别
微博https://service.weibo.com/share/share.phpURL 参数传递
QQhttps://connect.qq.com/widget/shareqq/index.htmlURL 参数传递
复制链接navigator.clipboardClipboard 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 后台:管理员审核、回复、删除评价

反馈审批工作流

1用户提交评价
2管理员审核
3通过:公开显示
4回复用户
5驳回:仅用户可见

管理员在 Admin 后台可以查看所有评价,通过/驳回/回复。建议对同一 IP/用户做提交频率限制(例如每小时最多 3 条)。

常见反馈问题

Q: 匿名用户可以提交评价吗?
11

Cloudflare 配置

ℹ️
可选增强方案

本项目部署在 Vercel 上。推荐用法:Cloudflare 仅做 DNS 管理(灰色云朵 DNS-only),由 Vercel 处理 CDN 和安全。

添加站点到 Cloudflare

  1. 登录 Cloudflare Dashboard,点击 Add a site
  2. 输入域名,选择 Free 计划,Cloudflare 自动扫描并导入现有 DNS 记录
  3. Cloudflare 分配两个 Nameservers(如 anna.ns.cloudflare.com / bob.ns.cloudflare.com)
  4. 去域名注册商(Namecheap/GoDaddy 等)将 Nameservers 改为 Cloudflare 提供的地址
  5. 等待 DNS 传播(5-30 分钟),回到 Cloudflare 点击 Check nameservers 验证

DNS 记录配置

类型名称记录类型代理状态
主域名@A76.76.21.21DNS-only(灰色)
通配符*CNAMEcname.vercel-dns.comDNS-only(灰色)
wwwwwwCNAMEcname.vercel-dns.comDNS-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

为什么不使用代理模式

  1. Vercel 自带全球 CDN 和边缘缓存,无需 Cloudflare 代理
  2. 双层代理会增加延迟,且可能导致 HTTPS 证书问题
  3. 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-onlyCloudflareVercelCloudflare DDoS/WAF需要额外安全防护
Vercel NameserversVercelVercelVercel 基础防护简单部署,无额外需求

常见 Cloudflare 问题

Q: 网站打不开/SSL 错误?
Q: 子域名通配符不生效?
12

本地开发

快速启动

# 安装依赖
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 checkTypeScript 类型检查(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-safetyAPI 鉴权安全扫描(验证 @api-auth 声明与中间件行为一致性)
npm run test:supabaseSupabase 连接 + 表 + 桶 + 迁移状态健康检查
npm run test:storageStorage 全链路集成测试(上传/公开URL/签名URL/RLS)
npm run test:payment-strategies支付策略工厂测试(stripe/paypal/google-pay/apple-iap/manual)
npm run test:unitVitest 单元测试
npm run test:e2ePlaywright 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 参数生成管理员策略。

13

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  → 公开接口,无需认证
鉴权级别中间件说明
admin03.admin + 04.auth-guard管理员专用,需登录 + admin 角色
user04.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.jsonOpenAPI 3.1.0 原始 JSON直接访问
/_scalarScalar 交互式文档(紫色主题)直接访问
/_swaggerSwagger UI 文档直接访问
14

国际化配置

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,
  },
}

语言检测优先级

系统按以下优先级自动检测用户语言:

  1. URL 路径前缀(如 /en/about)
  2. Cookie(i18n_locale)
  3. 浏览器语言(navigator.language)
  4. 时区判断(亚洲时区 → zh,其他 → en)
  5. 兜底默认 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任务看板
h5H5 营销页
userBar用户状态栏
login登录弹窗
review评价组件
share社交分享

新增页面 i18n 规范

  1. 将所有用户可见文字提取到 locales/zh.json 和 locales/en.json 中,使用命名空间 key(如 tasks.title)
  2. 在 <script setup> 中使用 const { t } = useI18n()
  3. 模板中使用 t('key') 获取翻译文本
  4. SEO meta 中使用 () => t('key') 保持响应式
  5. Admin 页面((admin)/)和帮助文档(/help)保持硬编码中文,无需 i18n
15

常见问题

部署相关

Q: 如何部署到 Vercel?
Q: 为什么 H5 页面没有内容?
Q: 如何配置自定义域名?
Q: i18n 构建报错 "Cannot read properties of undefined"?
Q: build 时 prerender 失败?

数据库相关

Q: 本地开发如何连接 Supabase?
Q: 如何执行数据库迁移?
Q: RLS 策略怎么写?
Q: 如何从 Mock DB 切换到真实 Supabase?
Q: 子域名路由不生效?

认证相关

Q: 支持哪些登录方式?
Q: Token 过期了怎么办?
Q: 如何添加管理员?
Q: 管理后台如何登录?

性能相关

Q: 页面加载慢怎么办?
Q: 如何监控线上性能?
Q: API 接口有速率限制吗?