Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions combined.log
Original file line number Diff line number Diff line change
Expand Up @@ -323,3 +323,7 @@
{"level":"info","message":"Initial MongoDB connection established","timestamp":"2025-01-30T10:34:09.640Z"}
{"level":"info","message":"Server running on port 5000","timestamp":"2025-01-30T10:34:09.647Z"}
{"level":"info","message":"MongoDB disconnected","timestamp":"2025-01-30T11:15:21.609Z"}
{"level":"info","message":"Initial MongoDB connection established","timestamp":"2025-01-31T05:13:24.194Z"}
{"level":"info","message":"Server running on port 5000","timestamp":"2025-01-31T05:13:24.200Z"}
{"level":"info","message":"Initial MongoDB connection established","timestamp":"2025-01-31T08:29:46.579Z"}
{"level":"info","message":"Server running on port 5000","timestamp":"2025-01-31T08:29:46.588Z"}
245 changes: 86 additions & 159 deletions src/services/codeExecutionService.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,191 +5,118 @@ const crypto = require('crypto');
const os = require('os');
const tempDir = os.tmpdir();

exports.execute = async (code, language, input = '') => {
return new Promise((resolve, reject) => {
const uniqueId = crypto.randomBytes(16).toString('hex');
let tempFilePath;
let executionCommand;
let cleanupFiles = [];
let isCompiledLanguage = false;

switch (language.toLowerCase()) {
case 'python':
tempFilePath = path.join(tempDir, `temp_${uniqueId}.py`);
executionCommand = 'python';
cleanupFiles.push(tempFilePath);
break;

case 'javascript':
case 'js':
tempFilePath = path.join(tempDir, `temp_${uniqueId}.js`);
executionCommand = 'node';
cleanupFiles.push(tempFilePath);
break;

case 'java':
isCompiledLanguage = true;
const javaDir = path.join(tempDir, `java_${uniqueId}`);
fs.mkdirSync(javaDir);
tempFilePath = path.join(javaDir, 'Main.java');
cleanupFiles.push(javaDir);
break;

case 'cpp':
case 'c++':
isCompiledLanguage = true;
tempFilePath = path.join(tempDir, `temp_${uniqueId}.cpp`);
const executablePath = path.join(tempDir, `temp_${uniqueId}_exe`);
cleanupFiles.push(tempFilePath, executablePath);
break;

default:
return reject(new Error(`Unsupported language: ${language}`));
}

// Write code to file for all languages
fs.writeFileSync(tempFilePath, code);

if (isCompiledLanguage) {
switch (language.toLowerCase()) {
case 'java':
handleJavaExecution(tempFilePath, input, resolve, reject, cleanupFiles);
break;

case 'cpp':
case 'c++':
handleCppExecution(tempFilePath, input, resolve, reject, cleanupFiles);
break;
}
} else {
// Handle interpreted languages
const childProcess = spawn(executionCommand, [tempFilePath], { stdio: ['pipe', 'pipe', 'pipe'] });

let output = '';
let errorOutput = '';

childProcess.stdout.on('data', (data) => output += data.toString());
childProcess.stderr.on('data', (data) => errorOutput += data.toString());

childProcess.on('close', (code) => {
cleanup(cleanupFiles);
if (code !== 0) {
return reject(new Error(errorOutput || 'Process exited with an error.'));
}
resolve({ output: output.trim() });
});

if (input) {
childProcess.stdin.write(input + '\n');
childProcess.stdin.end();
}
}
});
// Configuration
const DOCKER_IMAGES = {
python: 'python:3-slim',
javascript: 'node:18-slim',
java: 'openjdk:17-jdk-slim',
cpp: 'gcc:latest'
};

// Java Execution Handler
function handleJavaExecution(javaPath, input, resolve, reject, cleanupFiles) {
const javaDir = path.dirname(javaPath);
let javaCode = fs.readFileSync(javaPath, 'utf-8');
const EXECUTION_TIMEOUT = 10000; // 10 seconds

// Extract the public class name using a regex
const classNameMatch = javaCode.match(/public\s+class\s+([A-Za-z_][A-Za-z0-9_]*)/);
if (!classNameMatch) {
cleanup(cleanupFiles);
return reject(new Error('No public class found in Java code.'));
exports.execute = async (code, language, input = '') => {
const lang = language.toLowerCase();
const uniqueId = crypto.randomBytes(16).toString('hex');
const tempFile = path.join(tempDir, `code_${uniqueId}`);

try {
fs.writeFileSync(tempFile, code);

const { command, args } = buildDockerCommand(lang, tempFile);
const result = await runDockerContainer(command, args, input);

return { output: result };
} catch (error) {
throw new Error(error.message);
} finally {
cleanup([tempFile]);
}
};

const className = classNameMatch[1]; // Extracted class name
const newJavaPath = path.join(javaDir, `${className}.java`);
function buildDockerCommand(lang, filePath) {
const volumeMount = `-v ${filePath}:/code`;

// Rename the Java file to match the class name
fs.renameSync(javaPath, newJavaPath);

const compileProcess = spawn('javac', [newJavaPath], { cwd: javaDir });

let compileError = '';
compileProcess.stderr.on('data', (data) => compileError += data.toString());

compileProcess.on('close', (compileCode) => {
if (compileCode !== 0) {
cleanup(cleanupFiles);
return reject(new Error(compileError || 'Java compilation failed'));
}
switch(lang) {
case 'python':
return {
command: 'docker',
args: ['run', '--rm', volumeMount, DOCKER_IMAGES.python, 'python', '/code']
};

case 'javascript':
case 'js':
return {
command: 'docker',
args: ['run', '--rm', volumeMount, DOCKER_IMAGES.javascript, 'node', '/code']
};

case 'java':
return {
command: 'docker',
args: [
'run', '--rm', volumeMount,
DOCKER_IMAGES.java,
'sh', '-c', 'javac /code && java -cp / $(basename /code .java)'
]
};

case 'cpp':
case 'c++':
return {
command: 'docker',
args: [
'run', '--rm', volumeMount,
DOCKER_IMAGES.cpp,
'sh', '-c', 'g++ -o /app /code && /app'
]
};

default:
throw new Error(`Unsupported language: ${lang}`);
}
}

const executeProcess = spawn('java', ['-cp', javaDir, className], { cwd: javaDir, stdio: ['pipe', 'pipe', 'pipe'] });
async function runDockerContainer(command, args, input) {
return new Promise((resolve, reject) => {
const child = spawn(command, args, { stdio: ['pipe', 'pipe', 'pipe'] });
let output = '';
let errorOutput = '';

executeProcess.stdout.on('data', (data) => output += data.toString());
executeProcess.stderr.on('data', (data) => errorOutput += data.toString());

executeProcess.on('close', (code) => {
cleanup(cleanupFiles);
if (code !== 0) {
reject(new Error(errorOutput || 'Java execution failed'));
} else {
resolve({ output: output.trim() });
}
});
let error = '';
let timeout = setTimeout(() => {
child.kill();
reject(new Error('Execution timed out'));
}, EXECUTION_TIMEOUT);

// Handle input
if (input) {
executeProcess.stdin.write(input + '\n');
executeProcess.stdin.end();
child.stdin.write(input + '\n');
child.stdin.end();
}
});
}


// C++ Execution Handler
function handleCppExecution(cppPath, input, resolve, reject, cleanupFiles) {
const executablePath = cppPath.replace('.cpp', '_exe');
const compileProcess = spawn('g++', [cppPath, '-o', executablePath]);

let compileError = '';
compileProcess.stderr.on('data', (data) => compileError += data.toString());
// Collect output
child.stdout.on('data', (data) => output += data.toString());
child.stderr.on('data', (data) => error += data.toString());

compileProcess.on('close', (compileCode) => {
if (compileCode !== 0) {
cleanup(cleanupFiles);
return reject(new Error(compileError || 'C++ compilation failed'));
}

const executeProcess = spawn(executablePath, [], { stdio: ['pipe', 'pipe', 'pipe'] });
let output = '';
let errorOutput = '';

executeProcess.stdout.on('data', (data) => output += data.toString());
executeProcess.stderr.on('data', (data) => errorOutput += data.toString());

executeProcess.on('close', (code) => {
cleanup(cleanupFiles);
// Handle completion
child.on('close', (code) => {
clearTimeout(timeout);
if (code !== 0) {
reject(new Error(errorOutput || 'C++ execution failed'));
reject(new Error(error || 'Container execution failed'));
} else {
resolve({ output: output.trim() });
resolve(output.trim());
}
});

if (input) {
executeProcess.stdin.write(input + '\n');
executeProcess.stdin.end();
}
});
}

// Cleanup function
function cleanup(files) {
files.forEach(file => {
try {
if (fs.existsSync(file)) {
if (fs.lstatSync(file).isDirectory()) {
fs.rmSync(file, { recursive: true, force: true });
} else {
fs.unlinkSync(file);
}
fs.unlinkSync(file);
}
} catch (e) {
console.error('Error cleaning up file:', e);
console.error('Cleanup error:', e);
}
});
}
Loading