docs: 整理文档结构并创建索引(任务 2.3/20)
This commit is contained in:
@@ -0,0 +1,382 @@
|
||||
# API 文档
|
||||
|
||||
## API 概述
|
||||
|
||||
项目使用 Next.js API Routes 实现服务端接口,主要用于处理联系表单提交等后端逻辑。
|
||||
|
||||
## 基础信息
|
||||
|
||||
- **基础 URL**: `/api`
|
||||
- **内容类型**: `application/json`
|
||||
- **字符编码**: `UTF-8`
|
||||
|
||||
## 接口列表
|
||||
|
||||
### 1. 联系表单 API
|
||||
|
||||
#### 提交联系表单
|
||||
|
||||
**接口地址**
|
||||
|
||||
```
|
||||
POST /api/contact
|
||||
```
|
||||
|
||||
**请求头**
|
||||
|
||||
| 参数 | 类型 | 必填 | 描述 |
|
||||
|------|------|------|------|
|
||||
| Content-Type | string | 是 | application/json |
|
||||
|
||||
**请求参数**
|
||||
|
||||
| 参数 | 类型 | 必填 | 描述 |
|
||||
|------|------|------|------|
|
||||
| name | string | 是 | 联系人姓名 |
|
||||
| email | string | 是 | 联系人邮箱 |
|
||||
| phone | string | 否 | 联系人电话 |
|
||||
| subject | string | 是 | 咨询主题 |
|
||||
| message | string | 是 | 咨询内容 |
|
||||
| website | string | 否 | 蜜罐字段(用于反垃圾) |
|
||||
| submitTime | string | 否 | 表单提交时间戳 |
|
||||
| mathHash | string | 否 | 数学验证码哈希 |
|
||||
| mathTimestamp | string | 否 | 数学验证码时间戳 |
|
||||
| mathAnswer | number | 否 | 数学验证码答案 |
|
||||
|
||||
**请求示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "张三",
|
||||
"email": "zhangsan@example.com",
|
||||
"phone": "13800138000",
|
||||
"subject": "产品咨询",
|
||||
"message": "我想了解贵公司的软件开发服务。",
|
||||
"submitTime": "1709827200000",
|
||||
"mathHash": "MTAwLTE3MDk4MjcxMDAwMDA=",
|
||||
"mathTimestamp": "1709827100000",
|
||||
"mathAnswer": 100
|
||||
}
|
||||
```
|
||||
|
||||
**响应参数**
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
|------|------|------|
|
||||
| success | boolean | 请求是否成功 |
|
||||
| message | string | 成功消息(成功时) |
|
||||
| error | string | 错误消息(失败时) |
|
||||
|
||||
**成功响应**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "消息已发送,我们会尽快与您联系!"
|
||||
}
|
||||
```
|
||||
|
||||
**错误响应**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "请填写必填字段"
|
||||
}
|
||||
```
|
||||
|
||||
**错误码说明**
|
||||
|
||||
| HTTP 状态码 | 错误信息 | 描述 |
|
||||
|-------------|----------|------|
|
||||
| 200 | - | 蜜罐字段被填充(静默拒绝) |
|
||||
| 400 | 请填写必填字段 | 缺少必填字段 |
|
||||
| 400 | 请输入有效的邮箱地址 | 邮箱格式不正确 |
|
||||
| 400 | 提交过快,请稍后再试 | 提交时间间隔过短 |
|
||||
| 400 | 验证码错误,请重新计算 | 数学验证码错误 |
|
||||
| 500 | 发送失败,请稍后重试 | 邮件发送失败 |
|
||||
|
||||
## 安全机制
|
||||
|
||||
### 1. 蜜罐字段 (Honeypot)
|
||||
|
||||
通过隐藏字段 `website` 检测机器人提交:
|
||||
|
||||
```tsx
|
||||
// 前端隐藏字段
|
||||
<input name="website" className="hidden" tabIndex={-1} autoComplete="off" />
|
||||
|
||||
// 后端检测
|
||||
if (website) {
|
||||
// 检测到机器人,静默返回成功
|
||||
return NextResponse.json({ success: true });
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 提交时间验证
|
||||
|
||||
验证表单提交时间间隔,防止快速自动提交:
|
||||
|
||||
```tsx
|
||||
if (submitTime) {
|
||||
const timeDiff = Date.now() - parseInt(submitTime);
|
||||
if (timeDiff < 2000) {
|
||||
// 提交过快,拒绝请求
|
||||
return NextResponse.json({ success: false, error: '提交过快' });
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 数学验证码
|
||||
|
||||
使用数学运算验证码防止机器人:
|
||||
|
||||
```tsx
|
||||
// 前端生成验证码
|
||||
const num1 = Math.floor(Math.random() * 10) + 1;
|
||||
const num2 = Math.floor(Math.random() * 10) + 1;
|
||||
const answer = num1 + num2;
|
||||
const timestamp = Date.now();
|
||||
const hash = btoa(`${answer}-${timestamp}`);
|
||||
|
||||
// 后端验证
|
||||
const expectedHash = btoa(`${mathAnswer}-${mathTimestamp}`);
|
||||
if (expectedHash !== mathHash) {
|
||||
return NextResponse.json({ success: false, error: '验证码错误' });
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 邮箱验证
|
||||
|
||||
验证邮箱格式:
|
||||
|
||||
```tsx
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(email)) {
|
||||
return NextResponse.json({ success: false, error: '请输入有效的邮箱地址' });
|
||||
}
|
||||
```
|
||||
|
||||
### 5. XSS 防护
|
||||
|
||||
使用 DOMPurify 清理用户输入:
|
||||
|
||||
```tsx
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
const sanitizedName = DOMPurify.sanitize(name);
|
||||
```
|
||||
|
||||
## 邮件服务
|
||||
|
||||
### Resend 配置
|
||||
|
||||
项目使用 Resend 服务发送邮件:
|
||||
|
||||
```env
|
||||
RESEND_API_KEY=re_xxxxx
|
||||
COMPANY_EMAIL=contact@novalon.cn
|
||||
```
|
||||
|
||||
### 邮件模板
|
||||
|
||||
邮件使用 HTML 模板,包含:
|
||||
|
||||
- 公司品牌头部
|
||||
- 表单数据展示
|
||||
- 时间戳信息
|
||||
- 响应式设计
|
||||
|
||||
**邮件模板结构**
|
||||
|
||||
```html
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
/* 响应式样式 */
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<!-- 公司品牌 -->
|
||||
</div>
|
||||
<div class="content">
|
||||
<!-- 表单数据 -->
|
||||
</div>
|
||||
<div class="footer">
|
||||
<!-- 页脚信息 -->
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## Server Actions
|
||||
|
||||
### 联系表单 Server Action
|
||||
|
||||
位于 `src/app/(marketing)/contact/actions.ts`:
|
||||
|
||||
```tsx
|
||||
'use server';
|
||||
|
||||
export async function submitContactForm(formData: FormData) {
|
||||
// 服务端表单处理逻辑
|
||||
}
|
||||
```
|
||||
|
||||
**优势:**
|
||||
- 无需创建 API 路由
|
||||
- 自动 CSRF 保护
|
||||
- 类型安全
|
||||
- 更简洁的错误处理
|
||||
|
||||
## 环境变量
|
||||
|
||||
### 必需配置
|
||||
|
||||
```env
|
||||
# Resend API 密钥
|
||||
RESEND_API_KEY=re_xxxxx
|
||||
|
||||
# 公司邮箱
|
||||
COMPANY_EMAIL=contact@novalon.cn
|
||||
```
|
||||
|
||||
### 可选配置
|
||||
|
||||
```env
|
||||
# 环境
|
||||
NODE_ENV=production
|
||||
|
||||
# 站点 URL
|
||||
NEXT_PUBLIC_SITE_URL=https://www.novalon.cn
|
||||
```
|
||||
|
||||
## 请求限制
|
||||
|
||||
### 速率限制
|
||||
|
||||
建议在生产环境配置速率限制:
|
||||
|
||||
```typescript
|
||||
// 示例:使用 Upstash Redis
|
||||
import { Ratelimit } from '@upstash/ratelimit';
|
||||
import { Redis } from '@upstash/redis';
|
||||
|
||||
const ratelimit = new Ratelimit({
|
||||
redis: Redis.fromEnv(),
|
||||
limiter: Ratelimit.slidingWindow(10, '1 m'),
|
||||
});
|
||||
|
||||
const { success } = await ratelimit.limit(ip);
|
||||
if (!success) {
|
||||
return NextResponse.json({ error: '请求过于频繁' }, { status: 429 });
|
||||
}
|
||||
```
|
||||
|
||||
## CORS 配置
|
||||
|
||||
API 路由默认不允许跨域请求。如需配置 CORS:
|
||||
|
||||
```typescript
|
||||
export async function POST(request: NextRequest) {
|
||||
const response = NextResponse.json({ success: true });
|
||||
|
||||
response.headers.set('Access-Control-Allow-Origin', 'https://www.novalon.cn');
|
||||
response.headers.set('Access-Control-Allow-Methods', 'POST');
|
||||
|
||||
return response;
|
||||
}
|
||||
```
|
||||
|
||||
## 错误处理
|
||||
|
||||
### 统一错误响应格式
|
||||
|
||||
```typescript
|
||||
interface ApiResponse {
|
||||
success: boolean;
|
||||
message?: string;
|
||||
error?: string;
|
||||
data?: unknown;
|
||||
}
|
||||
```
|
||||
|
||||
### 错误日志
|
||||
|
||||
```typescript
|
||||
try {
|
||||
await resend.emails.send({ ... });
|
||||
} catch (error) {
|
||||
console.error('邮件发送失败:', error);
|
||||
return NextResponse.json(
|
||||
{ success: false, error: '发送失败,请稍后重试' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## 测试接口
|
||||
|
||||
### 使用 cURL
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3000/api/contact \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "测试用户",
|
||||
"email": "test@example.com",
|
||||
"subject": "测试主题",
|
||||
"message": "测试消息内容"
|
||||
}'
|
||||
```
|
||||
|
||||
### 使用 Playwright
|
||||
|
||||
```typescript
|
||||
// e2e/src/tests/api/contact.spec.ts
|
||||
test('提交联系表单', async ({ request }) => {
|
||||
const response = await request.post('/api/contact', {
|
||||
data: {
|
||||
name: '测试用户',
|
||||
email: 'test@example.com',
|
||||
subject: '测试主题',
|
||||
message: '测试消息内容',
|
||||
},
|
||||
});
|
||||
|
||||
expect(response.ok()).toBeTruthy();
|
||||
const data = await response.json();
|
||||
expect(data.success).toBe(true);
|
||||
});
|
||||
```
|
||||
|
||||
## API 版本控制
|
||||
|
||||
当前项目 API 不包含版本号。如需版本控制,建议:
|
||||
|
||||
```
|
||||
/api/v1/contact
|
||||
/api/v2/contact
|
||||
```
|
||||
|
||||
## 监控与日志
|
||||
|
||||
### 推荐集成
|
||||
|
||||
- **Sentry** - 错误监控
|
||||
- **LogRocket** - 会话回放
|
||||
- **Vercel Analytics** - 性能监控
|
||||
|
||||
### 日志格式
|
||||
|
||||
```typescript
|
||||
console.log({
|
||||
timestamp: new Date().toISOString(),
|
||||
level: 'info',
|
||||
message: '联系表单提交',
|
||||
data: { email, subject },
|
||||
});
|
||||
```
|
||||
Reference in New Issue
Block a user