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
46 changes: 46 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Code Executor Battle tester

on:
push:
branches:
- main
pull_request:
branches: ["*"]

jobs:
build-and-test:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "20" # Or your preferred Node.js version

- name: Install Node.js dependencies
run: npm install

- name: Set up Docker BuildX (for multi-platform builds, good practice)
uses: docker/setup-buildx-action@v3

- name: Build Docker images
# This builds all services defined in your docker-compose.yaml
# It's crucial to build them before running tests.
run: docker compose build

- name: Run Vitest Battle Tests
run: echo "this will handle letter"
# # 1. Start all Docker Compose services (including your Node.js server and language executors).
# # 2. Run the Vitest tests, which will make HTTP requests to your Node.js server.
# # 3. The tests will internally use `docker run --rm` against your language executor images.
# # 4. After tests complete, the `afterAll` hook in your tests will tear down Docker Compose.
# env:
# # Pass HOST_PROJECT_ROOT to your Node.js server if it needs it during tests.
# # In GitHub Actions, GITHUB_WORKSPACE is the root of your checked-out repo.
# HOST_PROJECT_ROOT: ${{ github.workspace }}
# # Ensure your Node.js server uses the correct port for testing
# PORT: 9091
# run: npm test
5 changes: 5 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## Contributing rules will be here

1. Go to the language config file from the `root/language/config.js`
2. Add your langage as we add others language
- Make sure your `cmd` is perfect
62 changes: 55 additions & 7 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import { LANGUAGE_CONFIG } from "./language/config.js";
import { exec as executeProcess } from "child_process";
import { allowOrigins } from "./origin/index.js";
import { extractError } from "./utils/index.js";
import http from "http";
import { Server as SocketServer } from "socket.io";
import NodeCache from "node-cache";

const exec = promisify(executeProcess);
const fs = fileSystem.promises;
dotenv.config();
Expand All @@ -20,9 +24,46 @@ app.use(
origin: allowOrigins,
})
);
const server = http.createServer(app);
const io = new SocketServer(server, {
cors: {
origin: allowOrigins,
methods: ["GET", "POST"],
},
pingTimeout: 60000, // 60 seconds
pingInterval: 25000, // 25 seconds
connectTimeout: 45000, // 45 seconds
});

const cache = new NodeCache({
stdTTL: 60 * 60, // Cache TTL of 1 hour
});
/** socket setup to display all current active users length */
io.on("connection", (socket) => {
const coders = cache.get("active_coders");
console.log(coders);
if (!cache.has(socket.id)) {
coders
? cache.set("active_coders", [...coders, socket.id])
: cache.set("active_coders", [socket.id]);
}
emitActiveCoder();
socket.on("disconnect", () => {
const coders = cache.get("active_coders");
if (coders) {
cache.set(
"active_coders",
coders.filter((coder) => coder !== socket.id)
);
}

emitActiveCoder();
});
});

// which will be passed from docker-compose.yml
/* which will be passed from docker-compose.yaml */
const HOST_PROJECT_ROOT = process.env.HOST_PROJECT_ROOT;
/** Project root directory */
const directory_name = process.cwd();

app.post("/run", async (req, res) => {
Expand All @@ -39,30 +80,33 @@ app.post("/run", async (req, res) => {
if (!langConfig)
return res.status(400).json({ error: `Unsupported language: ${language}` });

// Generate a unique name for the temporary directory
/* Generate a unique name for the temporary directory */
const tempDirName = uuidv4();
// This is the path *inside the nodejs-server container* where the code will be written
/* This is the path *inside the nodejs-server container* where the code will be written */
const tempDirInsideNodeServer = path.join(
directory_name,
"temp",
tempDirName
);
/* code file path where our code will be store */
const codeFilePath = path.join(tempDirInsideNodeServer, langConfig.mainFile);

// This is the absolute path *on the host machine* that corresponds to tempDirInsideNodeServer.
// We use HOST_PROJECT_ROOT environment variable to construct this absolute path.
/** 1. This is the absolute path **on the host machine** that corresponds to tempDirInsideNodeServer.
2. We use HOST_PROJECT_ROOT environment variable to construct this absolute path.
*/
if (!HOST_PROJECT_ROOT) {
console.error(
"HOST_PROJECT_ROOT environment variable is not set. Volume mounts might fail."
);
return res.status(500).json({
error: "Server configuration error",
details:
"HOST_PROJECT_ROOT environment variable is missing. Please ensure it's set in docker-compose.yaml.",
"HOST_PROJECT_ROOT environment variable is missing. Please ensure it's set in docker-compose.yaml",
isDeveloper: true,
});
}
const hostVolumePath = path.join(HOST_PROJECT_ROOT, "temp", tempDirName);
console.log("host: ", hostVolumePath);

try {
// 1. Create the temporary directory inside the nodejs-server container
Expand Down Expand Up @@ -113,7 +157,11 @@ app.use((req, res) => {
res.type("txt").send("404 not found");
}
});
function emitActiveCoder() {
const activeUsers = cache.get("active_coders");
io.emit("active_coders", activeUsers);
}
const PORT = process.env.PORT || 9091;
app.listen(PORT, () => {
server.listen(PORT, () => {
console.log(`Code executor backend listening on port ${PORT}`);
});
Binary file added browsers/screenshot_executeme.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 12 additions & 7 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,15 @@

## v0.0.2 Releases 🎉

- **TypeScript** Language support with `denoland`
- Extracted actual error from the whole error
- Readable and user-friendly output details error
- Reduce execution time
- Root routes and invalid path view `html` page setup
- Improve websites code complex issue
- Use server action to hide **API endpoint**
- [feat] **Added kotlin language support**
- [feat] **TypeScript** Language support with `denoland`
- [chore] Extracted actual error from the whole error
- [chore] Readable and user-friendly output details error
- [chore] Reduce execution time
- [feat] Root routes and invalid path view `html` page setup
- [refact] Improve websites code complex issue
- [feat] Use server action to hide **API endpoint**

- [feat] View live online coders with in-memory cache i backend
- [feat] shows a floating button for displaying online coders
- [fix] Fixed mobile responsive issues
6 changes: 6 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ services:
dockerfile: docker/Dockerfile.jvm
image: executor-java:latest

kotlin-image:
build:
context: .
dockerfile: docker/Dockerfile.kotlin
image: executor-kotlin:latest

nodejs-server-image:
build:
context: .
Expand Down
1 change: 0 additions & 1 deletion docker/Dockerfile.deno
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,3 @@ FROM denoland/deno:alpine-1.44.1

WORKDIR /app

# Entrypoint command will be overridden at runtime
27 changes: 27 additions & 0 deletions docker/Dockerfile.kotlin
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Use a slim OpenJDK 21 image as the base, same as your Java setup
FROM openjdk:21-jdk-slim

# Set the working directory inside the container.
# User's Kotlin source code will be mounted into this directory.
WORKDIR /app

# Install necessary tools: curl for downloading, unzip for extracting.
# This works for Debian-based slim images.
RUN apt-get update && apt-get install -y curl unzip && rm -rf /var/lib/apt/lists/*

# Define Kotlin version as an argument for easier updates
ARG KOTLIN_VERSION=1.9.20
# NOTE: As of 2025-07-02, 1.9.20 is a good stable choice.
# You might want to update this to the absolute latest stable version if needed.

# Download, extract, and clean up the Kotlin compiler
RUN curl -L https://github.com/JetBrains/kotlin/releases/download/v${KOTLIN_VERSION}/kotlin-compiler-${KOTLIN_VERSION}.zip -o /tmp/kotlin-compiler.zip \
&& unzip /tmp/kotlin-compiler.zip -d /opt/kotlin \
&& rm /tmp/kotlin-compiler.zip

# Add the Kotlin compiler's 'bin' directory to the PATH
ENV PATH="/opt/kotlin/kotlinc/bin:${PATH}"

# This CMD keeps the container running quietly when started by `docker-compose up`.
# It does NOT affect the actual execution when your Node.js server uses `docker run`.
CMD ["tail", "-f", "/dev/null"]
4 changes: 2 additions & 2 deletions docker/Dockerfile.python
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
FROM python:3.12-alpine
WORKDIR /app
# CMD will be overridden or provided by the mount

WORKDIR /app
File renamed without changes.
3 changes: 3 additions & 0 deletions example/index.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fun main() {
println("Hello, World!")
}
File renamed without changes.
File renamed without changes.
5 changes: 5 additions & 0 deletions language/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,9 @@ export const LANGUAGE_CONFIG = {
mainFile: "Main.java",
cmd: 'sh -c "javac Main.java && java Main"',
},
kotlin: {
image: "executor-kotlin",
mainFile: "index.kt",
cmd: 'sh -c "kotlinc index.kt -include-runtime -d index.jar && java -jar index.jar"',
},
};
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"description": "A robust and secure backend service for executing user-submitted code in isolated Docker containers. Supports multiple programming languages like Python, Node.js, and Java, with resource limiting for safe execution. Ideal for online compilers, coding platforms, and educational tools.",
"scripts": {
"dev": "nodemon app.js",
"test": "vitest run --reporter verbose --pool forks",
"start": "node --trace-warnings app.js"
},
"repository": {
Expand Down Expand Up @@ -43,12 +44,15 @@
"dependencies": {
"cors": "^2.8.5",
"express": "^5.1.0",
"node-cache": "^5.1.2",
"socket.io": "^4.8.1",
"util": "^0.12.5",
"uuid": "^11.1.0"
},
"devDependencies": {
"dotenv": "^16.6.0",
"nodemon": "^3.1.10"
"nodemon": "^3.1.10",
"vitest": "^3.2.4"
},
"funding": [
{
Expand Down
Loading