0175799004
删除不再需要的文档、测试报告和计划文件,包括标题层级规范、颜色优化报告、测试框架文档等
383 lines
7.4 KiB
Markdown
383 lines
7.4 KiB
Markdown
# 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 },
|
||
});
|
||
```
|