peardock/server/server.js
2024-11-26 03:36:51 -05:00

147 lines
5.0 KiB
JavaScript

import Hyperswarm from 'hyperswarm';
import Docker from 'dockerode';
import crypto from 'hypercore-crypto';
const docker = new Docker({ socketPath: '/var/run/docker.sock' });
const swarm = new Hyperswarm();
const connectedPeers = new Set();
// Generate a topic for the server
const topic = crypto.randomBytes(32);
console.log(`[INFO] Server started with topic: ${topic.toString('hex')}`);
swarm.join(topic, { server: true, client: false });
swarm.on('connection', (peer) => {
console.log(`[INFO] Peer connected`);
connectedPeers.add(peer);
peer.on('data', async (data) => {
try {
const parsedData = JSON.parse(data.toString());
console.log(`[DEBUG] Received data from peer: ${JSON.stringify(parsedData)}`);
let response;
switch (parsedData.command) {
case 'listContainers':
console.log(`[INFO] Handling 'listContainers' command`);
const containers = await docker.listContainers({ all: true });
response = { type: 'containers', data: containers };
break;
case 'startContainer':
console.log(`[INFO] Handling 'startContainer' command for container: ${parsedData.args.id}`);
await docker.getContainer(parsedData.args.id).start();
response = { success: true, message: `Container ${parsedData.args.id} started` };
break;
case 'stopContainer':
console.log(`[INFO] Handling 'stopContainer' command for container: ${parsedData.args.id}`);
await docker.getContainer(parsedData.args.id).stop();
response = { success: true, message: `Container ${parsedData.args.id} stopped` };
break;
case 'removeContainer':
console.log(`[INFO] Handling 'removeContainer' command for container: ${parsedData.args.id}`);
await docker.getContainer(parsedData.args.id).remove({ force: true });
response = { success: true, message: `Container ${parsedData.args.id} removed` };
break;
case 'startTerminal':
console.log(`[INFO] Starting terminal for container: ${parsedData.args.containerId}`);
handleTerminal(parsedData.args.containerId, peer);
return; // No response needed for streaming
default:
console.warn(`[WARN] Unknown command: ${parsedData.command}`);
response = { error: 'Unknown command' };
}
console.log(`[DEBUG] Sending response to peer: ${JSON.stringify(response)}`);
peer.write(JSON.stringify(response));
} catch (err) {
console.error(`[ERROR] Failed to handle data from peer: ${err.message}`);
peer.write(JSON.stringify({ error: err.message }));
}
});
peer.on('close', () => {
console.log(`[INFO] Peer disconnected`);
connectedPeers.delete(peer);
});
});
// Stream Docker events to all peers
docker.getEvents({}, (err, stream) => {
if (err) {
console.error(`[ERROR] Failed to get Docker events: ${err.message}`);
return;
}
stream.on('data', async (chunk) => {
try {
const event = JSON.parse(chunk.toString());
console.log(`[INFO] Docker event received: ${event.status} - ${event.id}`);
// Get updated container list and broadcast
const containers = await docker.listContainers({ all: true });
const update = { type: 'containers', data: containers };
for (const peer of connectedPeers) {
peer.write(JSON.stringify(update));
}
} catch (err) {
console.error(`[ERROR] Failed to process Docker event: ${err.message}`);
}
});
});
async function handleTerminal(containerId, peer) {
const container = docker.getContainer(containerId);
try {
const exec = await container.exec({
Cmd: ['/bin/sh'],
AttachStdin: true,
AttachStdout: true,
AttachStderr: true,
Tty: true,
});
const stream = await exec.start({ hijack: true, stdin: true });
console.log(`[INFO] Terminal session started for container: ${containerId}`);
stream.on('data', (chunk) => {
console.log(`[DEBUG] Terminal output: ${chunk.toString()}`);
peer.write(JSON.stringify({ type: 'terminalOutput', containerId, data: chunk.toString() }));
});
peer.on('data', (input) => {
try {
const parsed = JSON.parse(input.toString());
if (parsed.type === 'terminalInput' && parsed.data) {
console.log(`[DEBUG] Terminal input: ${parsed.data}`);
stream.write(parsed.data);
}
} catch (err) {
console.error(`[ERROR] Failed to parse terminal input: ${err.message}`);
}
});
peer.on('close', () => {
console.log(`[INFO] Peer disconnected, ending terminal session for container: ${containerId}`);
stream.end();
});
} catch (err) {
console.error(`[ERROR] Failed to start terminal for container ${containerId}: ${err.message}`);
peer.write(JSON.stringify({ error: `Failed to start terminal: ${err.message}` }));
}
}
process.on('SIGINT', () => {
console.log(`[INFO] Server shutting down`);
swarm.destroy();
process.exit();
});