fix: wait until the owner is disconnected from all sockets to close terminals and file manager
This commit is contained in:
parent
3ad7e5d9bc
commit
e229dab826
@ -46,61 +46,81 @@ export class Sandbox {
|
|||||||
socket: Socket;
|
socket: Socket;
|
||||||
sandboxId: string;
|
sandboxId: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
|
isOwner: boolean;
|
||||||
|
|
||||||
constructor(sandboxId: string, userId: string, { aiWorker, dokkuClient, gitClient, socket }: ServerContext) {
|
constructor(sandboxId: string, userId: string, isOwner: boolean, { aiWorker, dokkuClient, gitClient, socket }: ServerContext) {
|
||||||
this.fileManager = null;
|
this.fileManager = null;
|
||||||
this.terminalManager = null;
|
this.terminalManager = null;
|
||||||
this.container = null;
|
this.container = null;
|
||||||
this.sandboxId = sandboxId;
|
this.sandboxId = sandboxId;
|
||||||
this.userId = userId;
|
this.userId = userId;
|
||||||
|
this.isOwner = isOwner;
|
||||||
this.aiWorker = aiWorker;
|
this.aiWorker = aiWorker;
|
||||||
this.dokkuClient = dokkuClient;
|
this.dokkuClient = dokkuClient;
|
||||||
this.gitClient = gitClient;
|
this.gitClient = gitClient;
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initializes the container for the sandbox environment
|
||||||
async initializeContainer() {
|
async initializeContainer() {
|
||||||
|
// Acquire a lock to ensure exclusive access to the sandbox environment
|
||||||
await lockManager.acquireLock(this.sandboxId, async () => {
|
await lockManager.acquireLock(this.sandboxId, async () => {
|
||||||
|
// Check if a container already exists and is running
|
||||||
if (this.container && await this.container.isRunning()) {
|
if (this.container && await this.container.isRunning()) {
|
||||||
console.log(`Found existing container ${this.sandboxId}`)
|
console.log(`Found existing container ${this.sandboxId}`)
|
||||||
} else {
|
} else {
|
||||||
console.log("Creating container", this.sandboxId)
|
console.log("Creating container", this.sandboxId)
|
||||||
|
// Create a new container with a specified timeout
|
||||||
this.container = await E2BSandbox.create({
|
this.container = await E2BSandbox.create({
|
||||||
timeoutMs: CONTAINER_TIMEOUT,
|
timeoutMs: CONTAINER_TIMEOUT,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
// Ensure a container was successfully created
|
||||||
if (!this.container) throw new Error("Failed to create container")
|
if (!this.container) throw new Error("Failed to create container")
|
||||||
|
|
||||||
|
// Initialize the terminal manager if it hasn't been set up yet
|
||||||
if (!this.terminalManager) {
|
if (!this.terminalManager) {
|
||||||
this.terminalManager = new TerminalManager(this.container)
|
this.terminalManager = new TerminalManager(this.container)
|
||||||
console.log(`Terminal manager set up for ${this.sandboxId}`)
|
console.log(`Terminal manager set up for ${this.sandboxId}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize the file manager if it hasn't been set up yet
|
||||||
if (!this.fileManager) {
|
if (!this.fileManager) {
|
||||||
this.fileManager = new FileManager(
|
this.fileManager = new FileManager(
|
||||||
this.sandboxId,
|
this.sandboxId,
|
||||||
this.container,
|
this.container,
|
||||||
(files: (TFolder | TFile)[]) => {
|
(files: (TFolder | TFile)[]) => {
|
||||||
|
// Emit an event to the socket when files are loaded
|
||||||
this.socket.emit("loaded", files)
|
this.socket.emit("loaded", files)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
// Initialize the file manager and emit the initial files
|
||||||
this.fileManager.initialize()
|
this.fileManager.initialize()
|
||||||
this.socket.emit("loaded", this.fileManager.files)
|
this.socket.emit("loaded", this.fileManager.files)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Called when the client disconnects from the Sandbox
|
||||||
async disconnect() {
|
async disconnect() {
|
||||||
|
// Close all terminals managed by the terminal manager
|
||||||
await this.terminalManager?.closeAllTerminals()
|
await this.terminalManager?.closeAllTerminals()
|
||||||
|
// This way the terminal manager will be set up again if we reconnect
|
||||||
|
this.terminalManager = null;
|
||||||
|
// Close all file watchers managed by the file manager
|
||||||
await this.fileManager?.closeWatchers()
|
await this.fileManager?.closeWatchers()
|
||||||
|
// This way the file manager will be set up again if we reconnect
|
||||||
|
this.fileManager = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
handlers() {
|
handlers() {
|
||||||
|
|
||||||
// Handle heartbeat from a socket connection
|
// Handle heartbeat from a socket connection
|
||||||
const handleHeartbeat: SocketHandler = (_: any) => {
|
const handleHeartbeat: SocketHandler = (_: any) => {
|
||||||
this.container?.setTimeout(CONTAINER_TIMEOUT)
|
// Only keep the sandbox alive if the owner is still connected
|
||||||
|
if (this.isOwner) {
|
||||||
|
this.container?.setTimeout(CONTAINER_TIMEOUT)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle getting a file
|
// Handle getting a file
|
||||||
|
@ -112,10 +112,12 @@ io.on("connection", async (socket) => {
|
|||||||
const sandboxManager = sandboxes[data.sandboxId] ?? new Sandbox(
|
const sandboxManager = sandboxes[data.sandboxId] ?? new Sandbox(
|
||||||
data.sandboxId,
|
data.sandboxId,
|
||||||
data.userId,
|
data.userId,
|
||||||
|
data.isOwner,
|
||||||
{ aiWorker, dokkuClient, gitClient, socket }
|
{ aiWorker, dokkuClient, gitClient, socket }
|
||||||
)
|
)
|
||||||
|
|
||||||
// Initialize the sandbox container
|
// Initialize the sandbox container
|
||||||
|
// The file manager and terminal managers will be set up if they have been closed
|
||||||
sandboxManager.initializeContainer()
|
sandboxManager.initializeContainer()
|
||||||
|
|
||||||
// Register event handlers for the sandbox
|
// Register event handlers for the sandbox
|
||||||
@ -134,15 +136,15 @@ io.on("connection", async (socket) => {
|
|||||||
try {
|
try {
|
||||||
if (data.isOwner) {
|
if (data.isOwner) {
|
||||||
connections.ownerDisconnected(data.sandboxId)
|
connections.ownerDisconnected(data.sandboxId)
|
||||||
}
|
// If the owner has disconnected from all sockets, close open terminals and file watchers.o
|
||||||
|
// The sandbox itself will timeout after the heartbeat stops.
|
||||||
await sandboxManager.disconnect()
|
if (!connections.ownerIsConnected(data.sandboxId)) {
|
||||||
|
await sandboxManager.disconnect()
|
||||||
if (data.isOwner && !connections.ownerIsConnected(data.sandboxId)) {
|
socket.broadcast.emit(
|
||||||
socket.broadcast.emit(
|
"disableAccess",
|
||||||
"disableAccess",
|
"The sandbox owner has disconnected."
|
||||||
"The sandbox owner has disconnected."
|
)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
handleErrors("Error disconnecting:", e, socket);
|
handleErrors("Error disconnecting:", e, socket);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user