const { execSync } = require('child_process');
const axios = require('axios');
class SqlToolClient {
constructor(baseUrl = 'http://localhost:8080', apiKey = null) {
this.baseUrl = baseUrl.replace(/\/$/, '');
this.client = axios.create({
baseURL: this.baseUrl,
timeout: 60000,
headers: { 'Content-Type': 'application/json' }
});
if (apiKey) {
this.client.defaults.headers.common['Authorization'] = `Bearer ${apiKey}`;
}
}
async _post(path, data) {
const response = await this.client.post(path, data);
return response.data;
}
async _get(path) {
const response = await this.client.get(path);
return response.data;
}
async healthCheck() {
return this._get('/api/health');
}
async transfer({
source,
target,
sourceType = 'mysql',
targetType = 'postgresql',
tables = '',
batchSize = 1000,
verifyData = true,
skipErrors = true
}) {
return this._post('/api/transfer', {
source,
target,
source_type: sourceType,
target_type: targetType,
tables,
batch_size: batchSize,
verify_data: verifyData,
skip_errors: skipErrors
});
}
async backup({
source,
dbType = 'mysql',
output = '/tmp/backup.sql',
backupType = 'full',
compress = true,
includeProcedures = true,
includeFunctions = true,
includeTriggers = true,
parallelTables = 4
}) {
return this._post('/api/backup', {
source,
db_type: dbType,
output,
backup_type: backupType,
compress,
include_procedures: includeProcedures,
include_functions: includeFunctions,
include_triggers: includeTriggers,
parallel_tables: parallelTables
});
}
async compareData({
source,
target,
table,
sourceType = 'mysql',
targetType = 'mysql',
primaryKey = 'id',
ignoreFields = '',
compareMode = 'full'
}) {
return this._post('/api/compare', {
source,
target,
source_type: sourceType,
target_type: targetType,
table,
primary_key: primaryKey,
ignore_fields: ignoreFields,
compare_mode: compareMode
});
}
async createShard({
source,
table,
strategy = 'row_count',
threshold = '1000000',
prefix = 'shard'
}) {
return this._post('/api/shard/create', {
source,
table,
strategy,
threshold,
prefix
});
}
async detectSlowQuery({
source,
dbType = 'mysql',
thresholdMs = 1000,
limit = 10
}) {
return this._post('/api/detect-slow', {
source,
db_type: dbType,
threshold_ms: thresholdMs,
limit
});
}
async spanningQuery({
source,
table,
condition = '1=1',
orderBy = '',
orderDir = 'ASC',
limit = 100,
offset = 0
}) {
return this._post('/api/spanning-query', {
source,
table,
condition,
order_by: orderBy,
order_dir: orderDir,
limit,
offset
});
}
async insertLog({
source,
table = 'app_logs',
level = 'INFO',
message = '',
sourceName = ''
}) {
return this._post('/api/log/insert', {
source,
table,
level,
message,
source_name: sourceName
});
}
async queryLogs({
source,
table = 'app_logs',
levels = '',
keyword = '',
startTime = 0,
endTime = 0,
limit = 100
}) {
const result = await this._post('/api/log/query', {
source,
table,
levels,
keyword,
start_time: startTime,
end_time: endTime,
limit
});
return result.rows || [];
}
async detectInjection(inputText) {
return this._post('/api/security/detect-injection', {
input: inputText
});
}
async buildSafeSql({
table,
field,
operator = '=',
value = ''
}) {
return this._post('/api/security/build-safe-sql', {
table,
field,
operator,
value
});
}
}
class SqlToolCLI {
constructor(binaryPath = 'sqltool') {
this.binaryPath = binaryPath;
}
run(...args) {
try {
const result = execSync(`${this.binaryPath} ${args.join(' ')}`, {
encoding: 'utf-8',
timeout: 60000
});
return result;
} catch (error) {
return `错误: ${error.message}`;
}
}
transfer(source, target, sourceType, targetType, tables = '', batchSize = 1000) {
const args = [
'transfer',
'-s', source,
'-t', target,
'-S', sourceType,
'-T', targetType,
'-B', batchSize
];
if (tables) args.push('--tables', tables);
return this.run(...args);
}
backup(source, output, dbType = 'mysql', backupType = 'full', compress = true) {
const args = ['backup', '-s', source, '-T', dbType, '-o', output, '-t', backupType];
if (compress) args.push('-c');
return this.run(...args);
}
compareData(source, target, table, primaryKey = 'id', sourceType = 'mysql', targetType = 'mysql') {
return this.run(
'compare-data',
'-s', source,
'-t', target,
'-S', sourceType,
'-T', targetType,
'--table', table,
'--primary-key', primaryKey
);
}
createShard(source, table, strategy = 'row_count', threshold = '1000000', prefix = 'shard') {
return this.run(
'create-shard',
'-s', source,
'--table', table,
'--strategy', strategy,
'--threshold', threshold,
'--prefix', prefix
);
}
detectSlowQuery(source, dbType = 'mysql', thresholdMs = 1000) {
return this.run(
'detect-slow-query',
'-s', source,
'-T', dbType,
'--threshold-ms', thresholdMs
);
}
spanningQuery(source, table, condition = '1=1', orderBy = '', limit = 100, offset = 0) {
const args = [
'spanning-query',
'-s', source,
'--table', table,
'--condition', condition,
'-L', limit,
'--offset', offset
];
if (orderBy) args.push('--order-by', orderBy);
return this.run(...args);
}
insertLog(source, message, table = 'app_logs', level = 'INFO', sourceName = '') {
const args = [
'insert-log',
'-s', source,
'--table', table,
'--level', level,
'--message', message
];
if (sourceName) args.push('--source-name', sourceName);
return this.run(...args);
}
queryLogs(source, table = 'app_logs', levels = '', keyword = '', limit = 100) {
const args = ['query-logs', '-s', source, '--table', table, '-L', limit];
if (levels) args.push('--levels', levels);
if (keyword) args.push('--keyword', keyword);
return this.run(...args);
}
detectInjection(inputText) {
return this.run('detect-sql-injection', '-i', inputText);
}
buildSafeSql(table, field, operator = '=', value = '') {
return this.run(
'build-safe-sql',
'--table', table,
'--field', field,
'--operator', operator,
'--value', value
);
}
}
async function main() {
const args = process.argv.slice(2);
const useCLI = args.includes('--cli');
const binaryPath = args.find(arg => arg.startsWith('--binary='))?.split('=')[1]
|| '/Users/Zhuanz/Desktop/website/composer/sqlmap/target/release/sqltool';
console.log(`
╔════════════════════════════════════════════════════════════╗
║ SQLTool Node.js 完整调用示例 v0.4.1 ║
╚════════════════════════════════════════════════════════════╝
`);
if (useCLI) {
console.log('模式: CLI');
console.log(`二进制: ${binaryPath}\n`);
const cli = new SqlToolCLI(binaryPath);
console.log('1. SQL注入检测...');
console.log('='.repeat(60));
console.log(cli.detectInjection("' OR '1'='1"));
console.log('\n2. 安全SQL构建...');
console.log('='.repeat(60));
console.log(cli.buildSafeSql('users', 'name', '=', "test'; DROP TABLE"));
console.log('\n3. 数据迁移...');
console.log('='.repeat(60));
console.log(cli.transfer(
'mysql://root:pass@localhost:3306/source',
'postgresql://postgres:pass@localhost:5432/target',
'mysql', 'postgresql', 'users,orders', 5000
));
console.log('\n4. 数据库备份...');
console.log('='.repeat(60));
console.log(cli.backup(
'mysql://root:pass@localhost:3306/mydb',
'/tmp/backup.sql', 'mysql', 'full', true
));
console.log('\n5. 数据对比...');
console.log('='.repeat(60));
console.log(cli.compareData(
'mysql://root@localhost/db1',
'mysql://root@localhost/db2',
'users', 'id'
));
} else {
console.log('模式: HTTP API');
console.log('URL: http://localhost:8080\n');
const client = new SqlToolClient('http://localhost:8080');
try {
console.log('0. 健康检查...');
console.log('='.repeat(60));
console.log(JSON.stringify(await client.healthCheck(), null, 2));
console.log('\n1. SQL注入检测...');
console.log('='.repeat(60));
const injResult = await client.detectInjection("' OR '1'='1");
console.log(JSON.stringify(injResult, null, 2));
if (injResult.risk_level === 'High' || injResult.risk_level === 'Critical') {
console.log('⚠️ 警告: 检测到高风险SQL注入攻击!');
}
console.log('\n2. 安全SQL构建...');
console.log('='.repeat(60));
const sqlResult = await client.buildSafeSql({
table: 'users',
field: 'email',
operator: 'LIKE',
value: '%@example.com'
});
console.log(JSON.stringify(sqlResult, null, 2));
console.log('\n3. 数据迁移 (需要真实数据库连接)...');
console.log('='.repeat(60));
const transferResult = await client.transfer({
source: 'mysql://root:password@localhost:3306/source_db',
target: 'postgresql://postgres:password@localhost:5432/target_db',
sourceType: 'mysql',
targetType: 'postgresql',
tables: 'users,orders,products',
batchSize: 5000,
verifyData: true
});
console.log(JSON.stringify(transferResult, null, 2));
console.log('\n4. 数据库备份 (需要真实数据库连接)...');
console.log('='.repeat(60));
const backupResult = await client.backup({
source: 'mysql://root:password@localhost:3306/mydb',
dbType: 'mysql',
output: '/tmp/backup_20240101.sql',
backupType: 'full',
compress: true
});
console.log(JSON.stringify(backupResult, null, 2));
console.log('\n5. 数据对比 (需要真实数据库连接)...');
console.log('='.repeat(60));
const compareResult = await client.compareData({
source: 'mysql://root:password@localhost:3306/db1',
target: 'mysql://root:password@localhost:3306/db2',
table: 'users',
primaryKey: 'id',
ignoreFields: 'updated_at'
});
console.log(JSON.stringify(compareResult, null, 2));
console.log('\n6. 分库分表 (需要真实数据库连接)...');
console.log('='.repeat(60));
const shardResult = await client.createShard({
source: 'mysql://root:password@localhost:3306/mydb',
table: 'orders',
strategy: 'row_count',
threshold: '1000000',
prefix: 'orders_shard'
});
console.log(JSON.stringify(shardResult, null, 2));
console.log('\n7. 慢查询检测 (需要真实数据库连接)...');
console.log('='.repeat(60));
const slowResult = await client.detectSlowQuery({
source: 'mysql://root:password@localhost:3306/mydb',
thresholdMs: 1000,
limit: 10
});
console.log(JSON.stringify(slowResult, null, 2));
console.log('\n8. 跨分片查询 (需要真实数据库连接)...');
console.log('='.repeat(60));
const spanResult = await client.spanningQuery({
source: 'mysql://root:password@localhost:3306/mydb',
table: 'orders',
condition: "status='pending'",
orderBy: 'created_at',
orderDir: 'DESC',
limit: 100
});
console.log(JSON.stringify(spanResult, null, 2));
console.log('\n9. 插入日志 (需要真实数据库连接)...');
console.log('='.repeat(60));
const logInsertResult = await client.insertLog({
source: 'mysql://root:password@localhost:3306/mydb',
table: 'app_logs',
level: 'INFO',
message: '用户登录成功',
sourceName: 'auth-service'
});
console.log(JSON.stringify(logInsertResult, null, 2));
console.log('\n10. 查询日志 (需要真实数据库连接)...');
console.log('='.repeat(60));
const logQueryResult = await client.queryLogs({
source: 'mysql://root:password@localhost:3306/mydb',
table: 'app_logs',
levels: 'ERROR,WARN',
keyword: 'login',
limit: 50
});
console.log(JSON.stringify(logQueryResult, null, 2));
} catch (error) {
console.error(`\n错误: ${error.message}`);
if (error.code === 'ECONNREFUSED') {
console.log('\n请先启动 sqltool server:');
console.log(' sqltool server -p 8080 -s mysql://localhost/mydb');
}
process.exit(1);
}
}
console.log('\n' + '='.repeat(60));
console.log('示例执行完成!');
console.log('='.repeat(60));
}
main();