diff --git a/combined.log b/combined.log index 7f57fce..e65e273 100644 --- a/combined.log +++ b/combined.log @@ -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"} diff --git a/src/services/codeExecutionService.js b/src/services/codeExecutionService.js index c3c97ad..402da6a 100644 --- a/src/services/codeExecutionService.js +++ b/src/services/codeExecutionService.js @@ -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); } }); } \ No newline at end of file