1
1
mirror of https://github.com/ZeroCatDev/ClassworksKV.git synced 2025-10-22 18:33:11 +00:00
ClassworksKV/docs/FRONTEND_MIGRATION_GUIDE.md
2025-10-02 12:07:50 +08:00

600 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 前端迁移指南
## 概述
本文档描述了后端中间件系统的重构,以及前端需要如何适配这些变化。核心变化是统一了设备信息获取和权限验证流程。
---
## 核心变化
### 1. 统一的设备中间件系统
后端现在使用统一的中间件处理所有与设备UUID相关的操作
- **`deviceMiddleware`**: 自动获取或创建设备,设备不存在时自动创建
- **`requireWriteAuth`**: 验证写权限,检查设备密码
- **`tokenAuth`**: Token认证用于应用访问
### 2. 设备自动创建
**重要变化**: 当使用一个新的UUID访问API时后端会自动创建该设备无需手动调用创建设备接口。
### 3. 权限模型
- **读操作**: 永远不需要密码
- **写操作**: 如果设备设置了密码则需要验证,否则直接允许
---
## 场景1: 基于UUID的直接访问
适用于:用户直接操作设备数据(设备配置、设备管理等)
### 读操作(无需密码)
**请求方式**: `GET /device/:deviceUuid/*`
**特点**:
- 设备不存在时自动创建
- 无需提供密码
- 任何知道UUID的人都可以读取
**请求示例**:
```http
GET /device/550e8400-e29b-41d4-a716-446655440000/info
Headers:
x-site-key: your-site-key
```
**成功响应** (200):
```json
{
"id": 1,
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"name": null,
"password": null,
"passwordHint": null,
"accountId": null,
"createdAt": "2025-01-30T10:00:00.000Z",
"updatedAt": "2025-01-30T10:00:00.000Z"
}
```
### 写操作(需要密码验证)
**请求方式**: `POST|PUT|DELETE /device/:deviceUuid/*`
**特点**:
- 设备不存在时自动创建
- 如果设备设置了密码,必须提供正确密码
- 如果设备没有密码,直接允许写入
#### 密码提供方式
**方式1: 通过请求体(推荐)**
```http
POST /device/550e8400-e29b-41d4-a716-446655440000/config
Headers:
Content-Type: application/json
x-site-key: your-site-key
Body:
{
"password": "device-password",
"data": {
"theme": "dark",
"language": "zh-CN"
}
}
```
**方式2: 通过查询参数**
```http
POST /device/550e8400-e29b-41d4-a716-446655440000/config?password=device-password
Headers:
Content-Type: application/json
x-site-key: your-site-key
Body:
{
"data": {
"theme": "dark",
"language": "zh-CN"
}
}
```
**成功响应** (200):
```json
{
"message": "数据已更新",
"updatedAt": "2025-01-30T10:05:00.000Z"
}
```
**错误响应 - 需要密码** (401):
```json
{
"statusCode": 401,
"message": "此操作需要密码",
"passwordHint": "您的生日8位数字"
}
```
**错误响应 - 密码错误** (401):
```json
{
"statusCode": 401,
"message": "密码错误"
}
```
---
## 场景2: 基于Token的应用访问
适用于应用访问KV存储数据
### 步骤1: 获取Token
**请求方式**: `POST /apps/:appId/authorize`
**请求示例**:
```http
POST /apps/1/authorize
Headers:
Content-Type: application/json
x-site-key: your-site-key
Body:
{
"deviceUuid": "550e8400-e29b-41d4-a716-446655440000",
"password": "device-password",
"note": "我的应用授权"
}
```
**说明**:
- `deviceUuid`: 必填设备UUID
- `password`: 如果设备有密码则必填
- `note`: 可选,授权备注
**成功响应** (200):
```json
{
"token": "clxxx123456789abcdefg",
"appId": 1,
"appName": "我的应用",
"deviceUuid": "550e8400-e29b-41d4-a716-446655440000",
"deviceName": null,
"note": "我的应用授权",
"authorizedAt": "2025-01-30T10:00:00.000Z"
}
```
**错误响应 - 需要密码** (401):
```json
{
"statusCode": 401,
"message": "此操作需要密码",
"passwordHint": "您的生日8位数字"
}
```
### 步骤2: 使用Token访问KV存储
#### Token提供方式
**方式1: Authorization Header推荐**
```http
Headers:
Authorization: Bearer clxxx123456789abcdefg
```
**方式2: Query参数**
```http
?token=clxxx123456789abcdefg
```
**方式3: Request Body**
```json
{
"token": "clxxx123456789abcdefg",
...
}
```
---
### KV API端点
| 方法 | 端点 | 说明 |
|------|------|------|
| GET | `/kv` | 列出所有键(含元数据) |
| GET | `/kv/_keys` | 列出所有键名(仅键名) |
| GET | `/kv/:key` | 获取键值 |
| GET | `/kv/:key/metadata` | 获取键元数据 |
| POST | `/kv/:key` | 创建/更新键值 |
| POST | `/kv/_batchimport` | 批量导入 |
| DELETE | `/kv/:key` | 删除键值 |
---
### GET /kv - 列出所有键(含元数据)
**请求示例**:
```http
GET /kv?sortBy=key&sortDir=asc&limit=10&skip=0
Headers:
Authorization: Bearer clxxx123456789abcdefg
x-site-key: your-site-key
```
**查询参数**:
- `sortBy`: 排序字段key/createdAt/updatedAt默认 key
- `sortDir`: 排序方向asc/desc默认 asc
- `limit`: 每页数量,默认 100
- `skip`: 跳过数量,默认 0
**成功响应** (200):
```json
{
"items": [
{
"deviceId": 1,
"key": "config",
"metadata": {
"creatorIp": "192.168.1.1",
"createdAt": "2025-01-30T10:00:00.000Z",
"updatedAt": "2025-01-30T10:00:00.000Z"
}
},
{
"deviceId": 1,
"key": "user.name",
"metadata": {
"creatorIp": "192.168.1.1",
"createdAt": "2025-01-30T10:01:00.000Z",
"updatedAt": "2025-01-30T10:01:00.000Z"
}
}
],
"total_rows": 25,
"load_more": "/kv?sortBy=key&sortDir=asc&limit=10&skip=10"
}
```
---
### GET /kv/_keys - 列出所有键名
**请求示例**:
```http
GET /kv/_keys?limit=50&skip=0
Headers:
Authorization: Bearer clxxx123456789abcdefg
x-site-key: your-site-key
```
**成功响应** (200):
```json
{
"keys": ["config", "user.name", "user.theme", "app.settings"],
"total_rows": 4,
"current_page": {
"limit": 50,
"skip": 0,
"count": 4
}
}
```
---
### GET /kv/:key - 获取键值
**请求示例**:
```http
GET /kv/config
Headers:
Authorization: Bearer clxxx123456789abcdefg
x-site-key: your-site-key
```
**成功响应** (200):
```json
{
"theme": "dark",
"language": "zh-CN",
"fontSize": 14
}
```
**错误响应 - 键不存在** (404):
```json
{
"statusCode": 404,
"message": "未找到键名为 'config' 的记录"
}
```
---
### GET /kv/:key/metadata - 获取键元数据
**请求示例**:
```http
GET /kv/config/metadata
Headers:
Authorization: Bearer clxxx123456789abcdefg
x-site-key: your-site-key
```
**成功响应** (200):
```json
{
"deviceId": 1,
"key": "config",
"metadata": {
"creatorIp": "192.168.1.1",
"createdAt": "2025-01-30T10:00:00.000Z",
"updatedAt": "2025-01-30T10:05:00.000Z"
}
}
```
---
### POST /kv/:key - 创建/更新键值
**请求示例**:
```http
POST /kv/config
Headers:
Authorization: Bearer clxxx123456789abcdefg
Content-Type: application/json
x-site-key: your-site-key
Body:
{
"theme": "dark",
"language": "zh-CN",
"fontSize": 14
}
```
**成功响应** (200):
```json
{
"deviceId": 1,
"key": "config",
"created": false,
"updatedAt": "2025-01-30T10:10:00.000Z"
}
```
**说明**:
- `created`: true表示新建false表示更新
**错误响应 - 空值** (400):
```json
{
"statusCode": 400,
"message": "请提供有效的JSON值"
}
```
---
### POST /kv/_batchimport - 批量导入
**请求示例**:
```http
POST /kv/_batchimport
Headers:
Authorization: Bearer clxxx123456789abcdefg
Content-Type: application/json
x-site-key: your-site-key
Body:
{
"config": {
"theme": "dark",
"language": "zh-CN"
},
"user.name": {
"firstName": "John",
"lastName": "Doe"
},
"app.settings": {
"notifications": true
}
}
```
**成功响应** (200):
```json
{
"deviceId": 1,
"total": 3,
"successful": 3,
"failed": 0,
"results": [
{
"key": "config",
"created": false
},
{
"key": "user.name",
"created": true
},
{
"key": "app.settings",
"created": true
}
]
}
```
**部分失败响应** (200):
```json
{
"deviceId": 1,
"total": 3,
"successful": 2,
"failed": 1,
"results": [
{
"key": "config",
"created": false
},
{
"key": "user.name",
"created": true
}
],
"errors": [
{
"key": "app.settings",
"error": "Invalid value"
}
]
}
```
---
### DELETE /kv/:key - 删除键值
**请求示例**:
```http
DELETE /kv/config
Headers:
Authorization: Bearer clxxx123456789abcdefg
x-site-key: your-site-key
```
**成功响应** (204):
```
无响应体
```
**错误响应 - 键不存在** (404):
```json
{
"statusCode": 404,
"message": "未找到键名为 'config' 的记录"
}
```
---
## 错误码参考
| 状态码 | 说明 | 场景 |
|--------|------|------|
| 200 | 成功 | 操作成功 |
| 204 | 成功(无内容) | 删除成功 |
| 400 | 请求错误 | 参数缺失或格式错误 |
| 401 | 未授权 | 需要密码、密码错误、Token无效 |
| 403 | 禁止访问 | 权限不足 |
| 404 | 未找到 | 资源不存在 |
| 500 | 服务器错误 | 服务器内部错误 |
---
## 401错误详解
### 需要密码
```json
{
"statusCode": 401,
"message": "此操作需要密码",
"passwordHint": "您的生日8位数字"
}
```
**处理方式**: 提示用户输入密码,使用 `passwordHint` 作为提示信息
### 密码错误
```json
{
"statusCode": 401,
"message": "密码错误"
}
```
**处理方式**: 提示用户密码错误,允许重试
### Token无效
```json
{
"statusCode": 401,
"message": "未提供身份验证令牌"
}
```
```json
{
"statusCode": 401,
"message": "无效的身份验证令牌"
}
```
**处理方式**: 清除本地Token引导用户重新授权
---
## 迁移检查清单
### Phase 1: 基础适配
- [ ] 移除手动创建设备的逻辑(设备会自动创建)
- [ ] 更新密码提供方式从header改为body/query
- [ ] 实现统一的错误处理
- [ ] 更新API端点路径
### Phase 2: Token集成
- [ ] 实现应用授权流程POST /apps/:appId/authorize
- [ ] 集成Token到KV操作
- [ ] 实现Token存储和管理localStorage
- [ ] 处理Token过期/无效场景
### Phase 3: 优化
- [ ] 封装统一的API客户端
- [ ] 实现请求重试机制
- [ ] 添加Loading状态管理
- [ ] 优化错误提示用户体验
### Phase 4: 测试
- [ ] 测试设备自动创建
- [ ] 测试密码验证流程(需要密码、密码错误、密码正确)
- [ ] 测试Token授权流程
- [ ] 测试各种错误场景404、401、400等
---
## 关键注意事项
### 1. 设备自动创建
- ✅ 无需手动创建设备,首次访问自动创建
- ✅ 简化前端流程减少API调用
- ⚠️ 确保UUID使用正确的格式建议使用uuidv4
### 2. 密码处理
- ✅ 读操作永远不需要密码
- ✅ 写操作只在设备设置了密码时才需要
- ⚠️ 密码通过body或query提供不要放在header中
- ⚠️ 注意区分"需要密码"和"密码错误"两种情况
### 3. Token管理
- ✅ Token一次获取可重复使用
- ✅ Token与设备和应用绑定
- ⚠️ Token需要安全存储localStorage/sessionStorage
- ⚠️ Token失效时需要重新授权
### 4. Header要求
- 所有请求必须携带 `x-site-key` header
- Token认证使用 `Authorization: Bearer <token>` header推荐
---