#!/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();