fix: APIKey列表费用及Token显示不准确的问题,目前显示总数

feat: 增加APIKey过期设置,以及到期续期的能力
This commit is contained in:
KevinLiao
2025-07-25 09:34:40 +08:00
parent 561f5ffc7f
commit f614d54ab5
19 changed files with 3908 additions and 90 deletions

View File

@@ -0,0 +1,181 @@
#!/usr/bin/env node
/**
* 测试导入加密处理
* 验证增强版数据传输工具是否正确处理加密和未加密的导出数据
*/
const fs = require('fs').promises;
const path = require('path');
const crypto = require('crypto');
const config = require('../config/config');
const logger = require('../src/utils/logger');
// 模拟加密函数
function encryptData(data, salt = 'salt') {
if (!data || !config.security.encryptionKey) return data;
const key = crypto.scryptSync(config.security.encryptionKey, salt, 32);
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
let encrypted = cipher.update(data, 'utf8', 'hex');
encrypted += cipher.final('hex');
return iv.toString('hex') + ':' + encrypted;
}
// 模拟解密函数
function decryptData(encryptedData, salt = 'salt') {
if (!encryptedData || !config.security.encryptionKey) return encryptedData;
try {
if (encryptedData.includes(':')) {
const parts = encryptedData.split(':');
const key = crypto.scryptSync(config.security.encryptionKey, salt, 32);
const iv = Buffer.from(parts[0], 'hex');
const encrypted = parts[1];
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
return encryptedData;
} catch (error) {
logger.warn(`⚠️ Failed to decrypt data: ${error.message}`);
return encryptedData;
}
}
async function testImportHandling() {
console.log('🧪 测试导入加密处理\n');
// 测试数据
const testClaudeAccount = {
id: 'test-claude-123',
name: 'Test Claude Account',
email: 'test@example.com',
password: 'testPassword123',
accessToken: 'test-access-token',
refreshToken: 'test-refresh-token',
claudeAiOauth: {
access_token: 'oauth-access-token',
refresh_token: 'oauth-refresh-token',
scopes: ['read', 'write']
}
};
const testGeminiAccount = {
id: 'test-gemini-456',
name: 'Test Gemini Account',
geminiOauth: {
access_token: 'gemini-access-token',
refresh_token: 'gemini-refresh-token'
},
accessToken: 'gemini-access-token',
refreshToken: 'gemini-refresh-token'
};
// 1. 创建解密的导出文件(模拟 --decrypt=true
const decryptedExport = {
metadata: {
version: '2.0',
exportDate: new Date().toISOString(),
sanitized: false,
decrypted: true, // 标记为已解密
types: ['all']
},
data: {
claudeAccounts: [testClaudeAccount],
geminiAccounts: [testGeminiAccount]
}
};
// 2. 创建加密的导出文件(模拟 --decrypt=false
const encryptedClaudeAccount = { ...testClaudeAccount };
encryptedClaudeAccount.email = encryptData(encryptedClaudeAccount.email);
encryptedClaudeAccount.password = encryptData(encryptedClaudeAccount.password);
encryptedClaudeAccount.accessToken = encryptData(encryptedClaudeAccount.accessToken);
encryptedClaudeAccount.refreshToken = encryptData(encryptedClaudeAccount.refreshToken);
encryptedClaudeAccount.claudeAiOauth = encryptData(JSON.stringify(encryptedClaudeAccount.claudeAiOauth));
const encryptedGeminiAccount = { ...testGeminiAccount };
encryptedGeminiAccount.geminiOauth = encryptData(JSON.stringify(encryptedGeminiAccount.geminiOauth), 'gemini-account-salt');
encryptedGeminiAccount.accessToken = encryptData(encryptedGeminiAccount.accessToken, 'gemini-account-salt');
encryptedGeminiAccount.refreshToken = encryptData(encryptedGeminiAccount.refreshToken, 'gemini-account-salt');
const encryptedExport = {
metadata: {
version: '2.0',
exportDate: new Date().toISOString(),
sanitized: false,
decrypted: false, // 标记为未解密(加密状态)
types: ['all']
},
data: {
claudeAccounts: [encryptedClaudeAccount],
geminiAccounts: [encryptedGeminiAccount]
}
};
// 写入测试文件
const testDir = path.join(__dirname, '../data/test-imports');
await fs.mkdir(testDir, { recursive: true });
await fs.writeFile(
path.join(testDir, 'decrypted-export.json'),
JSON.stringify(decryptedExport, null, 2)
);
await fs.writeFile(
path.join(testDir, 'encrypted-export.json'),
JSON.stringify(encryptedExport, null, 2)
);
console.log('✅ 测试文件已创建:');
console.log(' - data/test-imports/decrypted-export.json (解密的数据)');
console.log(' - data/test-imports/encrypted-export.json (加密的数据)\n');
console.log('📋 测试场景:\n');
console.log('1. 导入解密的数据decrypted=true');
console.log(' - 导入时应该重新加密敏感字段');
console.log(' - 命令: npm run data:import:enhanced -- --input=data/test-imports/decrypted-export.json\n');
console.log('2. 导入加密的数据decrypted=false');
console.log(' - 导入时应该保持原样(已经是加密的)');
console.log(' - 命令: npm run data:import:enhanced -- --input=data/test-imports/encrypted-export.json\n');
console.log('3. 验证导入后的数据:');
console.log(' - 使用 CLI 查看账户状态');
console.log(' - 命令: npm run cli accounts list\n');
// 显示示例数据对比
console.log('📊 数据对比示例:\n');
console.log('原始数据(解密状态):');
console.log(` email: "${testClaudeAccount.email}"`);
console.log(` password: "${testClaudeAccount.password}"`);
console.log(` accessToken: "${testClaudeAccount.accessToken}"\n`);
console.log('加密后的数据:');
console.log(` email: "${encryptedClaudeAccount.email.substring(0, 50)}..."`);
console.log(` password: "${encryptedClaudeAccount.password.substring(0, 50)}..."`);
console.log(` accessToken: "${encryptedClaudeAccount.accessToken.substring(0, 50)}..."\n`);
// 验证加密/解密
console.log('🔐 验证加密/解密功能:');
const testString = 'test-data-123';
const encrypted = encryptData(testString);
const decrypted = decryptData(encrypted);
console.log(` 原始: "${testString}"`);
console.log(` 加密: "${encrypted.substring(0, 50)}..."`);
console.log(` 解密: "${decrypted}"`);
console.log(` 验证: ${testString === decrypted ? '✅ 成功' : '❌ 失败'}\n`);
}
// 运行测试
testImportHandling().catch(error => {
console.error('❌ 测试失败:', error);
process.exit(1);
});