| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- #!/usr/bin/env node
- /**
- * 测试账号授权脚本(仅内部使用,不部署到客户侧)
- *
- * 通过 Parse Master Key:
- * 1. 确认目标 _User 存在
- * 2. 检查/创建 APIGAuth(user=<userId>, api=<apigId>) 并充值
- * 3. 通过 POST /sessions 为该用户创建 session token
- * 4. 写入 ~/.openclaw/voc-credentials.json
- *
- * 用法:
- * node scripts/tools/_grant-test-account.js <userObjectId> [apigId] [count]
- * node scripts/tools/_grant-test-account.js Z0PxzHjYzB Vo3ROWEvDy 500
- *
- * 默认: apigId=Vo3ROWEvDy (voc-social), count=500
- */
- const fs = require('fs');
- const os = require('os');
- const path = require('path');
- const APP_ID = 'ncloudmaster';
- const MASTER_KEY = 'SnkK12*&sunq2#@20!';
- const BASE = 'https://server.fmode.cn/parse';
- const userId = process.argv[2];
- const apigId = process.argv[3] || 'Vo3ROWEvDy';
- const count = parseInt(process.argv[4] || '500', 10);
- if (!userId) {
- console.log('用法: node _grant-test-account.js <userObjectId> [apigId=Vo3ROWEvDy] [count=500]');
- process.exit(1);
- }
- const masterHeaders = {
- 'Content-Type': 'application/json',
- 'X-Parse-Application-Id': APP_ID,
- 'X-Parse-Master-Key': MASTER_KEY,
- };
- async function main() {
- console.log(`== 测试账号授权 ==`);
- console.log(` user: ${userId}`);
- console.log(` apig: ${apigId}`);
- console.log(` count: ${count}`);
- // 1) 确认 _User 存在
- console.log(`\n[1/4] 查询 _User/${userId} ...`);
- const r1 = await fetch(`${BASE}/users/${userId}`, { headers: masterHeaders });
- const user = await r1.json();
- if (!r1.ok || !user.objectId) {
- console.error('❌ 找不到该用户:', JSON.stringify(user));
- process.exit(2);
- }
- console.log(` ✓ 用户: ${user.username} (${user.objectId}) created=${user.createdAt}`);
- // 2) 查 APIGAuth
- console.log(`\n[2/4] 查 APIGAuth(user=${userId}, api=${apigId}) ...`);
- const where = encodeURIComponent(JSON.stringify({
- user: { __type: 'Pointer', className: '_User', objectId: userId },
- api: { __type: 'Pointer', className: 'APIG', objectId: apigId },
- }));
- const r2 = await fetch(`${BASE}/classes/APIGAuth?where=${where}&limit=1`, { headers: masterHeaders });
- const d2 = await r2.json();
- let authId;
- if (d2.results && d2.results.length > 0) {
- const a = d2.results[0];
- authId = a.objectId;
- console.log(` 找到: ${authId} · count=${a.count ?? 0} · used=${a.used ?? 0}`);
- // 更新 count
- console.log(` -> 更新 count=${count}, used=0`);
- const r2b = await fetch(`${BASE}/classes/APIGAuth/${authId}`, {
- method: 'PUT',
- headers: masterHeaders,
- body: JSON.stringify({ count, used: 0 }),
- });
- const d2b = await r2b.json();
- if (!r2b.ok) {
- console.error('❌ 更新失败:', JSON.stringify(d2b));
- } else {
- console.log(` ✓ APIGAuth 更新: ${JSON.stringify(d2b)}`);
- }
- } else {
- console.log(` 未找到 -> 创建 APIGAuth(count=${count})`);
- const r2c = await fetch(`${BASE}/classes/APIGAuth`, {
- method: 'POST',
- headers: masterHeaders,
- body: JSON.stringify({
- user: { __type: 'Pointer', className: '_User', objectId: userId },
- api: { __type: 'Pointer', className: 'APIG', objectId: apigId },
- count,
- used: 0,
- }),
- });
- const d2c = await r2c.json();
- if (!r2c.ok || !d2c.objectId) {
- console.error('❌ 创建失败:', JSON.stringify(d2c));
- process.exit(3);
- }
- authId = d2c.objectId;
- console.log(` ✓ 新 APIGAuth: ${authId}`);
- }
- // 3) 创建 Session (Parse restricted session)
- console.log(`\n[3/4] POST /sessions 创建 session ...`);
- // Parse cloud sessions require user pointer and authData; master key bypass available
- const r3 = await fetch(`${BASE}/sessions`, {
- method: 'POST',
- headers: masterHeaders,
- body: JSON.stringify({
- user: { __type: 'Pointer', className: '_User', objectId: userId },
- createdWith: { action: 'login', authProvider: 'master-key-script' },
- restricted: false,
- expiresAt: { __type: 'Date', iso: new Date(Date.now() + 30 * 24 * 3600 * 1000).toISOString() },
- installationId: 'openclaw-admin-grant',
- }),
- });
- const d3 = await r3.json();
- console.log(` status=${r3.status}`);
- console.log(` resp: ${JSON.stringify(d3).slice(0, 300)}`);
- let sessionToken = d3.sessionToken;
- if (!sessionToken) {
- // 尝试方法 2: POST /classes/_Session
- console.log(` /sessions 失败, 尝试 /classes/_Session ...`);
- const r3b = await fetch(`${BASE}/classes/_Session`, {
- method: 'POST',
- headers: masterHeaders,
- body: JSON.stringify({
- user: { __type: 'Pointer', className: '_User', objectId: userId },
- sessionToken: 'r:' + randomHex(32),
- createdWith: { action: 'login', authProvider: 'master-key-script' },
- restricted: false,
- expiresAt: { __type: 'Date', iso: new Date(Date.now() + 30 * 24 * 3600 * 1000).toISOString() },
- installationId: 'openclaw-admin-grant',
- }),
- });
- const d3b = await r3b.json();
- console.log(` status=${r3b.status} resp=${JSON.stringify(d3b).slice(0, 300)}`);
- // The sessionToken was sent in request; query back to confirm it was stored
- if (r3b.ok && d3b.objectId) {
- const r3c = await fetch(`${BASE}/classes/_Session/${d3b.objectId}`, { headers: masterHeaders });
- const d3c = await r3c.json();
- sessionToken = d3c.sessionToken;
- console.log(` query-back token: ${sessionToken ? sessionToken.slice(0, 10) + '...' : 'none'}`);
- }
- }
- if (!sessionToken) {
- console.error('\n❌ 无法拿到 sessionToken');
- console.log('建议手工在控制台为用户创建 session,或让用户自行登录获取');
- process.exit(4);
- }
- // 4) 写入 voc-credentials.json
- const credPath = path.join(os.homedir(), '.openclaw', 'voc-credentials.json');
- fs.mkdirSync(path.dirname(credPath), { recursive: true });
- let cur = {};
- if (fs.existsSync(credPath)) {
- try { cur = JSON.parse(fs.readFileSync(credPath, 'utf-8')); } catch (e) {}
- }
- cur.vocToken = sessionToken;
- fs.writeFileSync(credPath, JSON.stringify(cur, null, 2) + '\n', 'utf-8');
- console.log(`\n[4/4] ✓ sessionToken 写入 ${credPath}`);
- console.log(` token: ${sessionToken.slice(0, 10)}...${sessionToken.slice(-6)}`);
- // 5) 最终校验:用该 token 调 /users/me
- console.log(`\n== 校验 ==`);
- const verify = await fetch(`${BASE}/users/me`, {
- headers: {
- 'X-Parse-Application-Id': APP_ID,
- 'X-Parse-Session-Token': sessionToken,
- },
- });
- const vdata = await verify.json();
- console.log(` /users/me status=${verify.status}`);
- console.log(` returned user: ${vdata.username} (${vdata.objectId})`);
- console.log(`\n🎉 测试账号已就绪: ${user.username}`);
- console.log(` - APIG ${apigId} 余额: ${count} 次`);
- console.log(` - Token 已写入 ${credPath}`);
- console.log(` - 可执行 node scripts/tools/collect-jiangzhong-liver-deep.js --batch=1 --force 重跑抖音`);
- }
- function randomHex(n) {
- let s = '';
- const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
- for (let i = 0; i < n; i++) s += chars[Math.floor(Math.random() * chars.length)];
- return s;
- }
- main().catch((e) => { console.error('❌', e); process.exit(9); });
|