Refa: improve usability of Node.js/JavaScript code executor (#8979)

### What problem does this PR solve?

Improve usability of Node.js/JavaScript code executor.

### Type of change

- [x] Refactoring

---------

Co-authored-by: Kevin Hu <kevinhu.sh@gmail.com>
Co-authored-by: writinwaters <93570324+writinwaters@users.noreply.github.com>
This commit is contained in:
Yongteng Lei
2025-07-23 09:26:09 +08:00
committed by GitHub
parent f63ad6b725
commit c3b8d8b4ba
5 changed files with 75 additions and 13 deletions

View File

@ -213,6 +213,42 @@ To add Node.js dependencies:
--- ---
## Usage
### 🐍 A Python example
```python
def main(arg1: str, arg2: str) -> str:
return f"result: {arg1 + arg2}"
```
### 🟨 JavaScript examples
A simple sync function
```javascript
function main({arg1, arg2}) {
return arg1+arg2
}
```
Async funcion with aioxs
```javascript
const axios = require('axios');
async function main() {
try {
const response = await axios.get('https://github.com/infiniflow/ragflow');
return 'Body:' + response.data;
} catch (error) {
return 'Error:' + error.message;
}
}
```
---
## 📋 FAQ ## 📋 FAQ
### ❓Sandbox Not Working? ### ❓Sandbox Not Working?

View File

@ -15,24 +15,29 @@
# #
import base64 import base64
from core.container import _CONTAINER_EXECUTION_SEMAPHORES
from core.logger import logger from core.logger import logger
from fastapi import Request from fastapi import Request
from models.enums import ResultStatus from models.enums import ResultStatus, SupportLanguage
from models.schemas import CodeExecutionRequest, CodeExecutionResult from models.schemas import CodeExecutionRequest, CodeExecutionResult
from services.execution import execute_code from services.execution import execute_code
from services.limiter import limiter from services.limiter import limiter
from services.security import analyze_code_security from services.security import analyze_code_security
from core.container import _CONTAINER_EXECUTION_SEMAPHORES
async def healthz_handler(): async def healthz_handler():
return {"status": "ok"} return {"status": "ok"}
@limiter.limit("5/second") @limiter.limit("5/second")
async def run_code_handler(req: CodeExecutionRequest, request: Request): async def run_code_handler(req: CodeExecutionRequest, request: Request):
logger.info("🟢 Received /run request") logger.info("🟢 Received /run request")
async with _CONTAINER_EXECUTION_SEMAPHORES[req.language]: async with _CONTAINER_EXECUTION_SEMAPHORES[req.language]:
code = base64.b64decode(req.code_b64).decode("utf-8") code = base64.b64decode(req.code_b64).decode("utf-8")
if req.language == SupportLanguage.NODEJS:
code += "\n\nmodule.exports = { main };"
req.code_b64 = base64.b64encode(code.encode("utf-8")).decode("utf-8")
is_safe, issues = analyze_code_security(code, language=req.language) is_safe, issues = analyze_code_security(code, language=req.language)
if not is_safe: if not is_safe:
issue_details = "\n".join([f"Line {lineno}: {issue}" for issue, lineno in issues]) issue_details = "\n".join([f"Line {lineno}: {issue}" for issue, lineno in issues])

View File

@ -21,3 +21,4 @@ router = APIRouter()
router.get("/healthz")(healthz_handler) router.get("/healthz")(healthz_handler)
router.post("/run")(run_code_handler) router.post("/run")(run_code_handler)

View File

@ -26,7 +26,7 @@ from core.logger import logger
_CONTAINER_QUEUES: dict[SupportLanguage, Queue] = {} _CONTAINER_QUEUES: dict[SupportLanguage, Queue] = {}
_CONTAINER_LOCK: asyncio.Lock = asyncio.Lock() _CONTAINER_LOCK: asyncio.Lock = asyncio.Lock()
_CONTAINER_EXECUTION_SEMAPHORES:dict[SupportLanguage,asyncio.Semaphore] = {} _CONTAINER_EXECUTION_SEMAPHORES: dict[SupportLanguage, asyncio.Semaphore] = {}
async def init_containers(size: int) -> tuple[int, int]: async def init_containers(size: int) -> tuple[int, int]:

View File

@ -82,20 +82,40 @@ const fs = require('fs');
const path = require('path'); const path = require('path');
const args = JSON.parse(process.argv[2]); const args = JSON.parse(process.argv[2]);
const mainPath = path.join(__dirname, 'main.js'); const mainPath = path.join(__dirname, 'main.js');
function isPromise(value) {
return Boolean(value && typeof value.then === 'function');
}
if (fs.existsSync(mainPath)) { if (fs.existsSync(mainPath)) {
const { main } = require(mainPath); const mod = require(mainPath);
const main = typeof mod === 'function' ? mod : mod.main;
if (typeof main !== 'function') {
console.error('Error: main is not a function');
process.exit(1);
}
if (typeof args === 'object' && args !== null) { if (typeof args === 'object' && args !== null) {
main(args).then(result => { try {
if (result !== null) { const result = main(args);
console.log(result); if (isPromise(result)) {
result.then(output => {
if (output !== null) {
console.log(output);
}
}).catch(err => {
console.error('Error in async main function:', err);
});
} else {
if (result !== null) {
console.log(result);
}
} }
}).catch(err => { } catch (err) {
console.error('Error in main function:', err); console.error('Error when executing main:', err);
}); }
} else { } else {
console.error('Error: args is not a valid object:', args); console.error('Error: args is not a valid object:', args);
} }