功能: - 激活码管理 (Pro/Auto 两种类型) - 账号池管理 - 设备绑定记录 - 使用日志 - 搜索/筛选功能 - 禁用/启用功能 (支持退款参考) - 全局设置 (换号间隔、额度消耗等) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
204 lines
5.9 KiB
JavaScript
204 lines
5.9 KiB
JavaScript
'use strict';
|
|
|
|
// ============================================
|
|
// CursorPro SQLite Utils - 反混淆版本
|
|
// ============================================
|
|
|
|
const { exec } = require('child_process');
|
|
const { promisify } = require('util');
|
|
const fs = require('fs');
|
|
|
|
const execAsync = promisify(exec);
|
|
|
|
/**
|
|
* 转义 SQL 字符串中的单引号
|
|
*/
|
|
function escapeSqlString(value) {
|
|
if (value === null || value === undefined) {
|
|
return '';
|
|
}
|
|
return String(value).replace(/'/g, "''");
|
|
}
|
|
|
|
/**
|
|
* 执行 SQLite 命令
|
|
* @param {string} dbPath - 数据库文件路径
|
|
* @param {string} sql - SQL 语句
|
|
* @returns {Promise<string>} - 执行结果
|
|
*/
|
|
async function execSqlite(dbPath, sql) {
|
|
const isWindows = process.platform === 'win32';
|
|
|
|
try {
|
|
if (isWindows) {
|
|
// Windows: 直接使用 sqlite3 命令
|
|
const escapedSql = sql.replace(/"/g, '\\"');
|
|
const command = `sqlite3 "${dbPath}" "${escapedSql}"`;
|
|
|
|
const { stdout, stderr } = await execAsync(command, {
|
|
encoding: 'utf-8',
|
|
maxBuffer: 10 * 1024 * 1024 // 10MB
|
|
});
|
|
|
|
if (stderr && !stderr.includes('-- Loading')) {
|
|
console.warn('[SQLite] stderr:', stderr);
|
|
}
|
|
|
|
return stdout.trim();
|
|
} else {
|
|
// macOS/Linux: 使用临时文件避免转义问题
|
|
const os = require('os');
|
|
const pathModule = require('path');
|
|
const tempFile = pathModule.join(
|
|
os.tmpdir(),
|
|
'cursor_sql_' + Date.now() + '.sql'
|
|
);
|
|
|
|
// 写入 SQL 到临时文件
|
|
fs.writeFileSync(tempFile, sql, 'utf-8');
|
|
|
|
try {
|
|
const command = `sqlite3 "${dbPath}" < "${tempFile}"`;
|
|
|
|
const { stdout, stderr } = await execAsync(command, {
|
|
encoding: 'utf-8',
|
|
maxBuffer: 10 * 1024 * 1024,
|
|
shell: '/bin/bash'
|
|
});
|
|
|
|
if (stderr && !stderr.includes('-- Loading')) {
|
|
console.warn('[SQLite] stderr:', stderr);
|
|
}
|
|
|
|
return stdout.trim();
|
|
} finally {
|
|
// 清理临时文件
|
|
try {
|
|
fs.unlinkSync(tempFile);
|
|
} catch (e) {}
|
|
}
|
|
}
|
|
} catch (error) {
|
|
// 检查是否是 sqlite3 不存在的错误
|
|
if (
|
|
error.message === 'ENOENT' ||
|
|
error.message?.includes('sqlite3') ||
|
|
error.message?.includes('not found')
|
|
) {
|
|
throw new Error('sqlite3 命令不存在,请先安装 SQLite3');
|
|
}
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 从 SQLite 数据库读取单个值
|
|
* @param {string} dbPath - 数据库路径
|
|
* @param {string} key - 键名
|
|
* @returns {Promise<string|null>} - 值或 null
|
|
*/
|
|
async function sqliteGet(dbPath, key) {
|
|
if (!fs.existsSync(dbPath)) {
|
|
console.warn('[SQLite] 数据库文件不存在:', dbPath);
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
const sql = `SELECT value FROM ItemTable WHERE key = '${escapeSqlString(key)}';`;
|
|
const result = await execSqlite(dbPath, sql);
|
|
return result || null;
|
|
} catch (error) {
|
|
console.error('[SQLite] 读取失败:', error);
|
|
return null;
|
|
}
|
|
}
|
|
exports.sqliteGet = sqliteGet;
|
|
|
|
/**
|
|
* 向 SQLite 数据库写入单个值
|
|
* @param {string} dbPath - 数据库路径
|
|
* @param {string} key - 键名
|
|
* @param {string} value - 值
|
|
* @returns {Promise<boolean>} - 是否成功
|
|
*/
|
|
async function sqliteSet(dbPath, key, value) {
|
|
if (!fs.existsSync(dbPath)) {
|
|
console.warn('[SQLite] 数据库文件不存在:', dbPath);
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
// 使用 REPLACE INTO 实现 upsert
|
|
const sql = `REPLACE INTO ItemTable (key, value) VALUES ('${escapeSqlString(key)}', '${escapeSqlString(value)}');`;
|
|
await execSqlite(dbPath, sql);
|
|
return true;
|
|
} catch (error) {
|
|
console.error('[SQLite] 写入失败:', error);
|
|
return false;
|
|
}
|
|
}
|
|
exports.sqliteSet = sqliteSet;
|
|
|
|
/**
|
|
* 批量写入 SQLite 数据库
|
|
* @param {string} dbPath - 数据库路径
|
|
* @param {Array<[string, string]>} kvPairs - 键值对数组
|
|
* @returns {Promise<boolean>} - 是否成功
|
|
*/
|
|
async function sqliteSetBatch(dbPath, kvPairs) {
|
|
if (!fs.existsSync(dbPath)) {
|
|
console.warn('[SQLite] 数据库文件不存在:', dbPath);
|
|
return false;
|
|
}
|
|
|
|
if (kvPairs.length === 0) {
|
|
return true;
|
|
}
|
|
|
|
try {
|
|
// 构建批量 SQL 语句
|
|
const statements = kvPairs.map(([key, value]) =>
|
|
`REPLACE INTO ItemTable (key, value) VALUES ('${escapeSqlString(key)}', '${escapeSqlString(value)}');`
|
|
);
|
|
|
|
const sql = 'BEGIN TRANSACTION; ' + statements.join(' ') + ' COMMIT;';
|
|
|
|
await execSqlite(dbPath, sql);
|
|
return true;
|
|
} catch (error) {
|
|
console.error('[SQLite] 批量写入失败:', error);
|
|
return false;
|
|
}
|
|
}
|
|
exports.sqliteSetBatch = sqliteSetBatch;
|
|
|
|
/**
|
|
* 批量读取 SQLite 数据库
|
|
* @param {string} dbPath - 数据库路径
|
|
* @param {string[]} keys - 键名数组
|
|
* @returns {Promise<Map<string, string|null>>} - 键值 Map
|
|
*/
|
|
async function sqliteGetBatch(dbPath, keys) {
|
|
const resultMap = new Map();
|
|
|
|
if (!fs.existsSync(dbPath)) {
|
|
console.warn('[SQLite] 数据库文件不存在:', dbPath);
|
|
keys.forEach(key => resultMap.set(key, null));
|
|
return resultMap;
|
|
}
|
|
|
|
try {
|
|
// 逐个读取 (SQLite CLI 批量读取输出解析较复杂)
|
|
for (const key of keys) {
|
|
const value = await sqliteGet(dbPath, key);
|
|
resultMap.set(key, value);
|
|
}
|
|
return resultMap;
|
|
} catch (error) {
|
|
console.error('[SQLite] 批量读取失败:', error);
|
|
keys.forEach(key => resultMap.set(key, null));
|
|
return resultMap;
|
|
}
|
|
}
|
|
exports.sqliteGetBatch = sqliteGetBatch;
|