refactor: 重构禅音工具路径与配置,新增私人剪贴板技能

这是一次大型重构和功能新增:
1. 将原 zen_box 工具重命名为 web_zen_box,调整所有相关路径与配置
2. 新增 web_order_box 原生Web应用的完整资源与部署脚本
3. 新增私人剪贴板后端技能与配套测试脚本
4. 修复订单盒子APP的API地址配置,避免路径重复拼接
5. 新增本地开发跨域白名单支持localhost
6. 清理旧版zen_box的冗余文件
This commit is contained in:
yangxiangyuan
2026-07-26 21:03:28 +08:00
parent a062a7e2c0
commit 212dbc4e1d
47 changed files with 6446 additions and 755 deletions
@@ -0,0 +1,79 @@
// 重置 rateLimitMap(通过重启服务或等待)
// 这里直接测试单个请求
const http = require('http')
const BASE = 'http://localhost:8976'
const API = `${BASE}/api/v1/ingest/private_clipboard`
function post(path, body) {
return new Promise((resolve, reject) => {
const data = JSON.stringify(body)
const req = http.request(`${API}${path}`, {
method: 'POST',
headers: {
'X-API-Id': 'private_clipboard',
'X-API-Key': 'pcb_nuS0qf-W63qcvAPsrG5ePA',
'Content-Type': 'application/json; charset=utf-8',
'Content-Length': Buffer.byteLength(data)
}
}, (res) => {
let b = ''
res.on('data', d => b += d)
res.on('end', () => resolve({ status: res.statusCode, body: b }))
})
req.on('error', reject)
req.write(data)
req.end()
})
}
async function sleep(ms) { return new Promise(r => setTimeout(r, ms)) }
async function run() {
console.log('等待 32 秒让速率限制重置...')
await sleep(32000)
// 1. 英文
console.log('\n--- Test 1: 英文 ---')
const r1 = await post('/text', { text: 'hello world' })
console.log(`Status: ${r1.status}, Body: ${r1.body}`)
// 2. 中文
console.log('\n--- Test 2: 中文 ---')
const r2 = await post('/text', { text: '你好世界' })
console.log(`Status: ${r2.status}, Body: ${r2.body}`)
// 3. XSS
console.log('\n--- Test 3: XSS ---')
const r3 = await post('/text', { text: '<script>alert(1)</script>' })
console.log(`Status: ${r3.status}, Body: ${r3.body}`)
// 4. 特殊字符
console.log('\n--- Test 4: 特殊字符 & < > ---')
const r4 = await post('/text', { text: 'Tom & Jerry <best> "movie"' })
console.log(`Status: ${r4.status}, Body: ${r4.body}`)
// 5. 查询验证
console.log('\n--- Test 5: 查询最近 4 条 ---')
await new Promise((resolve, reject) => {
http.get(`${API}/latest?limit=4`, {
headers: {
'X-API-Id': 'private_clipboard',
'X-API-Key': 'pcb_nuS0qf-W63qcvAPsrG5ePA'
}
}, (res) => {
let b = ''
res.on('data', d => b += d)
res.on('end', () => {
try {
const data = JSON.parse(b)
data.items.forEach((item, i) => {
console.log(` [${i}] text_content: "${item.text_content}"`)
})
} catch { console.log(`Body: ${b}`) }
resolve()
})
}).on('error', reject)
})
}
run().catch(console.error)
@@ -0,0 +1,78 @@
// 快速测试:写入 + 查询
const http = require('http')
const API = 'http://localhost:8976/api/v1/ingest/private_clipboard'
function post(path, body) {
return new Promise((resolve, reject) => {
const data = JSON.stringify(body)
const req = http.request(`${API}${path}`, {
method: 'POST',
headers: {
'X-API-Id': 'private_clipboard',
'X-API-Key': 'pcb_nuS0qf-W63qcvAPsrG5ePA',
'Content-Type': 'application/json; charset=utf-8',
'Content-Length': Buffer.byteLength(data)
}
}, (res) => {
let b = ''
res.on('data', d => b += d)
res.on('end', () => resolve({ status: res.statusCode, body: b }))
})
req.on('error', reject)
req.write(data)
req.end()
})
}
async function run() {
let pass = 0, fail = 0
// 1. 中文
const r1 = await post('/text', { text: '你好世界' })
if (r1.status === 200) { console.log('✅ 中文写入成功'); pass++ }
else { console.log(`❌ 中文写入失败: ${r1.body}`); fail++ }
// 2. XSS
const r2 = await post('/text', { text: '<script>alert(1)</script>' })
if (r2.status === 200) { console.log('✅ XSS 写入成功'); pass++ }
else { console.log(`❌ XSS 写入失败: ${r2.body}`); fail++ }
// 3. 特殊字符
const r3 = await post('/text', { text: 'Tom & Jerry <best> "movie"' })
if (r3.status === 200) { console.log('✅ 特殊字符写入成功'); pass++ }
else { console.log(`❌ 特殊字符写入失败: ${r3.body}`); fail++ }
// 4. 查询验证
await new Promise((resolve) => {
http.get(`${API}/latest?limit=3`, {
headers: { 'X-API-Id': 'private_clipboard', 'X-API-Key': 'pcb_nuS0qf-W63qcvAPsrG5ePA' }
}, (res) => {
let b = ''
res.on('data', d => b += d)
res.on('end', () => {
const data = JSON.parse(b)
console.log('\n--- 查询结果 ---')
data.items.forEach((item, i) => {
console.log(`[${i}] "${item.text_content}"`)
})
// 验证转义
const xss = data.items.find(i => i.text_content.includes('&lt;'))
if (xss) { console.log('\n✅ XSS 已转义'); pass++ }
else { console.log('\n❌ XSS 未转义'); fail++ }
const cn = data.items.find(i => i.text_content === '你好世界')
if (cn) { console.log('✅ 中文存储正确'); pass++ }
else { console.log('❌ 中文存储错误'); fail++ }
const sp = data.items.find(i => i.text_content.includes('&amp;') && i.text_content.includes('&lt;'))
if (sp) { console.log('✅ 特殊字符转义正确'); pass++ }
else { console.log('❌ 特殊字符转义错误'); fail++ }
console.log(`\n=== 结果: ${pass} 通过, ${fail} 失败 ===`)
resolve()
})
})
})
}
run()
+43
View File
@@ -0,0 +1,43 @@
// 测试速率限制
const http = require('http')
const API = 'http://localhost:8976/api/v1/ingest/private_clipboard'
function post(path, body) {
return new Promise((resolve, reject) => {
const data = JSON.stringify(body)
const req = http.request(`${API}${path}`, {
method: 'POST',
headers: {
'X-API-Id': 'private_clipboard',
'X-API-Key': 'pcb_nuS0qf-W63qcvAPsrG5ePA',
'Content-Type': 'application/json; charset=utf-8',
'Content-Length': Buffer.byteLength(data)
}
}, (res) => {
let b = ''
res.on('data', d => b += d)
res.on('end', () => resolve({ status: res.statusCode, body: b }))
})
req.on('error', reject)
req.write(data)
req.end()
})
}
async function run() {
console.log('开始发送 65 次请求...')
let limited = false
for (let i = 1; i <= 65; i++) {
const r = await post('/text', { text: `ratelimit-${i}` })
if (r.status === 429) {
console.log(`第 ${i} 次请求被限制 (429)`)
console.log(`响应: ${r.body}`)
limited = true
break
}
}
if (!limited) console.log('❌ 65 次请求全部通过,速率限制未生效')
else console.log('✅ 速率限制生效')
}
run()