#!/usr/bin/env node const { execSync, exec } = require('child_process'); const fs = require('fs'); const path = require('path'); // 检查端口是否被占用 function isPortTaken(port) { return new Promise((resolve) => { // 在Linux上使用lsof检查端口占用 try { execSync(`lsof -i :${port}`, { stdio: 'ignore' }); resolve(true); } catch (error) { resolve(false); } }); } // 查找占用端口的进程ID function findProcessUsingPort(port) { try { const output = execSync(`lsof -i :${port} | grep LISTEN`, { encoding: 'utf8' }); const lines = output.trim().split('\n'); if (lines.length > 0) { const parts = lines[0].trim().split(/\s+/); return { pid: parts[1], process: parts[0], user: parts[2] }; } } catch (error) { console.log(`未找到占用端口 ${port} 的进程`); } return null; } // 停止指定进程 function stopProcess(pid) { try { execSync(`kill -9 ${pid}`); console.log(`成功停止进程 ${pid}`); return true; } catch (error) { console.error(`停止进程 ${pid} 失败:`, error.message); return false; } } // 修改PM2配置文件中的端口 function updatePM2ConfigFile(newPort) { const configPath = path.join(__dirname, 'ecosystem.config.js'); try { let content = fs.readFileSync(configPath, 'utf8'); // 备份配置文件 const backupPath = configPath + '.bak.' + Date.now(); fs.writeFileSync(backupPath, content); console.log(`已创建配置备份: ${backupPath}`); // 修改production环境端口 content = content.replace(/PORT:\s*3001/g, `PORT: ${newPort}`); fs.writeFileSync(configPath, content); console.log(`已将PM2配置中的端口修改为: ${newPort}`); return true; } catch (error) { console.error('修改PM2配置文件失败:', error.message); return false; } } // 重启PM2应用 function restartPM2App() { try { execSync('pm2 restart ecosystem.config.js', { stdio: 'inherit' }); return true; } catch (error) { console.error('重启PM2应用失败:', error.message); return false; } } // 主函数 async function main() { console.log('=== 微信小程序后端服务 - 端口冲突修复工具 ===\n'); const originalPort = 3001; let newPort = 3001; // 检查原始端口是否被占用 const isOriginalPortTaken = await isPortTaken(originalPort); if (isOriginalPortTaken) { console.log(`发现端口 ${originalPort} 被占用!`); // 查找占用进程 const processInfo = findProcessUsingPort(originalPort); if (processInfo) { console.log(`占用端口的进程信息:`); console.log(`- 进程ID: ${processInfo.pid}`); console.log(`- 进程名称: ${processInfo.process}`); console.log(`- 用户: ${processInfo.user}`); // 询问是否停止该进程 const readline = require('readline').createInterface({ input: process.stdin, output: process.stdout }); readline.question(`是否停止该进程以释放端口 ${originalPort}? (y/n): `, (answer) => { readline.close(); if (answer.toLowerCase() === 'y') { if (stopProcess(processInfo.pid)) { console.log(`端口 ${originalPort} 已被释放,正在重启应用...`); restartPM2App(); } } else { // 查找可用端口 console.log('正在查找可用端口...'); let foundPort = false; for (let i = 3002; i <= 3100; i++) { execSync(`lsof -i :${i}`, { stdio: 'ignore' }, (error) => { if (error) { newPort = i; foundPort = true; // 修改PM2配置并重启 if (updatePM2ConfigFile(newPort)) { console.log(`已切换到可用端口: ${newPort}`); console.log('正在重启PM2应用...'); restartPM2App(); } } }); if (foundPort) break; } if (!foundPort) { console.error('未找到可用端口,请手动指定一个未被占用的端口。'); } } }); } else { console.error('无法确定占用端口的进程,请手动检查端口占用情况。'); } } else { console.log(`端口 ${originalPort} 未被占用,检查应用状态...`); // 检查PM2应用状态 try { const statusOutput = execSync('pm2 status wechat-app', { encoding: 'utf8' }); console.log(statusOutput); // 提示用户重启应用 const readline = require('readline').createInterface({ input: process.stdin, output: process.stdout }); readline.question('是否需要重启应用? (y/n): ', (answer) => { readline.close(); if (answer.toLowerCase() === 'y') { restartPM2App(); } else { console.log('操作已取消。'); } }); } catch (error) { console.error('检查PM2应用状态失败:', error.message); console.log('建议尝试手动重启应用: pm2 restart wechat-app'); } } } // 提供非交互式修复选项 function provideNonInteractiveFix() { console.log('\n=== 非交互式修复选项 ==='); console.log('1. 强制释放3001端口并重启应用'); console.log('2. 使用备用端口3004并更新配置'); console.log('3. 查看当前端口占用情况'); const readline = require('readline').createInterface({ input: process.stdin, output: process.stdout }); readline.question('请选择修复方式 (1-3): ', (answer) => { readline.close(); switch (answer) { case '1': const processInfo = findProcessUsingPort(3001); if (processInfo && stopProcess(processInfo.pid)) { console.log('正在重启应用...'); restartPM2App(); } else { console.log('端口未被占用或无法停止占用进程'); } break; case '2': if (updatePM2ConfigFile(3004)) { console.log('正在重启应用...'); restartPM2App(); } break; case '3': try { console.log('端口占用情况:'); execSync('netstat -tuln | grep 300', { stdio: 'inherit' }); } catch (error) { console.log('未找到相关端口占用信息'); } break; default: console.log('无效选项,操作已取消。'); } }); } // 运行主程序 main().catch(err => { console.error('修复过程中发生错误:', err); provideNonInteractiveFix(); }); // 提供帮助信息 setTimeout(() => { console.log('\n如果自动修复失败,可以尝试以下手动解决方案:'); console.log('1. 检查端口占用: lsof -i :3001 或 netstat -ano | findstr :3001'); console.log('2. 停止占用进程: kill -9 [进程ID]'); console.log('3. 或者修改PM2配置使用其他端口:'); console.log(' - 编辑 ecosystem.config.js 文件'); console.log(' - 将 PORT: 3001 修改为其他可用端口'); console.log(' - 保存并运行: pm2 restart ecosystem.config.js'); }, 1000);