Skip to content

Commit d77961b

Browse files
[feat 🚀] typescript support with denoland
1 parent cbc1d7c commit d77961b

21 files changed

Lines changed: 423 additions & 232 deletions

.dockerignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,7 @@ Dockerfile
1111
.dockerignore
1212
temp
1313
.env
14+
15+
# Ignore website and browser
16+
browsers
17+
websites

.prettierrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
22
"semi": true,
3-
"singleQuote": true,
3+
"singleQuote": false,
44
"tabWidth": 2
55
}

Dockerfile.deno

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
FROM denoland/deno:alpine-1.44.1
2+
3+
WORKDIR /app
4+
5+
# Entrypoint command will be overridden at runtime

Dockerfile.nodejs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
11
FROM node:20-alpine
2+
3+
# Set working directory
24
WORKDIR /app
3-
# CMD will be overridden or provided by the mount
5+
6+
# Install TypeScript globally
7+
RUN npm install -g typescript
8+
9+
# Optionally, create tsconfig.json here if needed

Main.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
class Main {
2-
public static void main(String[] args){
3-
System.out.println('Hello World!');
2+
public static void main(String[] args) {
3+
for (int i = 1; i <= 10; i++) {
4+
System.out.println("Number: " + i);
5+
}
46
}
57
}

app.js

Lines changed: 39 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,93 +1,76 @@
1-
const express = require('express');
2-
const fs = require('fs').promises;
3-
const util = require('util');
4-
const exec = util.promisify(require('child_process').exec);
5-
const path = require('path');
6-
const { v4: uuidv4 } = require('uuid');
7-
const dotenv = require('dotenv');
1+
import fileSystem from "fs";
2+
import express from "express";
3+
import dotenv from "dotenv";
4+
import { v4 as uuidv4 } from "uuid";
5+
import path from "path";
6+
import { promisify } from "util";
7+
import cors from "cors";
8+
import { LANGUAGE_CONFIG } from "./language/config.js";
9+
import { exec as executeProcess } from "child_process";
10+
import { allowOrigins } from "./origin/index.js";
11+
import { extractError } from "./utils/index.js";
12+
const exec = promisify(executeProcess);
13+
const fs = fileSystem.promises;
814
dotenv.config();
915

1016
const app = express();
1117
app.use(express.json());
12-
13-
// Mapping of language to Docker image and main file name
14-
const LANGUAGE_CONFIG = {
15-
python: {
16-
image: 'executor-python',
17-
mainFile: 'main.py',
18-
cmd: 'python main.py', // Command to execute the main file
19-
},
20-
nodejs: {
21-
image: 'executor-nodejs',
22-
mainFile: 'index.js',
23-
cmd: 'node index.js',
24-
},
25-
java: {
26-
image: 'executor-java',
27-
mainFile: 'Main.java',
28-
cmd: 'sh -c "javac Main.java && java Main"',
29-
},
30-
};
18+
app.use(
19+
cors({
20+
origin: allowOrigins,
21+
})
22+
);
3123

3224
// which will be passed from docker-compose.yml
3325
const HOST_PROJECT_ROOT = process.env.HOST_PROJECT_ROOT;
26+
const directory_name = process.cwd();
3427

35-
app.post('/run', async (req, res) => {
28+
app.post("/run", async (req, res) => {
3629
const { code, language } = req.body;
3730

3831
if (!code || !language) {
3932
return res
4033
.status(400)
41-
.json({ error: 'Code and language are required in the request body.' });
34+
.json({ error: "Code and language are required in the request body." });
4235
}
4336

4437
const langConfig = LANGUAGE_CONFIG[language];
38+
4539
if (!langConfig)
4640
return res.status(400).json({ error: `Unsupported language: ${language}` });
4741

4842
// Generate a unique name for the temporary directory
4943
const tempDirName = uuidv4();
5044
// This is the path *inside the nodejs-server container* where the code will be written
51-
const tempDirInsideNodeServer = path.join(__dirname, 'temp', tempDirName);
45+
const tempDirInsideNodeServer = path.join(
46+
directory_name,
47+
"temp",
48+
tempDirName
49+
);
5250
const codeFilePath = path.join(tempDirInsideNodeServer, langConfig.mainFile);
5351

5452
// This is the absolute path *on the host machine* that corresponds to tempDirInsideNodeServer.
5553
// We use HOST_PROJECT_ROOT environment variable to construct this absolute path.
5654
if (!HOST_PROJECT_ROOT) {
5755
console.error(
58-
'HOST_PROJECT_ROOT environment variable is not set. Volume mounts might fail.'
56+
"HOST_PROJECT_ROOT environment variable is not set. Volume mounts might fail."
5957
);
6058
return res.status(500).json({
61-
error: 'Server configuration error',
59+
error: "Server configuration error",
6260
details:
63-
"HOST_PROJECT_ROOT environment variable is missing. Please ensure it's set in docker-compose.yml.",
61+
"HOST_PROJECT_ROOT environment variable is missing. Please ensure it's set in docker-compose.yaml.",
62+
isDeveloper: true,
6463
});
6564
}
66-
const hostVolumePath = path.join(HOST_PROJECT_ROOT, 'temp', tempDirName);
67-
68-
console.log(`[Executor] Request for ${language} received.`);
69-
console.log(
70-
`[Executor] tempDirInsideNodeServer (inside Node.js container): ${tempDirInsideNodeServer}`
71-
);
72-
console.log(
73-
`[Executor] codeFilePath (inside Node.js container): ${codeFilePath}`
74-
);
75-
console.log(`[Executor] HOST_PROJECT_ROOT (from env): ${HOST_PROJECT_ROOT}`);
76-
console.log(
77-
`[Executor] hostVolumePath (for host Docker daemon): ${hostVolumePath}`
78-
);
65+
const hostVolumePath = path.join(HOST_PROJECT_ROOT, "temp", tempDirName);
7966

8067
try {
8168
// 1. Create the temporary directory inside the nodejs-server container
82-
// (This directory will also appear on the host due to the volume mount configured in docker-compose.yml)
69+
// (This directory will also appear on the host due to the volume mount configured in docker-compose.yaml)
8370
await fs.mkdir(tempDirInsideNodeServer, { recursive: true });
84-
console.log(
85-
`[Executor] Created temp directory: ${tempDirInsideNodeServer}`
86-
);
8771

8872
// 2. Write the user's code to the temporary file
8973
await fs.writeFile(codeFilePath, code);
90-
console.log(`[Executor] Wrote code to: ${codeFilePath}`);
9174

9275
// 3. Prepare and execute the Docker command
9376
// The -v flag uses the hostVolumePath (absolute path on host) as the source,
@@ -99,45 +82,26 @@ app.post('/run', async (req, res) => {
9982
const { stdout, stderr } = await exec(runCommand);
10083

10184
if (stderr) {
102-
console.error(
103-
`[Executor] Runtime error for ${language} code in ${tempDirInsideNodeServer}: ${stderr}`
104-
);
10585
return res.status(500).json({
106-
error: 'Runtime error',
107-
details: stderr,
86+
error: "Runtime error",
87+
details: extractError(stderr),
10888
});
10989
}
11090
return res.status(200).json({
11191
output: stdout,
11292
});
11393
} catch (err) {
114-
console.error(`[Executor] Error processing request: ${err.message}`);
11594
return res.status(500).json({
116-
error: 'Server error',
117-
details: err.message,
95+
error: "Internal Server error",
96+
details: extractError(err.message),
11897
});
11998
} finally {
120-
// Ensure cleanup happens whether the execution was successful or an error occurred.
121-
// This is a more robust way to ensure temp files are removed.
122-
try {
123-
await fs.rm(tempDirInsideNodeServer, { recursive: true, force: true });
124-
console.log(
125-
`[Executor] Cleaned up temp directory: ${tempDirInsideNodeServer}`
126-
);
127-
} catch (cleanupErr) {
128-
console.error(
129-
`[Executor] Error cleaning up ${tempDirInsideNodeServer}:`,
130-
cleanupErr
131-
);
132-
}
99+
await fs.rm(tempDirInsideNodeServer, { recursive: true, force: true });
133100
}
134101
});
135102

136103
// Use PORT from environment variables, or default to 6000
137-
const PORT = process.env.PORT || 6000;
104+
const PORT = process.env.PORT || 5000;
138105
app.listen(PORT, () => {
139106
console.log(`Code executor backend listening on port ${PORT}`);
140-
console.log(
141-
"Ensure your base Docker images (e.g., 'executor-python', 'executor-nodejs', 'executor-java') are pre-built."
142-
);
143107
});

docker-compose.yaml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ services:
55
dockerfile: Dockerfile.nodejs
66
image: executor-nodejs:latest
77

8+
deno-image:
9+
build:
10+
context: .
11+
dockerfile: Dockerfile.deno
12+
image: executor-deno:latest
13+
814
python-image:
915
build:
1016
context: .
@@ -26,7 +32,7 @@ services:
2632
- PORT=${PORT}
2733
- HOST_PROJECT_ROOT=${PWD}
2834
ports:
29-
- '6000:6000'
35+
- "6000:6000"
3036
volumes:
3137
- /var/run/docker.sock:/var/run/docker.sock
3238
- ./temp:/app/temp

index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
const myname: string = "sabbir";
2+
console.log(myname);

language/config.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
export const LANGUAGE_CONFIG = {
2+
python: {
3+
image: "executor-python",
4+
mainFile: "main.py",
5+
cmd: "python main.py", // Command to execute the main file
6+
},
7+
nodejs: {
8+
image: "executor-nodejs",
9+
mainFile: "index.js",
10+
cmd: "node index.js",
11+
},
12+
typescript: {
13+
image: "executor-deno",
14+
mainFile: "index.ts",
15+
cmd: "deno run --allow-all index.ts",
16+
},
17+
java: {
18+
image: "executor-java",
19+
mainFile: "Main.java",
20+
cmd: 'sh -c "javac Main.java && java Main"',
21+
},
22+
};

origin/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export const allowOrigins = [
2+
"http://localhost:3000",
3+
"https://executeme.vercel.app",
4+
];

0 commit comments

Comments
 (0)