Skip to content

Oudys/Interactive_codeing_sandbox

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 

Repository files navigation

Interactive Coding Sandbox (Node.js + React)

An interactive, web-based coding playground where students can write and run Node.js (JavaScript) code directly in the browser.
The frontend is built with React + Vite + Monaco Editor, and the backend uses Node.js + Express + VM2 for secure, sandboxed execution.


1. Project structure

  • server/ – Node/Express API and sandbox execution
    • index.js – Express app with /api/execute endpoint using VM2
    • package.json – backend scripts and dependencies
  • client/ – React + Vite frontend
    • src/App.tsx – Monaco editor UI, console, and controls
    • src/App.css – layout and styling
    • src/main.tsx – React entry point

2. Prerequisites

  • Node.js (LTS recommended, e.g. 18+)
  • npm (comes with Node)

Make sure you run all commands from the correct folder as shown below.


3. Backend (server) – setup and run

From the project root:

cd server
npm install        # already done once, but safe to run again
npm start

This will:

{
  "code": "console.log('Hello from sandbox')"
}

and returns:

{
  "stdout": "Hello from sandbox",
  "stderr": "",
  "exitCode": 0,
  "runtimeMs": 5,
  "timeout": false
}

Security notes

  • Code is executed inside a VM2 NodeVM with:
    • console: 'redirect' – capture console.log
    • timeout: 2000 ms – limit long/infinite loops
    • eval, wasm, and require disabled
  • Request body size is limited and code length is checked before execution.

4. Frontend (client) – setup and run

In a separate terminal, from the project root:

cd client
npm install        # already done once, but safe to run again
npm run dev

Vite will print a URL like:

http://localhost:5173

Open that URL in your browser.


5. Using the sandbox

Once both server and client are running:

  1. Open the frontend in your browser.
  2. On the left, you’ll see:
    • A Monaco editor configured for JavaScript/Node.
    • An Examples dropdown (Hello World, Loops, Functions, Arrays).
    • Buttons:
      • Run (Ctrl+Enter) – sends the current code to /api/execute.
      • Clear Output – clears the console (does not clear code).
      • Reset Code – resets the editor to the selected example.
  3. On the right, you’ll see the Output console:
    • stdout – normal program output.
    • stderr – errors from the executed code.
    • system messages – status and execution time.

Additional behavior:

  • Code is automatically saved to localStorage under the key sandbox:lastCode.
  • When the page loads, it restores the last code if available; otherwise, it loads the default example.

6. API details (for reference)

POST /api/execute

  • URL: http://localhost:5000/api/execute
  • Method: POST
  • Headers: Content-Type: application/json
  • Body:
{
  "code": "// any JavaScript/Node.js code as a string"
}
  • Response (200 OK on success):
{
  "stdout": "string",    // joined console.log output
  "stderr": "string",    // error message if any
  "exitCode": 0,         // 0 on success, 1 on error
  "runtimeMs": 12,       // execution time in milliseconds
  "timeout": false       // true if the execution hit the timeout limit
}

On invalid input or internal errors, the API returns a suitable HTTP status and a simple JSON error message (e.g. { "error": "Code must be a non-empty string." }).


7. Detailed Frontend Behavior (what happens in the browser)

UI layout

  • The app is split into 2 panels:
    • Left: Monaco code editor + toolbar (Examples + Run/Clear/Reset).
    • Right: Output console that shows stdout, stderr, and system messages.

State maintained by src/App.tsx

  • code (string): current editor contents. Auto-saved to localStorage.
  • output (OutputLine[]): an ordered list of console lines to display in the right panel.
    • Each line is typed as:
      • stdout (normal console.log output)
      • stderr (errors/exceptions)
      • system (status messages like “Running code…” and runtime info)
  • isRunning (boolean): disables the Run button while waiting for the backend.
  • selectedExampleId (string): which predefined snippet is currently selected.

