From c6e15773ec7244f21f5b94a2753b868949568321 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Fri, 20 Mar 2026 12:55:00 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=BB=88=E7=AB=AF=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=99=A8=E8=87=AA=E5=8A=A8=E5=88=86=E9=85=8D=E7=A9=BA=E9=97=B2?= =?UTF-8?q?=E7=AB=AF=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dev-runner.js | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 +- server/index.js | 35 +++++++++++++++++++++++++---- vite.config.ts | 17 +++++++++++++- 4 files changed, 107 insertions(+), 6 deletions(-) create mode 100644 dev-runner.js diff --git a/dev-runner.js b/dev-runner.js new file mode 100644 index 0000000..9619f0f --- /dev/null +++ b/dev-runner.js @@ -0,0 +1,59 @@ +import { spawn } from 'node:child_process'; +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const PORT_FILE = '.terminal-server-port'; + +async function waitForPortFile(timeout = 10000) { + const start = Date.now(); + while (Date.now() - start < timeout) { + if (fs.existsSync(PORT_FILE)) { + return true; + } + await new Promise(r => setTimeout(r, 100)); + } + return false; +} + +async function main() { + // Clean up any existing port file + if (fs.existsSync(PORT_FILE)) { + fs.unlinkSync(PORT_FILE); + } + + // Start server + console.log('Starting terminal server...'); + const server = spawn('node', ['server/index.js'], { + stdio: 'inherit', + shell: true, + }); + + // Wait for server to be ready + await waitForPortFile(); + const port = fs.readFileSync(PORT_FILE, 'utf-8').trim(); + console.log(`Terminal server ready on port ${port}`); + + // Set env for vite + process.env.TERMINAL_API_URL = `http://localhost:${port}`; + + // Start vite + console.log('Starting Vite dev server...'); + const vite = spawn('npx', ['vite'], { + stdio: 'inherit', + shell: true, + env: { ...process.env, TERMINAL_API_URL: `http://localhost:${port}` }, + }); + + // Cleanup on exit + process.on('exit', () => { + server.kill(); + vite.kill(); + if (fs.existsSync(PORT_FILE)) { + fs.unlinkSync(PORT_FILE); + } + }); +} + +main().catch(console.error); diff --git a/package.json b/package.json index 91e0d72..f9b9b7d 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "type": "module", "scripts": { - "dev": "concurrently \"node server/index.js\" \"vite\"", + "dev": "node dev-runner.js", "dev:server": "node server/index.js", "build": "tsc && vite build", "preview": "vite preview" diff --git a/server/index.js b/server/index.js index d22c3a9..7a94f58 100644 --- a/server/index.js +++ b/server/index.js @@ -9,7 +9,8 @@ const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const app = express(); -const PORT = process.env.PORT || 3002; +const PORT = parseInt(process.env.PORT || '3002', 10); +const PORT_FILE = '.terminal-server-port'; // CORS middleware app.use((req, res, next) => { @@ -347,6 +348,32 @@ wss.on('connection', (ws, req) => { }); }); -server.listen(PORT, () => { - console.log(`Terminal server running on http://localhost:${PORT}`); -}); +async function findAvailablePort(startPort) { + const net = await import('net'); + return new Promise((resolve) => { + const server = net.createServer(); + server.listen(startPort, () => { + const port = server.address().port; + server.close(() => resolve(port)); + }); + server.on('error', () => { + resolve(findAvailablePort(startPort + 1)); + }); + }); +} + +(async () => { + const fs = await import('fs'); + const availablePort = await findAvailablePort(PORT); + + server.listen(availablePort, () => { + console.log(`Terminal server running on http://localhost:${availablePort}`); + fs.writeFileSync(PORT_FILE, String(availablePort)); + }); + + process.on('exit', () => { + try { + fs.unlinkSync(PORT_FILE); + } catch {} + }); +})(); diff --git a/vite.config.ts b/vite.config.ts index 0c99825..58580ab 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -2,8 +2,23 @@ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import path from 'node:path' import { fileURLToPath } from 'node:url' +import fs from 'node:fs' const __dirname = path.dirname(fileURLToPath(import.meta.url)) +const PORT_FILE = '.terminal-server-port' + +function getTerminalApiUrl() { + if (process.env.TERMINAL_API_URL) { + return process.env.TERMINAL_API_URL + } + try { + if (fs.existsSync(PORT_FILE)) { + const port = fs.readFileSync(PORT_FILE, 'utf-8').trim() + return `http://localhost:${port}` + } + } catch {} + return 'http://localhost:3002' +} export default defineConfig({ plugins: [react()], @@ -19,7 +34,7 @@ export default defineConfig({ server: { proxy: { '/api': { - target: 'http://localhost:3002', + target: getTerminalApiUrl(), changeOrigin: true, ws: true, rewrite: (path) => path,