|
1 | 1 | import express from 'express'; |
2 | | -import { exec } from 'child_process'; |
| 2 | +// import cors from 'cors'; |
| 3 | +import { exec as rawExec } from 'child_process'; |
| 4 | +import util from 'util'; |
3 | 5 | import fs from 'fs'; |
4 | 6 | import { fileURLToPath } from 'url'; |
5 | 7 | import path from 'path'; |
6 | 8 |
|
| 9 | + |
7 | 10 | // Simulate __dirname |
8 | 11 | const __filename = fileURLToPath(import.meta.url); |
9 | 12 | const __dirname = path.dirname(__filename); |
10 | 13 |
|
11 | 14 | const app = express(); |
12 | | -app.use(express.json()); |
13 | | - |
| 15 | +// app.use(express.json()); |
| 16 | +app.use(express.json({ limit: '256kb' })); |
| 17 | +const exec = util.promisify(rawExec); |
14 | 18 | // Define paths for pre-created files |
15 | 19 | const tempDir = path.join(__dirname, 'temp'); |
16 | 20 | const languageConfigs = { |
@@ -161,60 +165,125 @@ app.get('/',(req,res)=>{ |
161 | 165 | // }); |
162 | 166 |
|
163 | 167 |
|
164 | | -app.post(['/execute', '/submit-code'], async (req, res) => { |
165 | | - const { code, language } = req.body; |
166 | | - if (!languageConfigs[language]) { |
167 | | - return res.status(400).json({ error: 'Unsupported language' }); |
168 | | - } |
| 168 | +// app.post(['/execute', '/submit-code'], async (req, res) => { |
| 169 | +// const { code, language } = req.body; |
| 170 | +// if (!languageConfigs[language]) { |
| 171 | +// return res.status(400).json({ error: 'Unsupported language' }); |
| 172 | +// } |
| 173 | + |
| 174 | +// const { filePath } = languageConfigs[language]; |
| 175 | +// fs.writeFileSync(filePath, code); |
| 176 | + |
| 177 | +// let command = ''; |
| 178 | +// const filename = path.basename(filePath); |
| 179 | + |
| 180 | +// switch (language) { |
| 181 | +// case 'cpp': |
| 182 | +// command = `/usr/bin/time -f "Execution Time: %e seconds\nMemory Used: %M KB" g++ ${filePath} -o ${tempDir}/a.out && ${tempDir}/a.out`; |
| 183 | +// break; |
| 184 | +// case 'python': |
| 185 | +// command = `python3 ${filePath}`; |
| 186 | +// break; |
| 187 | +// case 'js': |
| 188 | +// command = `node ${filePath}`; |
| 189 | +// break; |
| 190 | +// case 'java': |
| 191 | +// const className = filename.replace('.java', ''); |
| 192 | +// command = `javac ${filePath} && java -cp ${tempDir} ${className}`; |
| 193 | +// break; |
| 194 | +// default: |
| 195 | +// return res.status(400).json({ error: 'Unsupported language' }); |
| 196 | +// } |
| 197 | + |
| 198 | +// exec(command, (err, stdout, stderr) => { |
| 199 | +// if (err || stderr.includes('error')) { |
| 200 | +// return res.status(400).json({ |
| 201 | +// error: stderr.split('\n') |
| 202 | +// .filter(line => !line.includes('Execution Time') && !line.includes('Memory Used')) |
| 203 | +// .join('\n').trim(), |
| 204 | +// output: 'Test case 1: Failed', |
| 205 | +// }); |
| 206 | +// } |
169 | 207 |
|
170 | | - const { filePath } = languageConfigs[language]; |
171 | | - fs.writeFileSync(filePath, code); |
| 208 | +// const executionTime = (stderr.match(/Execution Time: (.+?) seconds/) || [])[1] || 'N/A'; |
| 209 | +// const memoryUsage = (stderr.match(/Memory Used: (.+?) KB/) || [])[1] || 'N/A'; |
172 | 210 |
|
173 | | - let command = ''; |
174 | | - const filename = path.basename(filePath); |
| 211 | +// res.status(200).json({ |
| 212 | +// output: stdout.trim(), |
| 213 | +// executionTime, |
| 214 | +// memoryUsage, |
| 215 | +// }); |
| 216 | +// }); |
| 217 | +// }); |
175 | 218 |
|
| 219 | +/* ---------- helpers ---------- */ |
| 220 | +function buildCommand(filePath, language) { |
176 | 221 | switch (language) { |
177 | 222 | case 'cpp': |
178 | | - command = `/usr/bin/time -f "Execution Time: %e seconds\nMemory Used: %M KB" g++ ${filePath} -o ${tempDir}/a.out && ${tempDir}/a.out`; |
179 | | - break; |
| 223 | + return `/usr/bin/time -f "Execution Time: %e seconds\nMemory Used: %M KB" \ |
| 224 | +g++ ${filePath} -o ${tempDir}/a.out && ${tempDir}/a.out`; |
180 | 225 | case 'python': |
181 | | - command = `python3 ${filePath}`; |
182 | | - break; |
| 226 | + return `python3 ${filePath}`; |
183 | 227 | case 'js': |
184 | | - command = `node ${filePath}`; |
185 | | - break; |
186 | | - case 'java': |
187 | | - const className = filename.replace('.java', ''); |
188 | | - command = `javac ${filePath} && java -cp ${tempDir} ${className}`; |
189 | | - break; |
| 228 | + return `node ${filePath}`; |
| 229 | + case 'java': { |
| 230 | + const className = path.basename(filePath, '.java'); |
| 231 | + return `javac ${filePath} && java -cp ${tempDir} ${className}`; |
| 232 | + } |
190 | 233 | default: |
191 | | - return res.status(400).json({ error: 'Unsupported language' }); |
| 234 | + throw new Error('Unsupported language'); |
192 | 235 | } |
| 236 | +} |
| 237 | + |
| 238 | +const pick = (s, re) => (s.match(re) || [])[1] || 'N/A'; |
| 239 | +const clean = s => |
| 240 | + s.split('\n') |
| 241 | + .filter(l => !/Execution Time|Memory Used/.test(l)) |
| 242 | + .join('\n') |
| 243 | + .trim(); |
| 244 | + |
| 245 | +/* ---------- ONE handler used by both endpoints ---------- */ |
| 246 | +async function runCode(req, res) { |
| 247 | + const { code = '', language = '' } = req.body || {}; |
| 248 | + |
| 249 | + /* 1. validate language ------------------------------------------------ */ |
| 250 | + const cfg = languageConfigs[language]; |
| 251 | + if (!cfg) return res.status(400).json({ error: 'Unsupported language' }); |
| 252 | + |
| 253 | + /* 2. write code to its temp file -------------------------------------- */ |
| 254 | + await fs.promises.mkdir(tempDir, { recursive: true }); |
| 255 | + await fs.promises.writeFile(cfg.filePath, code); |
| 256 | + |
| 257 | + /* 3. build the shell command ----------------------------------------- */ |
| 258 | + const cmd = buildCommand(cfg.filePath, language); |
| 259 | + |
| 260 | + /* 4. exec with hard limits ------------------------------------------- */ |
| 261 | + try { |
| 262 | + const { stdout, stderr } = await exec(cmd, { |
| 263 | + timeout: 10_000, // ⏱️ kill after 10 s |
| 264 | + maxBuffer: 1_048_576 // 1 MiB stdout+stderr |
| 265 | + }); |
193 | 266 |
|
194 | | - exec(command, (err, stdout, stderr) => { |
195 | | - if (err || stderr.includes('error')) { |
196 | | - return res.status(400).json({ |
197 | | - error: stderr.split('\n') |
198 | | - .filter(line => !line.includes('Execution Time') && !line.includes('Memory Used')) |
199 | | - .join('\n').trim(), |
200 | | - output: 'Test case 1: Failed', |
201 | | - }); |
202 | | - } |
203 | | - |
204 | | - const executionTime = (stderr.match(/Execution Time: (.+?) seconds/) || [])[1] || 'N/A'; |
205 | | - const memoryUsage = (stderr.match(/Memory Used: (.+?) KB/) || [])[1] || 'N/A'; |
206 | | - |
207 | | - res.status(200).json({ |
| 267 | + return res.json({ |
208 | 268 | output: stdout.trim(), |
209 | | - executionTime, |
210 | | - memoryUsage, |
| 269 | + executionTime: pick(stderr, /Execution Time:\s*(.+?)\s*seconds/), |
| 270 | + memoryUsage: pick(stderr, /Memory Used:\s*(.+?)\s*KB/) |
211 | 271 | }); |
212 | | - }); |
213 | | -}); |
| 272 | + } catch (err) { |
| 273 | + /* timeout → err.killed === true, compile/runtime error → err.code */ |
| 274 | + const status = err.killed ? 408 : 400; |
| 275 | + const stderr = err.stderr || err.message || ''; |
| 276 | + return res.status(status).json({ error: clean(stderr) }); |
| 277 | + } |
| 278 | +} |
214 | 279 |
|
| 280 | +/* ---------- the two routes ------------------------------------------- */ |
| 281 | +app.post('/execute', runCode); |
| 282 | +app.post('/submit-code', runCode); |
215 | 283 |
|
216 | 284 |
|
217 | | -const PORT = 8080; |
| 285 | +// const PORT = 8080; |
| 286 | +const PORT = process.env.PORT || 8080; |
218 | 287 | app.listen(PORT, '0.0.0.0', () => |
219 | 288 | console.log(`Server listening on ${PORT}`) |
220 | 289 | ); |
0 commit comments