| const Docker = require('dockerode'); |
| const fs = require('fs'); |
|
|
| let docker; |
| try { |
| docker = new Docker(); |
| } catch (e) { |
| console.warn('Docker not available — container features disabled'); |
| } |
|
|
| async function getOrCreateContainer(projectId, userId, workDir) { |
| if (!docker) throw new Error('Docker not available'); |
|
|
| const name = `indus-${projectId}`; |
|
|
| |
| try { |
| const container = docker.getContainer(name); |
| const info = await container.inspect(); |
| if (info.State.Running) return container; |
| await container.start(); |
| return container; |
| } catch (e) { |
| |
| } |
|
|
| |
| fs.mkdirSync(workDir, { recursive: true }); |
|
|
| |
| const container = await docker.createContainer({ |
| Image: 'indus-base:latest', |
| name, |
| Tty: true, |
| OpenStdin: true, |
| WorkingDir: '/workspace', |
| HostConfig: { |
| Memory: 512 * 1024 * 1024, |
| MemorySwap: 512 * 1024 * 1024, |
| CpuQuota: 50000, |
| NetworkMode: 'bridge', |
| Binds: [`${workDir}:/workspace`], |
| AutoRemove: false, |
| }, |
| }); |
|
|
| await container.start(); |
| console.log(`Container created: ${name}`); |
| return container; |
| } |
|
|
| async function stopContainer(projectId) { |
| if (!docker) return; |
| try { |
| const container = docker.getContainer(`indus-${projectId}`); |
| await container.stop({ t: 5 }); |
| console.log(`Container stopped: indus-${projectId}`); |
| } catch (e) { } |
| } |
|
|
| async function removeContainer(projectId) { |
| if (!docker) return; |
| try { |
| const container = docker.getContainer(`indus-${projectId}`); |
| await container.remove({ force: true }); |
| console.log(`Container removed: indus-${projectId}`); |
| } catch (e) { } |
| } |
|
|
| async function execInContainer(projectId, command) { |
| if (!docker) throw new Error('Docker not available'); |
|
|
| const container = docker.getContainer(`indus-${projectId}`); |
| const exec = await container.exec({ |
| Cmd: ['bash', '-c', command], |
| AttachStdout: true, |
| AttachStderr: true, |
| }); |
|
|
| const stream = await exec.start({ Detach: false }); |
| return new Promise((resolve, reject) => { |
| let output = ''; |
| stream.on('data', (chunk) => { output += chunk.toString(); }); |
| stream.on('end', () => resolve(output)); |
| stream.on('error', reject); |
| }); |
| } |
|
|
| module.exports = { |
| getOrCreateContainer, |
| stopContainer, |
| removeContainer, |
| execInContainer, |
| }; |
|
|