初始化2
This commit is contained in:
191
content/docs/api/rest-api.md
Normal file
191
content/docs/api/rest-api.md
Normal file
@@ -0,0 +1,191 @@
|
||||
---
|
||||
title: REST API 完整参考
|
||||
description: 200+ 标准接口文档,覆盖用户、内容、AI、支付等全部模块。
|
||||
category: api
|
||||
order: 1
|
||||
---
|
||||
|
||||
# REST API 完整参考
|
||||
|
||||
> 200+ 标准接口文档,覆盖用户、内容、AI、支付等全部模块。
|
||||
|
||||
## 📚 API 概览
|
||||
|
||||
**基础 URL:** `https://api.websopy.com/v1`
|
||||
|
||||
**认证方式:** Bearer Token
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer YOUR_API_KEY" \
|
||||
https://api.websopy.com/v1/user/profile
|
||||
```
|
||||
|
||||
## 📋 端点列表
|
||||
|
||||
### 用户管理 `/user`
|
||||
|
||||
| 方法 | 端点 | 说明 |
|
||||
|------|------|------|
|
||||
| GET | `/user/profile` | 获取当前用户信息 |
|
||||
| PUT | `/user/profile` | 更新用户信息 |
|
||||
| GET | `/user/settings` | 获取用户设置 |
|
||||
| PUT | `/user/settings` | 更新用户设置 |
|
||||
| POST | `/user/avatar` | 上传头像 |
|
||||
|
||||
### 项目管理 `/projects`
|
||||
|
||||
| 方法 | 端点 | 说明 |
|
||||
|------|------|------|
|
||||
| GET | `/projects` | 列出项目 |
|
||||
| POST | `/projects` | 创建项目 |
|
||||
| GET | `/projects/:id` | 获取项目详情 |
|
||||
| PUT | `/projects/:id` | 更新项目 |
|
||||
| DELETE | `/projects/:id` | 删除项目 |
|
||||
| GET | `/projects/:id/members` | 获取项目成员 |
|
||||
| POST | `/projects/:id/members` | 添加成员 |
|
||||
|
||||
### 文件存储 `/storage`
|
||||
|
||||
| 方法 | 端点 | 说明 |
|
||||
|------|------|------|
|
||||
| GET | `/storage/files` | 列出文件 |
|
||||
| POST | `/storage/upload` | 上传文件 |
|
||||
| GET | `/storage/files/:id` | 获取文件信息 |
|
||||
| DELETE | `/storage/files/:id` | 删除文件 |
|
||||
| GET | `/storage/files/:id/download` | 下载文件 |
|
||||
|
||||
### AI 功能 `/ai`
|
||||
|
||||
| 方法 | 端点 | 说明 |
|
||||
|------|------|------|
|
||||
| POST | `/ai/chat` | 发送对话请求 |
|
||||
| POST | `/ai/chat/stream` | 流式对话 |
|
||||
| GET | `/ai/sessions` | 列出会话 |
|
||||
| POST | `/ai/sessions` | 创建会话 |
|
||||
| GET | `/ai/sessions/:id` | 获取会话详情 |
|
||||
| DELETE | `/ai/sessions/:id` | 删除会话 |
|
||||
| POST | `/ai/knowledge` | 创建知识库 |
|
||||
| POST | `/ai/knowledge/:id/documents` | 添加文档 |
|
||||
|
||||
### 支付 `/payments`
|
||||
|
||||
| 方法 | 端点 | 说明 |
|
||||
|------|------|------|
|
||||
| GET | `/payments/orders` | 列出订单 |
|
||||
| POST | `/payments/orders` | 创建订单 |
|
||||
| GET | `/payments/orders/:id` | 订单详情 |
|
||||
| POST | `/payments/checkout` | 创建结算会话 |
|
||||
| GET | `/payments/subscriptions` | 订阅列表 |
|
||||
|
||||
## 🔍 请求与响应
|
||||
|
||||
### 请求头
|
||||
|
||||
```http
|
||||
Authorization: Bearer YOUR_API_KEY
|
||||
Content-Type: application/json
|
||||
Accept: application/json
|
||||
X-Request-ID: unique-request-id
|
||||
```
|
||||
|
||||
### 分页
|
||||
|
||||
```http
|
||||
GET /projects?page=2&limit=20
|
||||
```
|
||||
|
||||
响应包含分页信息:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [...],
|
||||
"pagination": {
|
||||
"page": 2,
|
||||
"limit": 20,
|
||||
"total": 100,
|
||||
"total_pages": 5
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 排序
|
||||
|
||||
```http
|
||||
GET /projects?sort=created_at&order=desc
|
||||
```
|
||||
|
||||
### 过滤
|
||||
|
||||
```http
|
||||
GET /projects?status=active&category=ai
|
||||
```
|
||||
|
||||
## 📝 错误处理
|
||||
|
||||
### 错误码
|
||||
|
||||
| 状态码 | 错误码 | 说明 |
|
||||
|--------|--------|------|
|
||||
| 400 | `INVALID_REQUEST` | 请求参数错误 |
|
||||
| 401 | `UNAUTHORIZED` | 未认证或 Token 无效 |
|
||||
| 403 | `FORBIDDEN` | 无权限访问 |
|
||||
| 404 | `NOT_FOUND` | 资源不存在 |
|
||||
| 429 | `RATE_LIMITED` | 请求过于频繁 |
|
||||
| 500 | `SERVER_ERROR` | 服务器内部错误 |
|
||||
|
||||
### 错误响应格式
|
||||
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"code": "INVALID_REQUEST",
|
||||
"message": "参数 'name' 不能为空",
|
||||
"details": {
|
||||
"field": "name",
|
||||
"constraint": "required"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🔧 使用示例
|
||||
|
||||
### JavaScript/TypeScript
|
||||
|
||||
```typescript
|
||||
const response = await fetch('https://api.websopy.com/v1/projects', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${apiKey}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: '我的项目',
|
||||
description: '项目描述'
|
||||
})
|
||||
})
|
||||
|
||||
const data = await response.json()
|
||||
console.log(data)
|
||||
```
|
||||
|
||||
### Python
|
||||
|
||||
```python
|
||||
import requests
|
||||
|
||||
response = requests.post(
|
||||
'https://api.websopy.com/v1/projects',
|
||||
headers={
|
||||
'Authorization': f'Bearer {api_key}',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
json={
|
||||
'name': '我的项目',
|
||||
'description': '项目描述'
|
||||
}
|
||||
)
|
||||
|
||||
data = response.json()
|
||||
print(data)
|
||||
```
|
||||
189
content/docs/api/streaming.md
Normal file
189
content/docs/api/streaming.md
Normal file
@@ -0,0 +1,189 @@
|
||||
---
|
||||
title: 流式输出(SSE)接入
|
||||
description: 使用 Server-Sent Events 实现 AI 流式响应,提升用户体验。
|
||||
category: api
|
||||
order: 2
|
||||
---
|
||||
|
||||
# 流式输出(SSE)接入
|
||||
|
||||
> 使用 Server-Sent Events 实现 AI 流式响应,提升用户体验。
|
||||
|
||||
## 🌊 什么是流式输出?
|
||||
|
||||
流式输出(Streaming)是一种数据传输方式,服务器将数据「分块」发送给客户端,而不是等待全部完成后再返回。
|
||||
|
||||
### 对比
|
||||
|
||||
| 方式 | 等待时间 | 体验 | 适用场景 |
|
||||
|------|----------|------|----------|
|
||||
| 普通请求 | 等待完整响应 | 一般 | 简单、快速的请求 |
|
||||
| 流式输出 | 实时看到生成过程 | ⭐ 优秀 | AI 生成、长文本 |
|
||||
|
||||
## 🚀 开始使用
|
||||
|
||||
### 前端:接收流式响应
|
||||
|
||||
```typescript
|
||||
import { WebsopyClient } from '@websopy/sdk'
|
||||
|
||||
const client = new WebsopyClient({
|
||||
apiKey: process.env.WEBSOPY_API_KEY
|
||||
})
|
||||
|
||||
async function streamChat() {
|
||||
const stream = await client.ai.chatStream({
|
||||
messages: [
|
||||
{ role: 'user', content: '写一篇关于 AI 的文章' }
|
||||
]
|
||||
})
|
||||
|
||||
// 方法 1:遍历数据块
|
||||
for await (const chunk of stream) {
|
||||
if (chunk.type === 'content') {
|
||||
process.stdout.write(chunk.content)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 响应数据结构
|
||||
|
||||
```typescript
|
||||
interface StreamChunk {
|
||||
type: 'content' | 'tool_call' | 'done' | 'error'
|
||||
content?: string
|
||||
tool?: {
|
||||
name: string
|
||||
arguments: string
|
||||
}
|
||||
usage?: {
|
||||
promptTokens: number
|
||||
completionTokens: number
|
||||
}
|
||||
}
|
||||
|
||||
// 示例数据块
|
||||
{ type: 'content', content: 'AI' }
|
||||
{ type: 'content', content: ' 正在' }
|
||||
{ type: 'content', content: '改变' }
|
||||
{ type: 'content', content: '世界...' }
|
||||
{ type: 'done', usage: { promptTokens: 20, completionTokens: 150 } }
|
||||
```
|
||||
|
||||
## 🌐 原生 SSE 实现
|
||||
|
||||
如果不用 SDK,直接使用 Fetch API:
|
||||
|
||||
```typescript
|
||||
async function nativeStream() {
|
||||
const response = await fetch(
|
||||
'https://api.websopy.com/v1/ai/chat?message=Hello',
|
||||
{
|
||||
headers: {
|
||||
'Authorization': `Bearer ${apiKey}`,
|
||||
'Accept': 'text/event-stream'
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const reader = response.body.getReader()
|
||||
const decoder = new TextDecoder()
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read()
|
||||
if (done) break
|
||||
|
||||
const chunk = decoder.decode(value)
|
||||
const lines = chunk.split('\n')
|
||||
for (const line of lines) {
|
||||
if (line.startsWith('data: ')) {
|
||||
const data = JSON.parse(line.slice(6))
|
||||
console.log('收到:', data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🎨 前端组件示例
|
||||
|
||||
### Vue Composition API
|
||||
|
||||
```typescript
|
||||
import { ref } from 'vue'
|
||||
|
||||
export function useStreamingChat() {
|
||||
const messages = ref([])
|
||||
const streaming = ref(false)
|
||||
const currentContent = ref('')
|
||||
|
||||
const sendMessage = async (content) => {
|
||||
streaming.value = true
|
||||
currentContent.value = ''
|
||||
|
||||
messages.value.push({ role: 'user', content })
|
||||
|
||||
const stream = await client.ai.chatStream({
|
||||
messages: messages.value
|
||||
})
|
||||
|
||||
for await (const chunk of stream) {
|
||||
if (chunk.type === 'content') {
|
||||
currentContent.value += chunk.content
|
||||
}
|
||||
}
|
||||
|
||||
messages.value.push({ role: 'assistant', content: currentContent.value })
|
||||
streaming.value = false
|
||||
}
|
||||
|
||||
return { messages, streaming, currentContent, sendMessage }
|
||||
}
|
||||
```
|
||||
|
||||
## ❓ 常见问题
|
||||
|
||||
### Q: 流式中断怎么办?
|
||||
|
||||
```typescript
|
||||
async function streamWithReconnect() {
|
||||
const maxRetries = 3
|
||||
let retries = 0
|
||||
|
||||
while (retries < maxRetries) {
|
||||
try {
|
||||
const stream = await client.ai.chatStream({...})
|
||||
return stream
|
||||
} catch (error) {
|
||||
retries++
|
||||
await sleep(1000 * retries) // 指数退避
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Q: 如何在流式过程中取消请求?
|
||||
|
||||
```typescript
|
||||
const controller = new AbortController()
|
||||
|
||||
// 发送请求
|
||||
const stream = await client.ai.chatStream({
|
||||
messages: [...],
|
||||
signal: controller.signal
|
||||
})
|
||||
|
||||
// 取消请求
|
||||
controller.abort()
|
||||
```
|
||||
|
||||
### Q: SSE 和 WebSocket 区别?
|
||||
|
||||
| 特性 | SSE | WebSocket |
|
||||
|------|-----|-----------|
|
||||
| 方向 | 单向(服务端→客户端) | 双向 |
|
||||
| 协议 | HTTP | ws:// |
|
||||
| 重连 | 自动 | 需手动处理 |
|
||||
| 复杂度 | 简单 | 复杂 |
|
||||
| 适用 | AI 流式输出 | 实时聊天 |
|
||||
206
content/docs/api/webhook.md
Normal file
206
content/docs/api/webhook.md
Normal file
@@ -0,0 +1,206 @@
|
||||
---
|
||||
title: Webhook 事件接入
|
||||
description: 订阅业务事件,处理订单支付、AI 任务完成等异步通知。
|
||||
category: api
|
||||
order: 3
|
||||
---
|
||||
|
||||
# Webhook 事件接入
|
||||
|
||||
> 订阅业务事件,处理订单支付、AI 任务完成等异步通知。
|
||||
|
||||
## 🔔 什么是 Webhook?
|
||||
|
||||
Webhook 是一种「反向 API 调用」机制:
|
||||
|
||||
- 传统 API:你 → 平台(主动拉取)
|
||||
- Webhook:平台 → 你(被动接收)
|
||||
|
||||
当某个事件发生时(如支付成功),平台主动通知你的服务器。
|
||||
|
||||
## 📋 可订阅的事件
|
||||
|
||||
| 事件类型 | 说明 |
|
||||
|----------|------|
|
||||
| `payment.completed` | 支付完成 |
|
||||
| `payment.failed` | 支付失败 |
|
||||
| `subscription.created` | 订阅创建 |
|
||||
| `subscription.cancelled` | 订阅取消 |
|
||||
| `ai.task.completed` | AI 任务完成 |
|
||||
| `ai.task.failed` | AI 任务失败 |
|
||||
| `file.uploaded` | 文件上传完成 |
|
||||
| `project.created` | 项目创建 |
|
||||
| `user.invited` | 用户被邀请 |
|
||||
| `workflow.triggered` | 工作流触发 |
|
||||
|
||||
## 🚀 配置 Webhook
|
||||
|
||||
### 步骤 1:创建 Webhook 端点
|
||||
|
||||
在你的服务器创建一个 API 端点:
|
||||
|
||||
```typescript
|
||||
// Node.js Express 示例
|
||||
app.post('/webhook/websopy', express.raw({ type: 'application/json' }), (req, res) => {
|
||||
// 验证签名
|
||||
const signature = req.headers['x-websopy-signature']
|
||||
const timestamp = req.headers['x-websopy-timestamp']
|
||||
|
||||
if (!verifySignature(req.body, signature, timestamp)) {
|
||||
return res.status(401).send('Invalid signature')
|
||||
}
|
||||
|
||||
// 处理事件
|
||||
const event = JSON.parse(req.body)
|
||||
console.log('收到事件:', event.type)
|
||||
|
||||
// 立即返回 200(重要!)
|
||||
res.status(200).send('OK')
|
||||
|
||||
// 异步处理业务逻辑
|
||||
processEvent(event)
|
||||
})
|
||||
```
|
||||
|
||||
### 步骤 2:在控制台配置
|
||||
|
||||
1. 进入 **开发者中心 → Webhook**
|
||||
2. 点击 **添加端点**
|
||||
3. 填写配置:
|
||||
|
||||
| 配置项 | 说明 |
|
||||
|--------|------|
|
||||
| URL | 你的服务器地址,如 `https://your-server.com/webhook/websopy` |
|
||||
| 事件类型 | 选择要订阅的事件 |
|
||||
| 密钥 | 用于验证签名的密钥 |
|
||||
|
||||
### 步骤 3:验证签名
|
||||
|
||||
```typescript
|
||||
import crypto from 'crypto'
|
||||
|
||||
function verifySignature(body, signature, timestamp) {
|
||||
const secret = process.env.WEBSOPY_WEBHOOK_SECRET
|
||||
|
||||
// 检查时间戳(5分钟内有效)
|
||||
const ts = parseInt(timestamp)
|
||||
if (Date.now() - ts > 5 * 60 * 1000) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 计算签名
|
||||
const payload = `${timestamp}.${body}`
|
||||
const expectedSignature = crypto
|
||||
.createHmac('sha256', secret)
|
||||
.update(payload)
|
||||
.digest('hex')
|
||||
|
||||
return crypto.timingSafeEqual(
|
||||
Buffer.from(signature),
|
||||
Buffer.from(expectedSignature)
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## 📨 事件数据格式
|
||||
|
||||
### 通用结构
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "evt_abc123xyz",
|
||||
"type": "payment.completed",
|
||||
"created_at": "2024-01-15T10:30:00Z",
|
||||
"data": {
|
||||
// 事件相关数据
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 支付完成事件
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "evt_abc123xyz",
|
||||
"type": "payment.completed",
|
||||
"created_at": "2024-01-15T10:30:00Z",
|
||||
"data": {
|
||||
"order_id": "ord_xyz789",
|
||||
"amount": 29900,
|
||||
"currency": "CNY",
|
||||
"payment_method": "alipay",
|
||||
"status": "completed",
|
||||
"user_id": "user_123"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ⏰ 失败重试
|
||||
|
||||
如果你的服务器返回非 200 状态码,Websopy 会自动重试:
|
||||
|
||||
| 重试次数 | 延迟 |
|
||||
|----------|------|
|
||||
| 第 1 次 | 1 分钟 |
|
||||
| 第 2 次 | 5 分钟 |
|
||||
| 第 3 次 | 30 分钟 |
|
||||
| 第 4 次 | 2 小时 |
|
||||
| 第 5 次 | 12 小时 |
|
||||
|
||||
超过重试次数仍未成功,事件将被标记为失败,可在控制台手动重试。
|
||||
|
||||
### 幂等处理
|
||||
|
||||
```typescript
|
||||
const processedEvents = new Set()
|
||||
|
||||
app.post('/webhook/websopy', async (req, res) => {
|
||||
const event = JSON.parse(req.body)
|
||||
|
||||
// 幂等检查
|
||||
if (processedEvents.has(event.id)) {
|
||||
return res.status(200).send('Already processed')
|
||||
}
|
||||
|
||||
try {
|
||||
await processEvent(event)
|
||||
processedEvents.add(event.id)
|
||||
res.status(200).send('OK')
|
||||
} catch (error) {
|
||||
res.status(500).send('Error')
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## 🧪 本地开发测试
|
||||
|
||||
### 使用 ngrok
|
||||
|
||||
```bash
|
||||
# 安装 ngrok
|
||||
npm install -g ngrok
|
||||
|
||||
# 启动本地服务
|
||||
ngrok http 3000
|
||||
|
||||
# 复制输出的 https URL 到 Webhook 配置
|
||||
# https://abc123.ngrok.io/webhook/websopy
|
||||
```
|
||||
|
||||
## ❓ 常见问题
|
||||
|
||||
### Q: 收到重复事件怎么办?
|
||||
|
||||
使用事件 ID 做幂等检查,参考上文示例。
|
||||
|
||||
### Q: 处理超时怎么办?
|
||||
|
||||
```typescript
|
||||
app.post('/webhook', async (req, res) => {
|
||||
// 立即返回
|
||||
res.status(200).send('OK')
|
||||
|
||||
// 异步处理
|
||||
setImmediate(() => processEvent(req.body))
|
||||
})
|
||||
```
|
||||
Reference in New Issue
Block a user