chore: 更新项目配置和部署脚本
- 更新Claude设置允许pm2命令执行 - 修改package.json脚本配置,统一生产环境端口13007 - 添加pm2.sh部署脚本 - 更新API文档移除认证相关内容 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,8 @@
|
|||||||
"Bash(git push:*)",
|
"Bash(git push:*)",
|
||||||
"Bash(git add:*)",
|
"Bash(git add:*)",
|
||||||
"Bash(npm run build:*)",
|
"Bash(npm run build:*)",
|
||||||
"Bash(git add:*)"
|
"Bash(git add:*)",
|
||||||
|
"Bash(pm2:*)"
|
||||||
],
|
],
|
||||||
"deny": []
|
"deny": []
|
||||||
}
|
}
|
||||||
|
|||||||
179
docs/API文档.md
179
docs/API文档.md
@@ -9,7 +9,6 @@
|
|||||||
- **基础URL**: `{API_BASE_URL}`
|
- **基础URL**: `{API_BASE_URL}`
|
||||||
- **数据格式**: JSON
|
- **数据格式**: JSON
|
||||||
- **字符编码**: UTF-8
|
- **字符编码**: UTF-8
|
||||||
- **认证方式**: 需要在请求头中包含认证信息
|
|
||||||
|
|
||||||
### 通用响应格式
|
### 通用响应格式
|
||||||
|
|
||||||
@@ -145,8 +144,7 @@ async function acquireAccounts(ownerId, platform, count = 1) {
|
|||||||
const response = await fetch(`/s/v1/${ownerId}/acquire?platform=${platform}&count=${count}`, {
|
const response = await fetch(`/s/v1/${ownerId}/acquire?platform=${platform}&count=${count}`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json'
|
||||||
'Authorization': 'Bearer YOUR_TOKEN'
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -219,8 +217,7 @@ async function updateAccountStatus(ownerId, accountId, newStatus, notes) {
|
|||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json'
|
||||||
'Authorization': 'Bearer YOUR_TOKEN'
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -302,8 +299,7 @@ async function uploadAccounts(ownerId, accounts) {
|
|||||||
const response = await fetch(`/s/v1/${ownerId}/upload`, {
|
const response = await fetch(`/s/v1/${ownerId}/upload`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json'
|
||||||
'Authorization': 'Bearer YOUR_TOKEN'
|
|
||||||
},
|
},
|
||||||
body: JSON.stringify(accounts)
|
body: JSON.stringify(accounts)
|
||||||
});
|
});
|
||||||
@@ -440,8 +436,7 @@ async function getAccountsList(filters, pagination, sort) {
|
|||||||
const response = await fetch('/web/v1/accounts/list', {
|
const response = await fetch('/web/v1/accounts/list', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json'
|
||||||
'Authorization': 'Bearer YOUR_TOKEN'
|
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
filters,
|
filters,
|
||||||
@@ -526,8 +521,7 @@ async function batchDeleteAccounts(ids) {
|
|||||||
const response = await fetch('/web/v1/accounts/delete-batch', {
|
const response = await fetch('/web/v1/accounts/delete-batch', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json'
|
||||||
'Authorization': 'Bearer YOUR_TOKEN'
|
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ ids })
|
body: JSON.stringify({ ids })
|
||||||
});
|
});
|
||||||
@@ -607,8 +601,7 @@ async function batchUpdateAccounts(ids, payload) {
|
|||||||
const response = await fetch('/web/v1/accounts/update-batch', {
|
const response = await fetch('/web/v1/accounts/update-batch', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json'
|
||||||
'Authorization': 'Bearer YOUR_TOKEN'
|
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ ids, payload })
|
body: JSON.stringify({ ids, payload })
|
||||||
});
|
});
|
||||||
@@ -632,7 +625,136 @@ if (result.code === 0) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 7. 统计概览接口
|
### 7. 账户导出接口
|
||||||
|
|
||||||
|
用于Web端批量导出账户并设置状态为已导出。
|
||||||
|
|
||||||
|
#### 请求
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /web/v1/accounts/export
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 请求体
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"ids": [1, 2, 3],
|
||||||
|
"mode": "text"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 参数
|
||||||
|
|
||||||
|
| 参数名 | 位置 | 类型 | 必填 | 说明 |
|
||||||
|
|--------|------|------|------|------|
|
||||||
|
| ids | 请求体 | number[] | 是 | 要导出的账户ID数组 |
|
||||||
|
| mode | 请求体 | string | 是 | 导出模式:"text"或"object" |
|
||||||
|
|
||||||
|
#### 导出模式说明
|
||||||
|
|
||||||
|
- `text`: 文本模式,返回的data字段为字符串,每行一个账户的data字段内容
|
||||||
|
- `object`: 对象模式,返回的data字段为完整的账户对象数组
|
||||||
|
|
||||||
|
#### 响应
|
||||||
|
|
||||||
|
文本模式成功响应:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"message": "Successfully exported 3 accounts.",
|
||||||
|
"data": {
|
||||||
|
"exportedCount": 3,
|
||||||
|
"data": "{\"username\":\"user1\",\"password\":\"pass1\"}\n{\"username\":\"user2\",\"password\":\"pass2\"}\n{\"username\":\"user3\",\"password\":\"pass3\"}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
对象模式成功响应:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"message": "Successfully exported 3 accounts.",
|
||||||
|
"data": {
|
||||||
|
"exportedCount": 3,
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"ownerId": "owner123",
|
||||||
|
"platform": "example",
|
||||||
|
"customId": "user1",
|
||||||
|
"data": "{\"username\":\"user1\",\"password\":\"pass1\"}",
|
||||||
|
"status": "exported",
|
||||||
|
"notes": "测试账户1",
|
||||||
|
"lockedAt": null,
|
||||||
|
"createdAt": "2023-01-01T00:00:00.000Z",
|
||||||
|
"updatedAt": "2023-01-01T12:00:00.000Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"ownerId": "owner123",
|
||||||
|
"platform": "example",
|
||||||
|
"customId": "user2",
|
||||||
|
"data": "{\"username\":\"user2\",\"password\":\"pass2\"}",
|
||||||
|
"status": "exported",
|
||||||
|
"notes": "测试账户2",
|
||||||
|
"lockedAt": null,
|
||||||
|
"createdAt": "2023-01-01T00:00:00.000Z",
|
||||||
|
"updatedAt": "2023-01-01T12:00:00.000Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 示例
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 使用fetch导出账户
|
||||||
|
async function exportAccounts(ids, mode) {
|
||||||
|
const response = await fetch('/web/v1/accounts/export', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ ids, mode })
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文本模式导出示例
|
||||||
|
const textResult = await exportAccounts([1, 2, 3], 'text');
|
||||||
|
if (textResult.code === 0) {
|
||||||
|
console.log('导出成功:', textResult.data.exportedCount);
|
||||||
|
console.log('文本数据:', textResult.data.data);
|
||||||
|
// 可以直接保存为文件或复制到剪贴板
|
||||||
|
const lines = textResult.data.data.split('\n');
|
||||||
|
lines.forEach((line, index) => {
|
||||||
|
console.log(`账户${index + 1}:`, line);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.error('导出失败:', textResult.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对象模式导出示例
|
||||||
|
const objectResult = await exportAccounts([1, 2, 3], 'object');
|
||||||
|
if (objectResult.code === 0) {
|
||||||
|
console.log('导出成功:', objectResult.data.exportedCount);
|
||||||
|
const accounts = objectResult.data.data;
|
||||||
|
accounts.forEach(account => {
|
||||||
|
console.log(`账户ID: ${account.id}, 状态: ${account.status}`);
|
||||||
|
console.log(`数据: ${account.data}`);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.error('导出失败:', objectResult.message);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8. 统计概览接口
|
||||||
|
|
||||||
用于Web端获取账户统计信息。
|
用于Web端获取账户统计信息。
|
||||||
|
|
||||||
@@ -693,8 +815,7 @@ async function getStatsOverview() {
|
|||||||
const response = await fetch('/web/v1/stats/overview', {
|
const response = await fetch('/web/v1/stats/overview', {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json'
|
||||||
'Authorization': 'Bearer YOUR_TOKEN'
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -734,23 +855,17 @@ if (result.code === 0) {
|
|||||||
| 1001 | 资源不存在 | 检查请求的资源是否存在 |
|
| 1001 | 资源不存在 | 检查请求的资源是否存在 |
|
||||||
| 2001 | 参数无效 | 检查请求参数是否符合要求 |
|
| 2001 | 参数无效 | 检查请求参数是否符合要求 |
|
||||||
| 3001 | 资源冲突 | 检查是否有重复数据 |
|
| 3001 | 资源冲突 | 检查是否有重复数据 |
|
||||||
| 4001 | 权限不足 | 检查认证信息是否正确 |
|
|
||||||
| 5001 | 业务错误 | 检查业务逻辑是否正确 |
|
| 5001 | 业务错误 | 检查业务逻辑是否正确 |
|
||||||
|
|
||||||
## 最佳实践
|
## 最佳实践
|
||||||
|
|
||||||
1. **认证**:所有API请求都需要在请求头中包含认证信息,例如:
|
1. **错误处理**:前端应该根据返回的`code`值进行相应的错误处理,而不是依赖`message`字段。
|
||||||
```http
|
|
||||||
Authorization: Bearer YOUR_TOKEN
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **错误处理**:前端应该根据返回的`code`值进行相应的错误处理,而不是依赖`message`字段。
|
2. **分页**:在获取列表数据时,合理设置分页大小,建议不超过100条。
|
||||||
|
|
||||||
3. **分页**:在获取列表数据时,合理设置分页大小,建议不超过100条。
|
3. **批量操作**:批量操作时,建议限制单次操作的数量,避免服务器压力过大。
|
||||||
|
|
||||||
4. **批量操作**:批量操作时,建议限制单次操作的数量,避免服务器压力过大。
|
4. **数据格式**:账户数据字段`data`存储的是JSON格式字符串,前端需要进行相应的序列化和反序列化操作。
|
||||||
|
|
||||||
5. **数据格式**:账户数据字段`data`存储的是JSON格式字符串,前端需要进行相应的序列化和反序列化操作。
|
|
||||||
|
|
||||||
## 类型定义
|
## 类型定义
|
||||||
|
|
||||||
@@ -845,6 +960,18 @@ interface BatchUpdateBody {
|
|||||||
payload: Partial<Pick<Account, 'status' | 'ownerId' | 'notes'>>;
|
payload: Partial<Pick<Account, 'status' | 'ownerId' | 'notes'>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 账户导出请求体
|
||||||
|
interface ExportAccountsBody {
|
||||||
|
ids: number[];
|
||||||
|
mode: 'text' | 'object';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 账户导出响应
|
||||||
|
interface ExportAccountsResponse {
|
||||||
|
exportedCount: number;
|
||||||
|
data: string | Account[];
|
||||||
|
}
|
||||||
|
|
||||||
// 统计概览
|
// 统计概览
|
||||||
interface StatsOverview {
|
interface StatsOverview {
|
||||||
totalAccounts: number;
|
totalAccounts: number;
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev --turbopack",
|
"dev": "next dev --turbopack",
|
||||||
"build": "next build --turbopack",
|
"build": "next build --turbopack",
|
||||||
"start": "next start",
|
"start": "next start -p 13007",
|
||||||
"start:prod": "next start -p 13007",
|
"deploy": "NODE_ENV=production npm run build && npm run start:prod",
|
||||||
"deploy": "pm2 start npm --name accounts-manager-web -- run start:prod"
|
"start:prod": "next start -p 13007"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-checkbox": "^1.3.3",
|
"@radix-ui/react-checkbox": "^1.3.3",
|
||||||
|
|||||||
Reference in New Issue
Block a user