feat: replace node-pty with E2B sandboxes
This commit is contained in:
@ -44,9 +44,8 @@ const io = new Server(httpServer, {
|
||||
let inactivityTimeout: NodeJS.Timeout | null = null;
|
||||
let isOwnerConnected = false;
|
||||
|
||||
const terminals: {
|
||||
[id: string]: { terminal: IPty; onData: IDisposable; onExit: IDisposable };
|
||||
} = {};
|
||||
const containers: Record<string, Sandbox> = {};
|
||||
const terminals: Record<string, Terminal> = {};
|
||||
|
||||
const dirName = path.join(__dirname, "..");
|
||||
|
||||
@ -101,6 +100,8 @@ io.use(async (socket, next) => {
|
||||
next();
|
||||
});
|
||||
|
||||
const lockManager = new LockManager();
|
||||
|
||||
io.on("connection", async (socket) => {
|
||||
if (inactivityTimeout) clearTimeout(inactivityTimeout);
|
||||
|
||||
@ -119,6 +120,17 @@ io.on("connection", async (socket) => {
|
||||
}
|
||||
}
|
||||
|
||||
await lockManager.acquireLock(data.sandboxId, async () => {
|
||||
try {
|
||||
if (!containers[data.sandboxId]) {
|
||||
containers[data.sandboxId] = await Sandbox.create();
|
||||
console.log("Created container ", data.sandboxId);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error creating container ", data.sandboxId, error);
|
||||
}
|
||||
});
|
||||
|
||||
const sandboxFiles = await getSandboxFiles(data.sandboxId);
|
||||
sandboxFiles.fileData.forEach((file) => {
|
||||
const filePath = path.join(dirName, file.id);
|
||||
@ -320,41 +332,33 @@ io.on("connection", async (socket) => {
|
||||
callback(newFiles.files);
|
||||
});
|
||||
|
||||
socket.on("createTerminal", (id: string, callback) => {
|
||||
socket.on("createTerminal", async (id: string, callback) => {
|
||||
if (terminals[id] || Object.keys(terminals).length >= 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pty = spawn(os.platform() === "win32" ? "cmd.exe" : "bash", [], {
|
||||
name: "xterm",
|
||||
cols: 100,
|
||||
cwd: path.join(dirName, "projects", data.sandboxId),
|
||||
await lockManager.acquireLock(data.sandboxId, async () => {
|
||||
try {
|
||||
terminals[id] = await containers[data.sandboxId].terminal.start({
|
||||
onData: (data: string) => {
|
||||
io.emit("terminalResponse", { id, data });
|
||||
},
|
||||
size: { cols: 80, rows: 20 },
|
||||
onExit: () => console.log("Terminal exited", id),
|
||||
});
|
||||
await terminals[id].sendData("export PS1='user> '\rclear\r");
|
||||
console.log("Created terminal", id);
|
||||
} catch (error) {
|
||||
console.error("Error creating terminal ", id, error);
|
||||
}
|
||||
});
|
||||
|
||||
const onData = pty.onData((data) => {
|
||||
io.emit("terminalResponse", {
|
||||
id,
|
||||
data,
|
||||
});
|
||||
});
|
||||
|
||||
const onExit = pty.onExit((code) => console.log("exit :(", code));
|
||||
|
||||
pty.write("export PS1='\\u > '\r");
|
||||
pty.write("clear\r");
|
||||
|
||||
terminals[id] = {
|
||||
terminal: pty,
|
||||
onData,
|
||||
onExit,
|
||||
};
|
||||
|
||||
callback();
|
||||
});
|
||||
|
||||
socket.on("resizeTerminal", (dimensions: { cols: number; rows: number }) => {
|
||||
Object.values(terminals).forEach((t) => {
|
||||
t.terminal.resize(dimensions.cols, dimensions.rows);
|
||||
t.resize(dimensions);
|
||||
});
|
||||
});
|
||||
|
||||
@ -364,19 +368,18 @@ io.on("connection", async (socket) => {
|
||||
}
|
||||
|
||||
try {
|
||||
terminals[id].terminal.write(data);
|
||||
terminals[id].sendData(data);
|
||||
} catch (e) {
|
||||
console.log("Error writing to terminal", e);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("closeTerminal", (id: string, callback) => {
|
||||
socket.on("closeTerminal", async (id: string, callback) => {
|
||||
if (!terminals[id]) {
|
||||
return;
|
||||
}
|
||||
|
||||
terminals[id].onData.dispose();
|
||||
terminals[id].onExit.dispose();
|
||||
await terminals[id].kill();
|
||||
delete terminals[id];
|
||||
|
||||
callback();
|
||||
@ -430,12 +433,23 @@ io.on("connection", async (socket) => {
|
||||
socket.on("disconnect", async () => {
|
||||
if (data.isOwner) {
|
||||
Object.entries(terminals).forEach((t) => {
|
||||
const { terminal, onData, onExit } = t[1];
|
||||
onData.dispose();
|
||||
onExit.dispose();
|
||||
const terminal = t[1];
|
||||
terminal.kill();
|
||||
delete terminals[t[0]];
|
||||
});
|
||||
|
||||
await lockManager.acquireLock(data.sandboxId, async () => {
|
||||
try {
|
||||
if (containers[data.sandboxId]) {
|
||||
await containers[data.sandboxId].close();
|
||||
delete containers[data.sandboxId];
|
||||
console.log("Closed container", data.sandboxId);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error closing container ", data.sandboxId, error);
|
||||
}
|
||||
});
|
||||
|
||||
socket.broadcast.emit(
|
||||
"disableAccess",
|
||||
"The sandbox owner has disconnected."
|
||||
|
Reference in New Issue
Block a user