Files
server-configs/test_delete.js
2026-02-13 22:24:27 +08:00

133 lines
3.6 KiB
JavaScript

#!/usr/bin/env node
const fs = require('fs');
const https = require('https');
const CONFIG_PATH = process.env.HOME + '/.config/vaultwarden/config.json';
let config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
const VAULT_URL = config.url;
const CLIENT_ID = config.client_id;
const CLIENT_SECRET = config.client_secret;
let token = null;
let tokenExpiry = null;
const deviceId = 'openclaw-cli-' + require('crypto').randomUUID();
async function getToken() {
if (token && tokenExpiry && Date.now() < tokenExpiry - 300000) {
return token;
}
const params = new URLSearchParams({
grant_type: 'client_credentials',
scope: 'api',
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
device_identifier: deviceId,
device_type: '14',
device_name: 'OpenClaw CLI'
});
const response = await new Promise((resolve, reject) => {
const url = new URL(VAULT_URL + '/identity/connect/token');
const options = {
hostname: url.hostname, port: url.port || 443,
path: url.pathname + url.search, method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
timeout: 30000
};
const req = https.request(options, (res) => {
let body = '';
res.on('data', chunk => body += chunk);
res.on('end', () => {
try {
const json = JSON.parse(body);
resolve(json);
} catch (e) { reject(e); }
});
});
req.on('error', reject);
req.write(params.toString());
req.end();
});
if (!response.access_token) {
throw new Error('认证失败');
}
token = response.access_token;
tokenExpiry = Date.now() + ((response.expires_in || 7200) - 300) * 1000;
return token;
}
async function getItemByName(name) {
const t = await getToken();
const response = await new Promise((resolve, reject) => {
const url = new URL(VAULT_URL + '/api/ciphers');
const options = {
hostname: url.hostname, port: url.port || 443,
path: url.pathname + url.search, method: 'GET',
headers: { 'Authorization': `Bearer ${t}` },
timeout: 30000
};
const req = https.request(options, (res) => {
let body = '';
res.on('data', chunk => body += chunk);
res.on('end', () => {
try {
const json = JSON.parse(body);
resolve(json);
} catch (e) { reject(e); }
});
});
req.on('error', reject);
req.end();
});
return (response.data || []).find(item => item.name?.toLowerCase() === name.toLowerCase());
}
async function deleteItem(id) {
const t = await getToken();
return new Promise((resolve, reject) => {
const url = new URL(VAULT_URL + `/api/ciphers/${id}?permanent=true`);
const options = {
hostname: url.hostname, port: url.port || 443,
path: url.pathname + url.search, method: 'DELETE',
headers: { 'Authorization': `Bearer ${t}` },
timeout: 30000
};
const req = https.request(options, (res) => {
let body = '';
res.on('data', chunk => body += chunk);
res.on('end', () => {
if (res.statusCode >= 200 && res.statusCode < 300) resolve();
else reject(new Error(`HTTP ${res.statusCode}: ${body}`));
});
});
req.on('error', reject);
req.end();
});
}
async function main() {
try {
const item = await getItemByName('Test Account');
if (!item) {
console.log('❌ 未找到: Test Account');
return;
}
console.log(`🗑️ 删除 "${item.name}"...`);
await deleteItem(item.id);
console.log(`✅ 已删除: ${item.name}\n`);
} catch (error) {
console.error('\n❌ 错误:', error.message);
process.exit(1);
}
}
main();