feat: 新增多项功能并添加新工具

- 私人剪贴板新增限时直通URL生成功能
- doc_cloud_keeper新增标题复制按钮
- 新增贪蛇出动游戏工具
- 新增OSS中转直链工具
- 添加批量抽帧测试脚本
This commit is contained in:
yangxiangyuan
2026-08-11 10:22:31 +08:00
parent 91148fe064
commit 6dc79a2c12
28 changed files with 3169 additions and 6 deletions
@@ -0,0 +1,132 @@
/**
* 私人剪贴板「限时直通URL」端到端黑盒验证
* 流程:/go 拿 gate → 登录 → 上传测试图片 → 创建直链 → 无鉴权访问 → 模拟过期 → 删除清理验证
* 用法:node dev_test_scripts/debug/debug_private_clipboard_share_link.js
*/
const http = require('http')
const fs = require('fs')
const os = require('os')
const path = require('path')
const BASE = { hostname: 'localhost', port: 8976 }
// 1x1 透明 PNG
const PNG_1PX = Buffer.from('89504e470d0a1a0a0000000d49484452000000010000000108060000001f15c4890000000d4944415478da63fcffff3f0300050201aad0a95e0000000049454e44ae426082', 'hex')
const loadEnv = () => {
const out = {}
try {
const text = fs.readFileSync(path.join(os.homedir(), 'Toolbox_local_creds.env.local'), 'utf8')
for (const line of text.split(/\r?\n/)) {
const t = line.trim()
if (!t || t.startsWith('#')) continue
const eq = t.indexOf('=')
if (eq <= 0) continue
out[t.slice(0, eq).trim()] = t.slice(eq + 1).trim()
}
} catch {}
return out
}
const req = (urlPath, opts = {}) => new Promise((resolve, reject) => {
const headers = Object.assign({}, opts.headers || {})
if (opts.cookie) headers.Cookie = opts.cookie
if (opts.body && !headers['Content-Type']) headers['Content-Type'] = 'application/json'
if (opts.body) headers['Content-Length'] = Buffer.byteLength(opts.body)
const r = http.request(Object.assign({}, BASE, { path: urlPath, method: opts.method || 'GET', headers }), (rp) => {
const chunks = []
rp.on('data', c => chunks.push(c))
rp.on('end', () => resolve({ status: rp.statusCode, headers: rp.headers, body: Buffer.concat(chunks) }))
})
r.on('error', reject)
if (opts.body) r.write(opts.body)
r.end()
})
const mergeCookies = (prev, resp) => {
const set = resp.headers['set-cookie'] || []
const map = {}
String(prev || '').split(/;\s*/).filter(Boolean).forEach(kv => { const i = kv.indexOf('='); if (i > 0) map[kv.slice(0, i)] = kv.slice(i + 1) })
set.forEach(c => { const kv = c.split(';')[0]; const i = kv.indexOf('='); if (i > 0) map[kv.slice(0, i)] = kv.slice(i + 1) })
return Object.keys(map).map(k => `${k}=${map[k]}`).join('; ')
}
let passed = 0
let failed = 0
const check = (name, cond, detail) => {
if (cond) { passed++; console.log(` [PASS] ${name}`) } else { failed++; console.log(` [FAIL] ${name} ${detail || ''}`) }
}
;(async () => {
const env = loadEnv()
const user = env.PRIVATE_CLIPBOARD_USERNAME || ''
const pass = env.PRIVATE_CLIPBOARD_PASSWORD || ''
if (!user || !pass) { console.error('[FAIL] env 缺少 PRIVATE_CLIPBOARD_USERNAME / PASSWORD'); process.exit(1) }
// 1. /go 拿 gate cookie
let ck = ''
let r = await req('/go?systemId=Tools-private_clipboard')
while (r.status >= 300 && r.status < 400 && r.headers.location) {
ck = mergeCookies(ck, r)
r = await req(r.headers.location, { cookie: ck })
}
ck = mergeCookies(ck, r)
check('1 /go gate 链路', /private_clipboard_gate=1/.test(ck), `cookie=${ck}`)
// 2. 登录拿业务 cookie
const login = await req('/api/private_clipboard/auth/login', { method: 'POST', cookie: ck, body: JSON.stringify({ username: user, password: pass }) })
ck = mergeCookies(ck, login)
const loginJson = JSON.parse(login.body.toString('utf8') || '{}')
check('2 业务登录', login.status === 200 && loginJson.ok === true, `status=${login.status}`)
// 3. 上传测试图片
const up = await req('/api/private_clipboard/upload?filename=share_test.png&mime=image/png', { method: 'POST', cookie: ck, body: PNG_1PX, headers: { 'Content-Type': 'image/png' } })
const upJson = JSON.parse(up.body.toString('utf8') || '{}')
check('3 上传测试图片', up.status === 200 && upJson.ok === true && upJson.id, `status=${up.status} body=${up.body.toString().slice(0, 100)}`)
const itemId = upJson.id
// 4. 创建限时直链
const sh = await req('/api/private_clipboard/share/create', { method: 'POST', cookie: ck, body: JSON.stringify({ id: itemId }) })
const shJson = JSON.parse(sh.body.toString('utf8') || '{}')
check('4 创建限时直链', sh.status === 200 && shJson.ok === true && /^https:\/\/traesite\.umersoft\.com:8975\/share\/clip\/[a-f0-9]{32}$/.test(String(shJson.url || '')), `body=${sh.body.toString().slice(0, 160)}`)
const token = String(shJson.url || '').split('/').pop()
console.log(` 直链过期时间(北京时间): ${shJson.expires_at_text}`)
// 5. 无鉴权访问(模拟第三方浏览器,不带任何 cookie)
const pub = await req(`/share/clip/${token}`)
check('5 无鉴权访问直链', pub.status === 200 && String(pub.headers['content-type'] || '').includes('image/png'), `status=${pub.status} type=${pub.headers['content-type']}`)
// 5b. 未登录调用 share/create 必须被拒
const shNoAuth = await req('/api/private_clipboard/share/create', { method: 'POST', body: JSON.stringify({ id: itemId }) })
check('5b 未授权创建直链被拒', shNoAuth.status === 401 || shNoAuth.status === 403, `status=${shNoAuth.status}`)
// 6. 模拟过期:把 expires_at 改成过去
const Database = require(path.join(process.cwd(), 'node_modules', 'better-sqlite3'))
const db = new Database(path.join(process.cwd(), 'data', 'private_clipboard.db'))
db.prepare('UPDATE clipboard_share_links SET expires_at = ? WHERE token = ?').run(Date.now() - 1000, token)
db.close()
const expired = await req(`/share/clip/${token}`)
const expiredHtml = expired.body.toString('utf8')
check('6 过期链接返回 410 + 提示文案', expired.status === 410 && expiredHtml.includes('你的链接期限已经大于三天') && expiredHtml.includes('无法访问'), `status=${expired.status}`)
// 7. 重新生成(覆盖旧 token),再删除条目,验证 share 链接被清理
const sh2 = await req('/api/private_clipboard/share/create', { method: 'POST', cookie: ck, body: JSON.stringify({ id: itemId }) })
const sh2Json = JSON.parse(sh2.body.toString('utf8') || '{}')
const token2 = String(sh2Json.url || '').split('/').pop()
const del = await req(`/api/private_clipboard/${itemId}`, { method: 'DELETE', cookie: ck })
const delJson = JSON.parse(del.body.toString('utf8') || '{}')
check('7 删除条目', del.status === 200 && delJson.ok === true, `status=${del.status}`)
const afterDel = await req(`/share/clip/${token2}`)
check('7b 删除后直链失效', afterDel.status === 410, `status=${afterDel.status}`)
const db2 = new Database(path.join(process.cwd(), 'data', 'private_clipboard.db'))
const left = db2.prepare('SELECT COUNT(1) AS c FROM clipboard_share_links WHERE item_id = ?').get(itemId)
db2.close()
check('7c share_links 表已清理', Number(left && left.c) === 0, `remaining=${left && left.c}`)
// 8. 伪造 token
const fake = await req('/share/clip/00000000000000000000000000000000')
check('8 伪造 token 返回失效页', fake.status === 410, `status=${fake.status}`)
console.log(`\n结果: ${passed} 通过 / ${failed} 失败`)
process.exit(failed ? 1 : 0)
})().catch(e => { console.error('[ERR]', e); process.exit(1) })