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:
2025-09-24 05:38:10 +08:00
parent 43a828a2c6
commit 97042e3d7b
4 changed files with 159 additions and 30 deletions

View File

@@ -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": []
} }

View File

@@ -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;

View File

@@ -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",

1
pm2.sh Normal file
View File

@@ -0,0 +1 @@
pm2 start npm --name accounts-manager-web -- run start:prod