更新开发文档和技能索引,优化角色驱动的技能使用规范。调整了各角色的主技能和辅助技能的描述,确保开发者在不同目录下遵循相应的开发风格和流程。增加了变更关联检查的要求,确保代码变更后进行必要的检查以防漏改。
This commit is contained in:
106
.cursor/scripts/db-exec/run.js
Normal file
106
.cursor/scripts/db-exec/run.js
Normal file
@@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Soul 创业派对 - MySQL 直接执行脚本
|
||||
* 当 MCP MySQL 因端口非 3306 无法连接时,用此脚本执行 SQL。
|
||||
*
|
||||
* 用法:
|
||||
* node run.js "SELECT 1"
|
||||
* node run.js -f migration.sql
|
||||
* node run.js --dsn "user:pass@tcp(host:port)/db" "SELECT 1"
|
||||
*
|
||||
* 凭证来源(优先级):
|
||||
* 1. 环境变量 DB_DSN
|
||||
* 2. 参数 --dsn
|
||||
* 3. 读取 soul-api/.env 中的 DB_DSN
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
function parseDsn(dsn) {
|
||||
// user:password@tcp(host:port)/database?params
|
||||
const match = dsn.match(/^([^:]+):([^@]+)@tcp\(([^:]+):(\d+)\)\/([^?]+)/);
|
||||
if (!match) throw new Error('DB_DSN 格式应为: user:password@tcp(host:port)/database');
|
||||
return {
|
||||
user: match[1],
|
||||
password: match[2],
|
||||
host: match[3],
|
||||
port: parseInt(match[4], 10),
|
||||
database: match[5],
|
||||
};
|
||||
}
|
||||
|
||||
function loadDsn() {
|
||||
if (process.env.DB_DSN) return process.env.DB_DSN;
|
||||
const envPath = path.resolve(__dirname, '../../../soul-api/.env');
|
||||
if (fs.existsSync(envPath)) {
|
||||
const content = fs.readFileSync(envPath, 'utf8');
|
||||
const line = content.split('\n').find((l) => l.trim().startsWith('DB_DSN='));
|
||||
if (line) {
|
||||
const val = line.split('=')[1]?.trim();
|
||||
if (val) return val.replace(/^["']|["']$/g, '');
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
let dsn = process.argv.find((a) => a.startsWith('--dsn='))?.split('=')[1];
|
||||
if (!dsn) dsn = loadDsn();
|
||||
if (!dsn) {
|
||||
console.error('未找到 DB_DSN。请设置环境变量 DB_DSN 或确保 soul-api/.env 中有 DB_DSN');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let sql = '';
|
||||
const fileArg = process.argv.indexOf('-f');
|
||||
if (fileArg !== -1 && process.argv[fileArg + 1]) {
|
||||
const filePath = path.resolve(process.cwd(), process.argv[fileArg + 1]);
|
||||
if (!fs.existsSync(filePath)) {
|
||||
console.error('文件不存在:', filePath);
|
||||
process.exit(1);
|
||||
}
|
||||
sql = fs.readFileSync(filePath, 'utf8');
|
||||
} else {
|
||||
const sqlArg = process.argv.find((a, i) => i > 0 && !a.startsWith('-') && a !== '-f');
|
||||
if (!sqlArg) {
|
||||
console.error('用法: node run.js "SQL" 或 node run.js -f migration.sql');
|
||||
process.exit(1);
|
||||
}
|
||||
sql = sqlArg;
|
||||
}
|
||||
|
||||
if (!sql.trim()) {
|
||||
console.error('SQL 为空');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const config = parseDsn(dsn);
|
||||
const mysql = require('mysql2/promise');
|
||||
const conn = await mysql.createConnection({
|
||||
host: config.host,
|
||||
port: config.port,
|
||||
user: config.user,
|
||||
password: config.password,
|
||||
database: config.database,
|
||||
multipleStatements: true,
|
||||
});
|
||||
|
||||
try {
|
||||
const [result] = await conn.query(sql);
|
||||
if (Array.isArray(result)) {
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} else if (result && typeof result === 'object' && 'affectedRows' in result) {
|
||||
console.log(JSON.stringify({ affectedRows: result.affectedRows, insertId: result.insertId }, null, 2));
|
||||
} else {
|
||||
console.log(result);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('执行失败:', err.message);
|
||||
process.exit(1);
|
||||
} finally {
|
||||
await conn.end();
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in New Issue
Block a user