test
This commit is contained in:
parent
57209a8159
commit
73adabe2c4
19
app.js
19
app.js
@ -383,11 +383,28 @@ function addConnection(topicHex) {
|
||||
<span>
|
||||
<span class="connection-status status-disconnected"></span>${topicId}
|
||||
</span>
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-sm btn-primary docker-terminal-btn">
|
||||
<i class="fas fa-terminal"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-danger disconnect-btn">
|
||||
<i class="fas fa-plug"></i>
|
||||
</button>
|
||||
`;
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Add Docker Terminal button event listener
|
||||
connectionItem.querySelector('.docker-terminal-btn').addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
const connection = connections[topicId];
|
||||
if (connection && connection.peer) {
|
||||
import('./dockerTerminal.js').then(({ startDockerTerminal }) => {
|
||||
startDockerTerminal(topicId, connection.peer);
|
||||
});
|
||||
} else {
|
||||
console.error('[ERROR] No active peer for Docker CLI terminal.');
|
||||
}
|
||||
})
|
||||
connectionItem.querySelector('span').addEventListener('click', () => switchConnection(topicId));
|
||||
connectionItem.querySelector('.disconnect-btn').addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
|
13
index.html
13
index.html
@ -515,6 +515,19 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Docker Terminal Modal -->
|
||||
<div id="docker-terminal-modal" style="display: none; flex-direction: column;">
|
||||
<div class="header">
|
||||
<span id="docker-terminal-title"></span>
|
||||
<div>
|
||||
<button id="docker-kill-terminal-btn" class="btn btn-sm btn-danger">
|
||||
<i class="fas fa-times-circle"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="docker-terminal-container" style="flex: 1; overflow: hidden; background-color: black;"></div>
|
||||
</div>
|
||||
|
||||
<!-- Alert Container -->
|
||||
<div id="alert-container" class="position-fixed top-0 start-50 translate-middle-x mt-3"
|
||||
style="z-index: 1051; max-width: 90%;"></div>
|
||||
|
100
libs/dockerTerminal.js
Normal file
100
libs/dockerTerminal.js
Normal file
@ -0,0 +1,100 @@
|
||||
import { Terminal } from 'xterm';
|
||||
import { FitAddon } from 'xterm-addon-fit';
|
||||
|
||||
// DOM Elements
|
||||
const dockerTerminalModal = document.getElementById('docker-terminal-modal');
|
||||
const dockerTerminalTitle = document.getElementById('docker-terminal-title');
|
||||
const dockerTerminalContainer = document.getElementById('docker-terminal-container');
|
||||
const dockerKillTerminalBtn = document.getElementById('docker-kill-terminal-btn');
|
||||
|
||||
// Terminal variables
|
||||
let dockerTerminalSession = null;
|
||||
|
||||
// Start Docker CLI terminal session
|
||||
function startDockerTerminal(connectionId, peer) {
|
||||
if (!peer) {
|
||||
console.error('[ERROR] No active peer for Docker CLI terminal.');
|
||||
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');
|
||||
}
|
||||
});
|
||||
|
||||
peer.on('data', (data) => {
|
||||
try {
|
||||
const response = JSON.parse(data.toString());
|
||||
if (response.type === 'dockerOutput' && response.connectionId === connectionId) {
|
||||
xterm.write(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`[ERROR] Failed to parse response from peer: ${error.message}`);
|
||||
}
|
||||
});
|
||||
|
||||
dockerTerminalTitle.textContent = `Docker CLI Terminal: ${connectionId}`;
|
||||
dockerTerminalModal.style.display = 'flex';
|
||||
}
|
||||
|
||||
// Sanitize input to ensure only Docker CLI commands are allowed
|
||||
function sanitizeDockerCommand(command) {
|
||||
const allowedCommands = ['docker', 'docker-compose'];
|
||||
const parts = command.split(/\s+/);
|
||||
const baseCommand = parts[0];
|
||||
|
||||
if (allowedCommands.includes(baseCommand)) {
|
||||
return command; // Valid Docker command
|
||||
}
|
||||
|
||||
return null; // Invalid command
|
||||
}
|
||||
|
||||
// Clean up Docker CLI terminal session
|
||||
function cleanUpDockerTerminal() {
|
||||
if (dockerTerminalSession) {
|
||||
dockerTerminalSession.xterm.dispose();
|
||||
dockerTerminalSession = null;
|
||||
dockerTerminalContainer.innerHTML = '';
|
||||
dockerTerminalModal.style.display = 'none';
|
||||
console.log('[INFO] Docker CLI terminal session cleaned up.');
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Kill Terminal button
|
||||
dockerKillTerminalBtn.onclick = () => {
|
||||
cleanUpDockerTerminal();
|
||||
};
|
||||
|
||||
// Export functions
|
||||
export { startDockerTerminal, cleanUpDockerTerminal };
|
@ -107,7 +107,44 @@ swarm.on('connection', (peer) => {
|
||||
|
||||
await duplicateContainer(name, image, hostname, netmode, cpu, memoryInMB, dupConfig, peer);
|
||||
return; // Response is handled within the duplicateContainer function
|
||||
case 'dockerCommand':
|
||||
console.log(`[INFO] Executing Docker CLI command: ${parsedData.data}`);
|
||||
try {
|
||||
const exec = spawn('sh', ['-c', parsedData.data]);
|
||||
|
||||
exec.stdout.on('data', (output) => {
|
||||
peer.write(
|
||||
JSON.stringify({
|
||||
type: 'dockerOutput',
|
||||
connectionId: parsedData.connectionId,
|
||||
data: output.toString(),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
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':
|
||||
console.log(`[INFO] Handling 'startContainer' command for container: ${parsedData.args.id}`);
|
||||
await docker.getContainer(parsedData.args.id).start();
|
||||
|
Loading…
Reference in New Issue
Block a user