diff --git a/apps/server/src/index.ts b/apps/server/src/index.ts index 59ebffa7..c8b7291d 100644 --- a/apps/server/src/index.ts +++ b/apps/server/src/index.ts @@ -322,25 +322,57 @@ terminalWss.on( } ); -// Start server -server.listen(PORT, () => { - const terminalStatus = isTerminalEnabled() - ? isTerminalPasswordRequired() - ? "enabled (password protected)" - : "enabled" - : "disabled"; - console.log(` +// Start server with error handling for port conflicts +const startServer = (port: number) => { + server.listen(port, () => { + const terminalStatus = isTerminalEnabled() + ? isTerminalPasswordRequired() + ? "enabled (password protected)" + : "enabled" + : "disabled"; + const portStr = port.toString().padEnd(4); + console.log(` ╔═══════════════════════════════════════════════════════╗ ║ Automaker Backend Server ║ ╠═══════════════════════════════════════════════════════╣ -║ HTTP API: http://localhost:${PORT} ║ -║ WebSocket: ws://localhost:${PORT}/api/events ║ -║ Terminal: ws://localhost:${PORT}/api/terminal/ws ║ -║ Health: http://localhost:${PORT}/api/health ║ +║ HTTP API: http://localhost:${portStr} ║ +║ WebSocket: ws://localhost:${portStr}/api/events ║ +║ Terminal: ws://localhost:${portStr}/api/terminal/ws ║ +║ Health: http://localhost:${portStr}/api/health ║ ║ Terminal: ${terminalStatus.padEnd(37)}║ ╚═══════════════════════════════════════════════════════╝ `); -}); + }); + + server.on("error", (error: NodeJS.ErrnoException) => { + if (error.code === "EADDRINUSE") { + console.error(` +╔═══════════════════════════════════════════════════════╗ +║ ❌ ERROR: Port ${port} is already in use ║ +╠═══════════════════════════════════════════════════════╣ +║ Another process is using this port. ║ +║ ║ +║ To fix this, try one of: ║ +║ ║ +║ 1. Kill the process using the port: ║ +║ lsof -ti:${port} | xargs kill -9 ║ +║ ║ +║ 2. Use a different port: ║ +║ PORT=${port + 1} npm run dev:server ║ +║ ║ +║ 3. Use the init.sh script which handles this: ║ +║ ./init.sh ║ +╚═══════════════════════════════════════════════════════╝ +`); + process.exit(1); + } else { + console.error("[Server] Error starting server:", error); + process.exit(1); + } + }); +}; + +startServer(PORT); // Graceful shutdown process.on("SIGTERM", () => { diff --git a/init.sh b/init.sh index 5f4bba43..75b876aa 100755 --- a/init.sh +++ b/init.sh @@ -33,43 +33,38 @@ fi echo -e "${YELLOW}Checking Playwright browsers...${NC}" npx playwright install chromium 2>/dev/null || true +# Function to kill process on a port and wait for it to be freed +kill_port() { + local port=$1 + local pids=$(lsof -ti:$port 2>/dev/null) + + if [ -n "$pids" ]; then + echo -e "${YELLOW}Killing process(es) on port $port: $pids${NC}" + echo "$pids" | xargs kill -9 2>/dev/null || true + + # Wait for port to be freed (max 5 seconds) + local retries=0 + while [ $retries -lt 10 ]; do + if ! lsof -ti:$port >/dev/null 2>&1; then + echo -e "${GREEN}✓ Port $port is now free${NC}" + return 0 + fi + sleep 0.5 + retries=$((retries + 1)) + done + + echo -e "${RED}Warning: Port $port may still be in use${NC}" + return 1 + else + echo -e "${GREEN}✓ Port $port is available${NC}" + return 0 + fi +} + # Kill any existing processes on required ports echo -e "${YELLOW}Checking for processes on ports 3007 and 3008...${NC}" -lsof -ti:3007 | xargs kill -9 2>/dev/null || true -lsof -ti:3008 | xargs kill -9 2>/dev/null || true - -# Start the backend server -echo -e "${BLUE}Starting backend server on port 3008...${NC}" -npm run dev:server > logs/server.log 2>&1 & -SERVER_PID=$! - -echo -e "${YELLOW}Waiting for server to be ready...${NC}" - -# Wait for server health check -MAX_RETRIES=30 -RETRY_COUNT=0 -SERVER_READY=false - -while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do - if curl -s http://localhost:3008/api/health > /dev/null 2>&1; then - SERVER_READY=true - break - fi - sleep 1 - RETRY_COUNT=$((RETRY_COUNT + 1)) - echo -n "." -done - -echo "" - -if [ "$SERVER_READY" = false ]; then - echo -e "${RED}Error: Server failed to start${NC}" - echo "Check logs/server.log for details" - kill $SERVER_PID 2>/dev/null || true - exit 1 -fi - -echo -e "${GREEN}✓ Server is ready!${NC}" +kill_port 3007 +kill_port 3008 echo "" # Prompt user for application mode @@ -81,12 +76,59 @@ echo " 2) Desktop Application (Electron)" echo "═══════════════════════════════════════════════════════" echo "" +SERVER_PID="" + +# Cleanup function +cleanup() { + echo 'Cleaning up...' + if [ -n "$SERVER_PID" ]; then + kill $SERVER_PID 2>/dev/null || true + fi + exit +} + +trap cleanup INT TERM EXIT + while true; do read -p "Enter your choice (1 or 2): " choice case $choice in 1) echo "" echo -e "${BLUE}Launching Web Application...${NC}" + + # Start the backend server (only needed for Web mode) + echo -e "${BLUE}Starting backend server on port 3008...${NC}" + mkdir -p logs + npm run dev:server > logs/server.log 2>&1 & + SERVER_PID=$! + + echo -e "${YELLOW}Waiting for server to be ready...${NC}" + + # Wait for server health check + MAX_RETRIES=30 + RETRY_COUNT=0 + SERVER_READY=false + + while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do + if curl -s http://localhost:3008/api/health > /dev/null 2>&1; then + SERVER_READY=true + break + fi + sleep 1 + RETRY_COUNT=$((RETRY_COUNT + 1)) + echo -n "." + done + + echo "" + + if [ "$SERVER_READY" = false ]; then + echo -e "${RED}Error: Server failed to start${NC}" + echo "Check logs/server.log for details" + kill $SERVER_PID 2>/dev/null || true + exit 1 + fi + + echo -e "${GREEN}✓ Server is ready!${NC}" echo "The application will be available at: ${GREEN}http://localhost:3007${NC}" echo "" npm run dev:web @@ -95,6 +137,8 @@ while true; do 2) echo "" echo -e "${BLUE}Launching Desktop Application...${NC}" + echo -e "${YELLOW}(Electron will start its own backend server)${NC}" + echo "" npm run dev:electron break ;; @@ -103,6 +147,3 @@ while true; do ;; esac done - -# Cleanup on exit -trap "echo 'Cleaning up...'; kill $SERVER_PID 2>/dev/null || true; exit" INT TERM EXIT