Example loading + reset behavior

  • The “Examples” dropdown is backed by a hard-coded array of example snippets.
  • Selecting an example:
    • updates selectedExampleId
    • loads the example’s code into the Monaco editor
    • writes a system message like Loaded example: Hello World
  • Clicking “Reset Code” reloads the selected example into the editor.

Running code (end-to-end request/response flow)

  1. User edits code in Monaco.

  2. User clicks Run (or presses Ctrl+Enter as mentioned in the UI text).

  3. The frontend validates basic state (e.g., code.trim() must not be empty).

  4. It sends this request to the backend:

    • POST http://localhost:5000/api/execute
    • JSON body: { "code": "<editor text>" }
  5. The frontend reads the JSON response and renders it to the output console:

    • If stdout is non-empty, it renders it as stdout lines.
    • If stderr is non-empty, it renders it as stderr lines.
    • It appends a system line with execution status + runtimeMs.

Auto-save behavior

  • On every code change, the app stores the latest code in:
    • localStorage["sandbox:lastCode"]
  • On first load:
    • if sandbox:lastCode exists, it restores it
    • otherwise it loads the default example

8. Detailed Backend Behavior (secure execution pipeline)

Endpoint

  • POST /api/execute
  • Expects JSON:
    • { "code": string }
  • Produces JSON result:
    • { stdout, stderr, exitCode, runtimeMs, timeout? }

Request validation (safety before execution)

The backend rejects requests when:

  • code is not a string or is empty/whitespace (400)
  • code.length is above the configured limit (413)

This prevents excessive payload sizes from reaching the sandbox.

Sandbox execution mechanism (VM2)

Backend runs user code inside a VM2 NodeVM configured as:

  • console: 'redirect'
    • allows the backend to intercept console.log(...)
    • intercepted logs are joined and returned as stdout
  • timeout: 2000
    • enforces a maximum execution time inside the VM2 runtime
  • eval: false, wasm: false
    • blocks some dynamic/advanced execution paths
  • require: false
    • disables module loading from user code

How console.log is captured

  • The server creates a logs[] array.
  • It registers a vm.on('console.log', ...) handler.
  • When the user code calls console.log, the handler:
    • converts arguments to a single string
    • pushes into logs[]
  • At the end, the backend returns:
    • stdout = logs.join('\n')

Error handling & output mapping

  • The server uses try/catch around vm.run(code, 'user-code.js').
  • If an exception occurs:
    • stderr becomes String(error)
    • exitCode becomes 1
  • If no exception occurs:
    • stderr is returned as an empty string
    • exitCode becomes 0

Timeout flag behavior

  • VM2 is configured with timeout: 2000.
  • The response includes a timeout field computed using the measured runtime:
    • timeout: runtimeMs >= 2000
  • Note: this flag is an approximation based on runtime measurement.

9. How to explain/test for a reviewer (quick checklist)

  1. Start backend:
    • cd server && npm start
    • confirm server prints a message like Sandbox server listening on port 5000
  2. Start frontend:
    • cd client && npm run dev
    • open the Vite URL in the browser
  3. Pick an example (Hello World) and click Run.
  4. Confirm:
    • output appears in the right console
    • errors appear as stderr when code throws
  5. Test a timeout scenario (example: an infinite loop):
    • verify the system shows a timeout-like result (runtime near 2000ms and/or an error).

10. Future Improvements (optional)

  • Add more predefined examples (async/await, Promises, error handling, data structures).
  • Add a snippets manager (save named programs, list and load them).
  • Add rate limiting + request logging on the backend.
  • Add stronger timeout labeling (detect VM2 timeout exception specifically).
  • Package with Docker for easier deployment.

11. Troubleshooting

  • Frontend cannot reach the server
    • Ensure backend is running on http://localhost:5000.
    • Check firewall/port conflicts.
  • Blank page or React errors
    • Ensure npm install finished successfully in client/.
    • Restart npm run dev after dependency changes.

If you want, I can also add a “Security Considerations” section tailored to your reviewer (what VM2 blocks, what it does not, and why timeouts matter).

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors