Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions src/process/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,28 @@ app.get("/", (_, res) => {
});

const sshConnections = new Map();
const MAX_CONNECTIONS = 100; // Limit concurrent connections

// Precompile regex for better performance
const ansiRegex = /\x1b\[[0-9;?]*[a-zA-Z]/g;

// Function to clean ANSI escape sequences
function cleanAnsiSequences(text) {
// Remove ANSI escape sequences (like [?2004h, [?2004l, colors, etc.)
return text.replace(/\x1b\[[0-9;?]*[a-zA-Z]/g, '');
// Reset lastIndex for reusable global regex
ansiRegex.lastIndex = 0;
return text.replace(ansiRegex, '');
}

wss.on("connection", (ws) => {
// Enforce connection limit
if (sshConnections.size >= MAX_CONNECTIONS) {
console.warn(`Connection limit reached (${sshConnections.size}), rejecting new connection`);
ws.send('Server at capacity. Please try again later.\n');
ws.close();
return;
}

console.log("New client connected");

ws.on("message", (message) => {
Expand Down Expand Up @@ -104,14 +118,19 @@ function connectSSH(ws, hostname, password) {
stream.on('data', (data) => {
const output = data.toString();
const cleanedOutput = cleanAnsiSequences(output);
console.log('SSH output:', cleanedOutput);
// Reduce console.log overhead in production
if (process.env.NODE_ENV !== 'production') {
console.log('SSH output:', cleanedOutput);
}
ws.send(cleanedOutput);
});

stream.stderr.on('data', (data) => {
const error = data.toString();
const cleanedError = cleanAnsiSequences(error);
console.error('SSH stderr:', cleanedError);
if (process.env.NODE_ENV !== 'production') {
console.error('SSH stderr:', cleanedError);
}
ws.send(`ERROR: ${cleanedError}`);
});
});
Expand Down
43 changes: 23 additions & 20 deletions src/renderer/react-wrapper/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { useEffect, useState, useRef, useCallback } from "react";
import { useEffect, useState, useRef, useCallback, useMemo } from "react";
import './App.css';
import { addProcessListener, sendToProcess } from "./nexus-bridge";

const WS_URL = "ws://localhost:3230";
const MAX_MESSAGE_HISTORY = 1000;
const TRIM_TO_SIZE = 900;

function App() {
const [messages, setMessages] = useState<string[]>([]);
Expand Down Expand Up @@ -113,18 +115,19 @@ function App() {
websocket.onmessage = (event) => {
const message = event.data;
console.log('Received:', message);
setMessages((prev) => [...prev, message]);

// Check if connection was successful
if (message.includes('Connected to') || message.includes('SSH Client ready')) {
// Use callback form for better performance
setMessages((prev) => {
// Limit message history to prevent memory issues
const newMessages = prev.length > MAX_MESSAGE_HISTORY ? prev.slice(-TRIM_TO_SIZE) : prev;
return [...newMessages, message];
});

// Check if connection was successful (optimized checks)
if (message.includes('Connected to')) {
setIsConnecting(false);
setIsConnected(true);
}

if (message.includes('Connection failed') ||
message.includes('ERROR:') ||
message.includes('Connection closed') ||
message.includes('SSH stream closed')) {
} else if (message.includes('Connection failed') || message.includes('Connection closed')) {
setIsConnecting(false);
setIsConnected(false);
}
Expand All @@ -146,7 +149,7 @@ function App() {
};
}, []);

const handleDisconnect = () => {
const handleDisconnect = useCallback(() => {
if (ws && ws.readyState === WebSocket.OPEN) {
const disconnectData = {
action: 'disconnect'
Expand All @@ -155,9 +158,9 @@ function App() {
}
setIsConnected(false);
setIsConnecting(false);
};
}, [ws]);

const handleTerminalInput = (e: React.KeyboardEvent<HTMLInputElement>) => {
const handleTerminalInput = useCallback((e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter' && isConnected && ws && ws.readyState === WebSocket.OPEN) {
const input = (e.target as HTMLInputElement).value;

Expand All @@ -169,19 +172,19 @@ function App() {
ws.send(JSON.stringify(inputData));
(e.target as HTMLInputElement).value = '';
}
};
}, [isConnected, ws]);

const getButtonText = () => {
const getButtonText = useMemo(() => {
if (isConnecting) return 'Connecting...';
if (isConnected) return 'Disconnect';
return 'Connect';
};
}, [isConnecting, isConnected]);

const getButtonClass = () => {
const getButtonClass = useMemo(() => {
if (isConnecting) return 'connect';
if (isConnected) return 'disconnect';
return 'connect';
};
}, [isConnecting, isConnected]);

return (
<>
Expand Down Expand Up @@ -215,10 +218,10 @@ function App() {
</div>
<button
onClick={isConnected ? handleDisconnect : () => handleConnect()}
className={getButtonClass()}
className={getButtonClass}
disabled={!hostname.trim() || isConnecting || !ws}
>
{getButtonText()}
{getButtonText}
</button>
</div>
<div className="row2">
Expand Down
5 changes: 3 additions & 2 deletions src/renderer/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@ const observer = new MutationObserver(() => {
}
});


// Use more specific observation to reduce overhead
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['style']
attributeFilter: ['style'],
attributeOldValue: false // Don't track old values to save memory
});