feat: 完善 /config /path /profile 命令的 add/remove/list 功能
- /config: 新增 add/remove 命令,支持存储桶的添加和删除(带引用检查) - /path: 新增 remove 命令,支持预设路径删除(带引用检查) - /profile: 已有 add/remove/list 功能,增强验证逻辑 - 帮助卡片:全面更新所有命令的详细说明和示例 - README.md: 补充完整的命令表格和使用示例 - CHANGELOG.md: 创建更新日志文档 - 安全保护:删除前检查引用关系,禁止删除 default 存储桶
This commit is contained in:
137
CHANGELOG.md
Normal file
137
CHANGELOG.md
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
# 更新日志
|
||||||
|
|
||||||
|
## v5.1 - 2026-03-14
|
||||||
|
|
||||||
|
### ✨ 新增功能
|
||||||
|
|
||||||
|
#### `/config` 命令增强
|
||||||
|
- ✅ `add` - 添加存储桶配置
|
||||||
|
```bash
|
||||||
|
/config add <名称> <accessKey> <secretKey> <bucket> <region> <domain>
|
||||||
|
示例:/config add mybucket xxxxxx yyyyyy my-bucket z0 https://cdn.example.com
|
||||||
|
```
|
||||||
|
- ✅ `remove` - 删除存储桶配置(带引用检查)
|
||||||
|
```bash
|
||||||
|
/config remove <名称>
|
||||||
|
```
|
||||||
|
- ✅ `list` - 查看所有存储桶(已有)
|
||||||
|
- ✅ `set` - 修改配置项(已有)
|
||||||
|
|
||||||
|
**保护机制:**
|
||||||
|
- ⚠️ 不能删除 `default` 存储桶
|
||||||
|
- ⚠️ 删除前检查是否有上传配置引用该存储桶
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### `/path` 命令增强
|
||||||
|
- ✅ `add` - 添加预设路径(已有)
|
||||||
|
```bash
|
||||||
|
/path add <名称> <路径>
|
||||||
|
示例:/path add backup /backup/
|
||||||
|
```
|
||||||
|
- ✅ `remove` - 删除预设路径(带引用检查)
|
||||||
|
```bash
|
||||||
|
/path remove <名称>
|
||||||
|
```
|
||||||
|
- ✅ `list` - 查看所有预设路径(已有)
|
||||||
|
|
||||||
|
**保护机制:**
|
||||||
|
- ⚠️ 删除前检查是否有上传配置引用该路径
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### `/profile` 命令增强
|
||||||
|
- ✅ `add` - 添加上传配置模板(已有)
|
||||||
|
```bash
|
||||||
|
/profile add <名称> <存储桶> [路径键名]
|
||||||
|
示例:/profile add IPA 上传 default ipa
|
||||||
|
```
|
||||||
|
- ✅ `remove` - 删除上传配置模板(已有)
|
||||||
|
```bash
|
||||||
|
/profile remove <名称>
|
||||||
|
```
|
||||||
|
- ✅ `list` - 查看所有上传配置模板(已有)
|
||||||
|
|
||||||
|
**验证机制:**
|
||||||
|
- ✅ 添加时验证存储桶是否存在
|
||||||
|
- ✅ 添加时验证路径键名是否存在(如果提供)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 📖 文档更新
|
||||||
|
|
||||||
|
#### 帮助卡片全面升级
|
||||||
|
- 详细说明了所有命令的用法
|
||||||
|
- 添加了完整的示例
|
||||||
|
- 增加了注意事项说明
|
||||||
|
- 优化了卡片布局和可读性
|
||||||
|
|
||||||
|
#### README.md 更新
|
||||||
|
- 补充了完整的命令表格
|
||||||
|
- 添加了每个命令的详细示例
|
||||||
|
- 分类整理了上传命令、存储桶配置、预设路径、上传配置模板
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🛡️ 安全增强
|
||||||
|
|
||||||
|
1. **引用检查** - 删除存储桶/路径前自动检查是否被上传配置引用
|
||||||
|
2. **默认保护** - 禁止删除 default 存储桶
|
||||||
|
3. **参数验证** - 添加存储桶时验证区域代码
|
||||||
|
4. **存在性检查** - 添加上传配置时验证存储桶和路径是否存在
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 📝 使用示例
|
||||||
|
|
||||||
|
#### 完整配置流程
|
||||||
|
```bash
|
||||||
|
# 1. 添加存储桶
|
||||||
|
/config add production xxxxxx yyyyyy prod-bucket z0 https://cdn.example.com
|
||||||
|
|
||||||
|
# 2. 添加预设路径
|
||||||
|
/path add app /app/
|
||||||
|
/path add backup /backup/
|
||||||
|
|
||||||
|
# 3. 创建上传配置
|
||||||
|
/profile add 生产环境上传 production app
|
||||||
|
/profile add 备份上传 production backup
|
||||||
|
|
||||||
|
# 4. 查看配置
|
||||||
|
/config list
|
||||||
|
/path list
|
||||||
|
/profile list
|
||||||
|
|
||||||
|
# 5. 开始上传
|
||||||
|
/upload
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 删除配置
|
||||||
|
```bash
|
||||||
|
# 删除上传配置
|
||||||
|
/profile remove 备份上传
|
||||||
|
|
||||||
|
# 删除预设路径
|
||||||
|
/path remove backup
|
||||||
|
|
||||||
|
# 删除存储桶(需确保没有被引用)
|
||||||
|
/config remove production
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🐛 已知问题
|
||||||
|
|
||||||
|
无
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 📦 升级方式
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 重启服务
|
||||||
|
pm2 restart qiniu-bot
|
||||||
|
|
||||||
|
# 查看日志
|
||||||
|
pm2 logs qiniu-bot
|
||||||
|
```
|
||||||
55
README.md
55
README.md
@@ -197,16 +197,57 @@ NODE_ENV=production
|
|||||||
|
|
||||||
### 常用命令
|
### 常用命令
|
||||||
|
|
||||||
|
#### 📤 上传命令
|
||||||
|
|
||||||
| 命令 | 说明 |
|
| 命令 | 说明 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| `/upload` | 开始上传流程 |
|
| `/upload` 或 `/u` | 开始上传流程 |
|
||||||
| `/config list` | 查看存储桶配置 |
|
| `/help` 或 `/qh` | 查看详细帮助 |
|
||||||
| `/path list` | 查看预设路径 |
|
|
||||||
|
#### ⚙️ 存储桶配置 (`/config` 或 `/qc`)
|
||||||
|
|
||||||
|
| 命令 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `/config list` | 查看所有存储桶配置 |
|
||||||
|
| `/config add <名称> <accessKey> <secretKey> <bucket> <region> <domain>` | 添加存储桶配置 |
|
||||||
|
| `/config remove <名称>` | 删除存储桶配置 |
|
||||||
|
| `/config set <键路径> <值>` | 修改配置项 |
|
||||||
|
|
||||||
|
**示例:**
|
||||||
|
```bash
|
||||||
|
/config add mybucket xxxxxx yyyyyy my-bucket z0 https://cdn.example.com
|
||||||
|
/config remove mybucket
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 📁 预设路径 (`/path`)
|
||||||
|
|
||||||
|
| 命令 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `/path list` | 查看所有预设路径 |
|
||||||
| `/path add <名称> <路径>` | 添加预设路径 |
|
| `/path add <名称> <路径>` | 添加预设路径 |
|
||||||
| `/profile list` | 查看上传配置模板 |
|
| `/path remove <名称>` | 删除预设路径 |
|
||||||
| `/profile add <名称> <桶> [路径]` | 添加上传配置 |
|
|
||||||
| `/profile remove <名称>` | 删除上传配置 |
|
**示例:**
|
||||||
| `/help` | 查看详细帮助 |
|
```bash
|
||||||
|
/path add backup /backup/
|
||||||
|
/path add ipa /ipa/gamehall_jinxian2.ipa
|
||||||
|
/path remove backup
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 📤 上传配置模板 (`/profile`)
|
||||||
|
|
||||||
|
| 命令 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `/profile list` | 查看所有上传配置模板 |
|
||||||
|
| `/profile add <名称> <存储桶> [路径键名]` | 添加上传配置模板 |
|
||||||
|
| `/profile remove <名称>` | 删除上传配置模板 |
|
||||||
|
|
||||||
|
**示例:**
|
||||||
|
```bash
|
||||||
|
/profile add 默认上传 default
|
||||||
|
/profile add IPA 上传 default ipa
|
||||||
|
/profile remove IPA 上传
|
||||||
|
```
|
||||||
|
|
||||||
### 使用流程
|
### 使用流程
|
||||||
|
|
||||||
|
|||||||
131
src/index.js
131
src/index.js
@@ -530,10 +530,76 @@ async function handleConfigCommandV2(message, content, feishu, uploader) {
|
|||||||
const args = text.replace(/^\/(config|qc)\s*/i, '').trim().split(/\s+/);
|
const args = text.replace(/^\/(config|qc)\s*/i, '').trim().split(/\s+/);
|
||||||
const subCommand = args[0];
|
const subCommand = args[0];
|
||||||
|
|
||||||
|
const configPath = path.join(process.cwd(), 'config', 'qiniu-config.json');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const fullConfig = loadFullConfig();
|
||||||
|
|
||||||
if (subCommand === 'list' || !subCommand) {
|
if (subCommand === 'list' || !subCommand) {
|
||||||
const configData = await uploader.listConfig();
|
const configData = await uploader.listConfig();
|
||||||
await feishu.sendCard(chatId, createBucketsListCard(configData));
|
await feishu.sendCard(chatId, createBucketsListCard(configData));
|
||||||
|
} else if (subCommand === 'add') {
|
||||||
|
// /config add <名称> <accessKey> <secretKey> <bucket> <region> <domain>
|
||||||
|
if (args.length < 7) {
|
||||||
|
throw new Error('用法:/config add <名称> <accessKey> <secretKey> <bucket> <region> <domain>\n示例:/config add mybucket xxxxxx yyyyyy my-bucket z0 https://cdn.example.com');
|
||||||
|
}
|
||||||
|
const name = args[1];
|
||||||
|
const accessKey = args[2];
|
||||||
|
const secretKey = args[3];
|
||||||
|
const bucket = args[4];
|
||||||
|
const region = args[5];
|
||||||
|
const domain = args[6];
|
||||||
|
|
||||||
|
// 验证区域代码
|
||||||
|
const validRegions = ['z0', 'z1', 'z2', 'na0', 'as0'];
|
||||||
|
if (!validRegions.includes(region)) {
|
||||||
|
throw new Error(`无效的区域代码,可用:${validRegions.join(', ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
fullConfig.buckets[name] = {
|
||||||
|
accessKey,
|
||||||
|
secretKey,
|
||||||
|
bucket,
|
||||||
|
region,
|
||||||
|
domain
|
||||||
|
};
|
||||||
|
|
||||||
|
fs.writeFileSync(configPath, JSON.stringify(fullConfig, null, 2));
|
||||||
|
await feishu.sendMessage(chatId, {
|
||||||
|
msg_type: 'text',
|
||||||
|
content: { text: `✅ 已添加存储桶配置:**${name}**\n存储桶:${bucket}\n区域:${region}\n域名:${domain}` }
|
||||||
|
});
|
||||||
|
} else if (subCommand === 'remove' || subCommand === 'del') {
|
||||||
|
if (args.length < 2) {
|
||||||
|
throw new Error('用法:/config remove <名称>');
|
||||||
|
}
|
||||||
|
const name = args[1];
|
||||||
|
|
||||||
|
if (!fullConfig.buckets[name]) {
|
||||||
|
throw new Error(`存储桶 "${name}" 不存在,可用:${Object.keys(fullConfig.buckets).join(', ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 不允许删除 default 存储桶
|
||||||
|
if (name === 'default') {
|
||||||
|
throw new Error('不能删除 default 存储桶');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有上传配置引用此存储桶
|
||||||
|
const profiles = fullConfig.uploadProfiles || {};
|
||||||
|
const referencedBy = Object.entries(profiles)
|
||||||
|
.filter(([_, config]) => config.bucket === name)
|
||||||
|
.map(([name, _]) => name);
|
||||||
|
|
||||||
|
if (referencedBy.length > 0) {
|
||||||
|
throw new Error(`无法删除:存储桶 "${name}" 被以下上传配置引用:${referencedBy.join(', ')}\n请先删除或修改这些上传配置`);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete fullConfig.buckets[name];
|
||||||
|
fs.writeFileSync(configPath, JSON.stringify(fullConfig, null, 2));
|
||||||
|
await feishu.sendMessage(chatId, {
|
||||||
|
msg_type: 'text',
|
||||||
|
content: { text: `✅ 已删除存储桶配置:**${name}**` }
|
||||||
|
});
|
||||||
} else if (subCommand === 'set') {
|
} else if (subCommand === 'set') {
|
||||||
const [keyPath, value] = args.slice(1);
|
const [keyPath, value] = args.slice(1);
|
||||||
await uploader.setConfigValue(keyPath, value);
|
await uploader.setConfigValue(keyPath, value);
|
||||||
@@ -541,11 +607,13 @@ async function handleConfigCommandV2(message, content, feishu, uploader) {
|
|||||||
msg_type: 'text',
|
msg_type: 'text',
|
||||||
content: { text: `✅ 已设置 ${keyPath} = ${value}` }
|
content: { text: `✅ 已设置 ${keyPath} = ${value}` }
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
throw new Error(`未知命令:${subCommand}\n可用命令:list, add, remove, set`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await feishu.sendMessage(chatId, {
|
await feishu.sendMessage(chatId, {
|
||||||
msg_type: 'text',
|
msg_type: 'text',
|
||||||
content: { text: `❌ 配置失败:${error.message}` }
|
content: { text: `❌ 配置管理失败:${error.message}` }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -561,18 +629,51 @@ async function handlePathCommandV2(message, content, feishu) {
|
|||||||
try {
|
try {
|
||||||
const fullConfig = loadFullConfig();
|
const fullConfig = loadFullConfig();
|
||||||
|
|
||||||
if (subCommand === 'list') {
|
if (subCommand === 'list' || !subCommand) {
|
||||||
const paths = fullConfig.uploadPaths || {};
|
const paths = fullConfig.uploadPaths || {};
|
||||||
await feishu.sendCard(chatId, createPathsListCard(paths));
|
await feishu.sendCard(chatId, createPathsListCard(paths));
|
||||||
} else if (subCommand === 'add') {
|
} else if (subCommand === 'add') {
|
||||||
|
if (args.length < 3) {
|
||||||
|
throw new Error('用法:/path add <名称> <路径>\n示例:/path add backup /backup/');
|
||||||
|
}
|
||||||
const name = args[1];
|
const name = args[1];
|
||||||
const pathValue = args[2];
|
const pathValue = args[2];
|
||||||
|
|
||||||
|
fullConfig.uploadPaths = fullConfig.uploadPaths || {};
|
||||||
fullConfig.uploadPaths[name] = pathValue;
|
fullConfig.uploadPaths[name] = pathValue;
|
||||||
fs.writeFileSync(configPath, JSON.stringify(fullConfig, null, 2));
|
fs.writeFileSync(configPath, JSON.stringify(fullConfig, null, 2));
|
||||||
await feishu.sendMessage(chatId, {
|
await feishu.sendMessage(chatId, {
|
||||||
msg_type: 'text',
|
msg_type: 'text',
|
||||||
content: { text: `✅ 已添加预设路径:**${name}** → ${pathValue}` }
|
content: { text: `✅ 已添加预设路径:**${name}** → ${pathValue}` }
|
||||||
});
|
});
|
||||||
|
} else if (subCommand === 'remove' || subCommand === 'del') {
|
||||||
|
if (args.length < 2) {
|
||||||
|
throw new Error('用法:/path remove <名称>');
|
||||||
|
}
|
||||||
|
const name = args[1];
|
||||||
|
|
||||||
|
if (!fullConfig.uploadPaths || !fullConfig.uploadPaths[name]) {
|
||||||
|
throw new Error(`预设路径 "${name}" 不存在,可用:${Object.keys(fullConfig.uploadPaths || {}).join(', ') || '无'}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有上传配置引用此路径
|
||||||
|
const profiles = fullConfig.uploadProfiles || {};
|
||||||
|
const referencedBy = Object.entries(profiles)
|
||||||
|
.filter(([_, config]) => config.path === name)
|
||||||
|
.map(([pname, _]) => pname);
|
||||||
|
|
||||||
|
if (referencedBy.length > 0) {
|
||||||
|
throw new Error(`无法删除:路径 "${name}" 被以下上传配置引用:${referencedBy.join(', ')}\n请先删除或修改这些上传配置`);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete fullConfig.uploadPaths[name];
|
||||||
|
fs.writeFileSync(configPath, JSON.stringify(fullConfig, null, 2));
|
||||||
|
await feishu.sendMessage(chatId, {
|
||||||
|
msg_type: 'text',
|
||||||
|
content: { text: `✅ 已删除预设路径:**${name}**` }
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw new Error(`未知命令:${subCommand}\n可用命令:list, add, remove`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await feishu.sendMessage(chatId, {
|
await feishu.sendMessage(chatId, {
|
||||||
@@ -679,7 +780,7 @@ async function handleHelpCommandV2(chatId, feishu) {
|
|||||||
tag: 'div',
|
tag: 'div',
|
||||||
text: {
|
text: {
|
||||||
tag: 'lark_md',
|
tag: 'lark_md',
|
||||||
content: '**📤 上传方式**\n\n**方式 1:选择配置 → 发送文件**\n1️⃣ 发送 /upload\n2️⃣ 选择上传配置\n3️⃣ 发送文件\n4️⃣ 确认上传\n\n**方式 2:发送文件 → 选择配置**\n1️⃣ 直接发送文件\n2️⃣ 选择上传配置\n3️⃣ 确认上传'
|
content: '**📤 上传方式**\n\n**方式 1:选择配置 → 发送文件**\n1️⃣ 发送 `/upload`\n2️⃣ 选择上传配置\n3️⃣ 发送文件\n4️⃣ 确认上传\n\n**方式 2:发送文件 → 选择配置**\n1️⃣ 直接发送文件\n2️⃣ 选择上传配置\n3️⃣ 确认上传'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -689,7 +790,7 @@ async function handleHelpCommandV2(chatId, feishu) {
|
|||||||
tag: 'div',
|
tag: 'div',
|
||||||
text: {
|
text: {
|
||||||
tag: 'lark_md',
|
tag: 'lark_md',
|
||||||
content: '**⚙️ 配置命令**\n\n• /config list - 查看存储桶\n• /profile list - 查看上传配置\n• /profile add <名称> <桶> [路径] - 添加配置\n• /profile remove <名称> - 删除配置\n\n**📁 路径命令**\n\n• /path list - 查看预设路径\n• /path add <名称> <路径> - 添加路径'
|
content: '**⚙️ 存储桶配置 (/config)**\n\n• `/config list` - 查看所有存储桶配置\n• `/config add <名称> <accessKey> <secretKey> <bucket> <region> <domain>` - 添加存储桶\n• `/config remove <名称>` - 删除存储桶\n• `/config set <键路径> <值>` - 修改配置项\n\n**示例:**\n`/config add mybucket xxxxxx yyyyyy my-bucket z0 https://cdn.example.com`'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -699,7 +800,27 @@ async function handleHelpCommandV2(chatId, feishu) {
|
|||||||
tag: 'div',
|
tag: 'div',
|
||||||
text: {
|
text: {
|
||||||
tag: 'lark_md',
|
tag: 'lark_md',
|
||||||
content: '**💡 示例**\n\n`/profile add IPA 上传 default ipa`\n`/path add backup /backup/`\n\n**提示:** 上传同名文件会自动覆盖'
|
content: '**📁 预设路径 (/path)**\n\n• `/path list` - 查看所有预设路径\n• `/path add <名称> <路径>` - 添加预设路径\n• `/path remove <名称>` - 删除预设路径\n\n**示例:**\n`/path add backup /backup/`\n`/path add ipa /ipa/gamehall_jinxian2.ipa`'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: 'hr'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: 'div',
|
||||||
|
text: {
|
||||||
|
tag: 'lark_md',
|
||||||
|
content: '**📤 上传配置 (/profile)**\n\n• `/profile list` - 查看所有上传配置模板\n• `/profile add <名称> <存储桶> [路径键名]` - 添加上传配置\n• `/profile remove <名称>` - 删除上传配置\n\n**示例:**\n`/profile add 默认上传 default`\n`/profile add IPA 上传 default ipa`'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: 'hr'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: 'div',
|
||||||
|
text: {
|
||||||
|
tag: 'lark_md',
|
||||||
|
content: '**💡 快捷命令**\n\n• `/upload` 或 `/u` - 开始上传\n• `/help` 或 `/qh` - 显示帮助\n• `/config` 或 `/qc` - 配置管理\n\n**⚠️ 注意事项**\n• 删除存储桶/路径前需确保没有被上传配置引用\n• 不能删除 default 存储桶\n• 上传同名文件会自动覆盖'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user