test again
This commit is contained in:
parent
73adabe2c4
commit
c38f23d50e
2
app.js
2
app.js
@ -398,7 +398,7 @@ connectionItem.querySelector('.docker-terminal-btn').addEventListener('click', (
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const connection = connections[topicId];
|
const connection = connections[topicId];
|
||||||
if (connection && connection.peer) {
|
if (connection && connection.peer) {
|
||||||
import('./dockerTerminal.js').then(({ startDockerTerminal }) => {
|
import('./libs/dockerTerminal.js').then(({ startDockerTerminal }) => {
|
||||||
startDockerTerminal(topicId, connection.peer);
|
startDockerTerminal(topicId, connection.peer);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
18
index.html
18
index.html
@ -516,16 +516,16 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Docker Terminal Modal -->
|
<!-- Docker Terminal Modal -->
|
||||||
<div id="docker-terminal-modal" style="display: none; flex-direction: column;">
|
<!-- Docker CLI Terminal Modal -->
|
||||||
<div class="header">
|
<div id="docker-terminal-modal" class="position-fixed bottom-0 start-0 w-100 max-height-90 bg-dark text-white d-flex flex-column" style="display: none; z-index: 1000;">
|
||||||
<span id="docker-terminal-title"></span>
|
<div class="header d-flex justify-content-between align-items-center bg-secondary p-2">
|
||||||
<div>
|
<span id="docker-terminal-title" class="fw-bold">Docker CLI Terminal</span>
|
||||||
<button id="docker-kill-terminal-btn" class="btn btn-sm btn-danger">
|
<button id="docker-kill-terminal-btn" class="btn btn-sm btn-danger">
|
||||||
<i class="fas fa-times-circle"></i>
|
<i class="fas fa-times-circle"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div id="docker-terminal-container" style="flex: 1; overflow: hidden; background-color: black;"></div>
|
<div id="docker-terminal-container" class="flex-grow-1 overflow-hidden" style="background-color: black;"></div>
|
||||||
|
<div id="docker-terminal-resize-handle" class="bg-secondary" style="height: 10px; cursor: ns-resize;"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Alert Container -->
|
<!-- Alert Container -->
|
||||||
|
@ -10,87 +10,98 @@ const dockerKillTerminalBtn = document.getElementById('docker-kill-terminal-btn'
|
|||||||
// Terminal variables
|
// Terminal variables
|
||||||
let dockerTerminalSession = null;
|
let dockerTerminalSession = null;
|
||||||
|
|
||||||
// Start Docker CLI terminal session
|
|
||||||
function startDockerTerminal(connectionId, peer) {
|
function startDockerTerminal(connectionId, peer) {
|
||||||
if (!peer) {
|
if (!peer) {
|
||||||
console.error('[ERROR] No active peer for Docker CLI terminal.');
|
console.error('[ERROR] No active peer for Docker CLI terminal.');
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
if (dockerTerminalSession) {
|
|
||||||
console.log(`[INFO] Docker CLI terminal session already exists for connection: ${connectionId}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`[INFO] Starting Docker CLI terminal for connection: ${connectionId}`);
|
|
||||||
|
|
||||||
const xterm = new Terminal({
|
|
||||||
cursorBlink: true,
|
|
||||||
theme: { background: '#1a1a1a', foreground: '#ffffff' },
|
|
||||||
});
|
|
||||||
const fitAddon = new FitAddon();
|
|
||||||
xterm.loadAddon(fitAddon);
|
|
||||||
|
|
||||||
dockerTerminalContainer.innerHTML = ''; // Clear previous content
|
|
||||||
xterm.open(dockerTerminalContainer);
|
|
||||||
fitAddon.fit();
|
|
||||||
|
|
||||||
dockerTerminalSession = { xterm, fitAddon, connectionId, peer };
|
|
||||||
|
|
||||||
xterm.onData((input) => {
|
|
||||||
const sanitizedInput = sanitizeDockerCommand(input.trim());
|
|
||||||
if (sanitizedInput) {
|
|
||||||
console.log(`[DEBUG] Sending Docker CLI command: ${sanitizedInput}`);
|
|
||||||
peer.write(
|
|
||||||
JSON.stringify({
|
|
||||||
type: 'dockerCommand',
|
|
||||||
data: sanitizedInput,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
xterm.write('\r\n[ERROR] Invalid command. Only Docker CLI commands are allowed.\r\n');
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
if (dockerTerminalSession) {
|
||||||
peer.on('data', (data) => {
|
console.log('[INFO] Docker CLI terminal session already exists.');
|
||||||
try {
|
return;
|
||||||
const response = JSON.parse(data.toString());
|
}
|
||||||
if (response.type === 'dockerOutput' && response.connectionId === connectionId) {
|
|
||||||
xterm.write(response.data);
|
const xterm = new Terminal({
|
||||||
|
cursorBlink: true,
|
||||||
|
theme: { background: '#000000', foreground: '#ffffff' },
|
||||||
|
});
|
||||||
|
const fitAddon = new FitAddon();
|
||||||
|
xterm.loadAddon(fitAddon);
|
||||||
|
|
||||||
|
dockerTerminalContainer.innerHTML = ''; // Clear previous content
|
||||||
|
xterm.open(dockerTerminalContainer);
|
||||||
|
fitAddon.fit();
|
||||||
|
|
||||||
|
dockerTerminalSession = { xterm, fitAddon, connectionId, peer };
|
||||||
|
|
||||||
|
let inputBuffer = ''; // Buffer for user input
|
||||||
|
|
||||||
|
xterm.onData((input) => {
|
||||||
|
if (input === '\r') { // User pressed Enter
|
||||||
|
const sanitizedInput = sanitizeDockerCommand(inputBuffer.trim());
|
||||||
|
if (sanitizedInput) {
|
||||||
|
console.log(`[DEBUG] Sending Docker CLI command: ${sanitizedInput}`);
|
||||||
|
peer.write(
|
||||||
|
JSON.stringify({
|
||||||
|
type: 'dockerCommand',
|
||||||
|
connectionId,
|
||||||
|
data: sanitizedInput,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
xterm.write('\r\n'); // Move to the next line in the terminal
|
||||||
|
} else {
|
||||||
|
xterm.write('\r\n[ERROR] Invalid command. Only Docker CLI commands are allowed.\r\n');
|
||||||
|
}
|
||||||
|
inputBuffer = ''; // Clear the buffer after processing
|
||||||
|
} else if (input === '\u007F') { // Handle backspace
|
||||||
|
if (inputBuffer.length > 0) {
|
||||||
|
inputBuffer = inputBuffer.slice(0, -1); // Remove the last character from the buffer
|
||||||
|
xterm.write('\b \b'); // Erase the character from the terminal display
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
inputBuffer += input; // Append input to the buffer
|
||||||
|
xterm.write(input); // Display input in the terminal
|
||||||
}
|
}
|
||||||
} catch (error) {
|
});
|
||||||
console.error(`[ERROR] Failed to parse response from peer: ${error.message}`);
|
|
||||||
}
|
peer.on('data', (data) => {
|
||||||
});
|
try {
|
||||||
|
const response = JSON.parse(data.toString());
|
||||||
dockerTerminalTitle.textContent = `Docker CLI Terminal: ${connectionId}`;
|
if (response.type === 'dockerOutput' && response.connectionId === connectionId) {
|
||||||
dockerTerminalModal.style.display = 'flex';
|
xterm.write(`${response.data}\r\n`);
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`[ERROR] Failed to parse response from peer: ${error.message}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
dockerTerminalTitle.textContent = `Docker CLI Terminal: ${connectionId}`;
|
||||||
|
dockerTerminalModal.style.display = 'flex';
|
||||||
|
|
||||||
|
dockerKillTerminalBtn.onclick = () => {
|
||||||
|
cleanUpDockerTerminal();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Sanitize input to ensure only Docker CLI commands are allowed
|
// Sanitize input to ensure only Docker CLI commands are allowed
|
||||||
function sanitizeDockerCommand(command) {
|
function sanitizeDockerCommand(command) {
|
||||||
const allowedCommands = ['docker', 'docker-compose'];
|
// Allow commands starting with "docker" and disallow dangerous operators
|
||||||
const parts = command.split(/\s+/);
|
const isValid = /^docker(\s+[\w.-]+)*$/i.test(command);
|
||||||
const baseCommand = parts[0];
|
return isValid ? command : null;
|
||||||
|
|
||||||
if (allowedCommands.includes(baseCommand)) {
|
|
||||||
return command; // Valid Docker command
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null; // Invalid command
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up Docker CLI terminal session
|
// Clean up Docker CLI terminal session
|
||||||
function cleanUpDockerTerminal() {
|
function cleanUpDockerTerminal() {
|
||||||
if (dockerTerminalSession) {
|
if (dockerTerminalSession) {
|
||||||
dockerTerminalSession.xterm.dispose();
|
dockerTerminalSession.xterm.dispose();
|
||||||
dockerTerminalSession = null;
|
dockerTerminalSession = null;
|
||||||
dockerTerminalContainer.innerHTML = '';
|
dockerTerminalContainer.innerHTML = ''; // Clear terminal content
|
||||||
dockerTerminalModal.style.display = 'none';
|
dockerTerminalModal.style.display = 'none'; // Hide the modal
|
||||||
console.log('[INFO] Docker CLI terminal session cleaned up.');
|
dockerTerminalModal.classList.add('hidden');
|
||||||
|
console.log('[INFO] Docker CLI terminal session cleaned up.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Handle Kill Terminal button
|
// Handle Kill Terminal button
|
||||||
dockerKillTerminalBtn.onclick = () => {
|
dockerKillTerminalBtn.onclick = () => {
|
||||||
cleanUpDockerTerminal();
|
cleanUpDockerTerminal();
|
||||||
|
@ -107,44 +107,52 @@ swarm.on('connection', (peer) => {
|
|||||||
|
|
||||||
await duplicateContainer(name, image, hostname, netmode, cpu, memoryInMB, dupConfig, peer);
|
await duplicateContainer(name, image, hostname, netmode, cpu, memoryInMB, dupConfig, peer);
|
||||||
return; // Response is handled within the duplicateContainer function
|
return; // Response is handled within the duplicateContainer function
|
||||||
case 'dockerCommand':
|
case 'dockerCommand':
|
||||||
console.log(`[INFO] Executing Docker CLI command: ${parsedData.data}`);
|
console.log(`[INFO] Executing Docker CLI command: ${parsedData.data}`);
|
||||||
try {
|
try {
|
||||||
const exec = spawn('sh', ['-c', parsedData.data]);
|
const exec = spawn('sh', ['-c', parsedData.data]); // Use `spawn` to execute the command
|
||||||
|
|
||||||
exec.stdout.on('data', (output) => {
|
exec.stdout.on('data', (output) => {
|
||||||
|
console.log(`[DEBUG] Command output: ${output.toString()}`);
|
||||||
|
peer.write(
|
||||||
|
JSON.stringify({
|
||||||
|
type: 'dockerOutput',
|
||||||
|
connectionId: parsedData.connectionId,
|
||||||
|
data: output.toString(),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
exec.stderr.on('data', (error) => {
|
||||||
|
console.error(`[ERROR] Command error output: ${error.toString()}`);
|
||||||
|
peer.write(
|
||||||
|
JSON.stringify({
|
||||||
|
type: 'dockerOutput',
|
||||||
|
connectionId: parsedData.connectionId,
|
||||||
|
data: `[ERROR] ${error.toString()}`,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
exec.on('close', (code) => {
|
||||||
|
console.log(`[INFO] Command exited with code: ${code}`);
|
||||||
|
peer.write(
|
||||||
|
JSON.stringify({
|
||||||
|
type: 'dockerOutput',
|
||||||
|
connectionId: parsedData.connectionId,
|
||||||
|
data: `[INFO] Command exited with code ${code}\n`,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`[ERROR] Failed to execute command: ${error.message}`);
|
||||||
peer.write(
|
peer.write(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
type: 'dockerOutput',
|
error: `Failed to execute command: ${error.message}`
|
||||||
connectionId: parsedData.connectionId,
|
|
||||||
data: output.toString(),
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
break;
|
||||||
exec.stderr.on('data', (error) => {
|
|
||||||
peer.write(
|
|
||||||
JSON.stringify({
|
|
||||||
type: 'dockerOutput',
|
|
||||||
connectionId: parsedData.connectionId,
|
|
||||||
data: `[ERROR] ${error.toString()}`,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
exec.on('close', (code) => {
|
|
||||||
peer.write(
|
|
||||||
JSON.stringify({
|
|
||||||
type: 'dockerOutput',
|
|
||||||
connectionId: parsedData.connectionId,
|
|
||||||
data: `[INFO] Command exited with code ${code}\n`,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
peer.write(JSON.stringify({ error: `Failed to execute command: ${error.message}` }));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'startContainer':
|
case 'startContainer':
|
||||||
console.log(`[INFO] Handling 'startContainer' command for container: ${parsedData.args.id}`);
|
console.log(`[INFO] Handling 'startContainer' command for container: ${parsedData.args.id}`);
|
||||||
await docker.getContainer(parsedData.args.id).start();
|
await docker.getContainer(parsedData.args.id).start();
|
||||||
|
Loading…
Reference in New Issue
Block a user