Spaces:
Build error
Build error
subham
Sync local n8n deployment: refactor to single Dockerfile, add runners, sandbox, cleanup legacy compose/nginx
4c98a98 Download scripts/sandbox-api.js from Subham9126/n8n: direct link, hf CLI and curl.
- Browser
- Download file 3.53 kB
-
https://huggingface.co/spaces/Subham9126/n8n/resolve/main/scripts/sandbox-api.js
- Command line
-
hf download hf://spaces/Subham9126/n8n/scripts/sandbox-api.js
-
curl -L -o sandbox-api.js https://huggingface.co/spaces/Subham9126/n8n/resolve/main/scripts/sandbox-api.js
3.53 kB
| // sandbox-api.js — minimal HF-compatible sandbox API matching n8n-sandbox contract | |
| // n8n talks to http://127.0.0.1:8080 (N8N_SANDBOX_SERVICE_URL) with header x-api-key | |
| const http = require('http'); | |
| const { URL } = require('url'); | |
| const PORT = parseInt(process.env.SANDBOX_API_PORT || '8080', 10); | |
| const RUNNER_GRPC = process.env.SANDBOX_RUNNER_GRPC || '127.0.0.1:9091'; | |
| const API_KEYS = (process.env.SANDBOX_API_KEYS || process.env.N8N_SANDBOX_SERVICE_API_KEY || '').split(',').map(s=>s.trim()).filter(Boolean); | |
| const RUNNER_URL = `http://127.0.0.1:${(RUNNER_GRPC.split(':')[1]||'9091')}`; // http fallback for our JS runner | |
| function authorized(req) { | |
| if (API_KEYS.length===0) return true; | |
| const k = req.headers['x-api-key'] || req.headers['authorization']?.replace(/^Bearer\s+/,''); | |
| return API_KEYS.includes(k); | |
| } | |
| function json(res, code, obj){ res.writeHead(code, {'Content-Type':'application/json'}); res.end(JSON.stringify(obj)); } | |
| const server = http.createServer(async (req,res)=>{ | |
| const url = new URL(req.url, `http://localhost:${PORT}`); | |
| // health | |
| if (url.pathname === '/healthz' || url.pathname === '/health') { | |
| res.writeHead(200, {'Content-Type':'text/plain'}); res.end('ok\n'); return; | |
| } | |
| if (!authorized(req)) { json(res,401,{error:'unauthorized'}); return; } | |
| // n8n sandbox service expects POST /v1/sandbox or POST /execute — we support both | |
| // Docs: N8N_SANDBOX_SERVICE_URL=http://sandbox-api:8080 — n8n does POST /execute or /v1/... | |
| // Implement simple execute endpoint: | |
| if ((url.pathname === '/execute' || url.pathname === '/v1/execute' || url.pathname === '/v1/sandbox' || url.pathname === '/run') && req.method==='POST') { | |
| let body=''; req.on('data',c=>body+=c); req.on('end', async()=>{ | |
| try{ | |
| const data = body ? JSON.parse(body) : {}; | |
| // Support both {code, language} and n8n format {code, language, context?} | |
| const code = data.code || data.content || ''; | |
| const language = (data.language || data.lang || 'python').toLowerCase(); | |
| const timeoutMs = data.timeoutMs || 30000; | |
| if (!code) { json(res,400,{error:'missing code'}); return; } | |
| // Forward to sandbox-runner (our JS runner) via HTTP POST /run | |
| const runnerBody = JSON.stringify({code, language, timeoutMs}); | |
| const r = await new Promise((resolve,reject)=>{ | |
| const rq = http.request({hostname:'127.0.0.1', port: RUNNER_URL.split(':').pop(), path:'/run', method:'POST', headers:{'Content-Type':'application/json','Content-Length':Buffer.byteLength(runnerBody)}}, (rr)=>{ | |
| let b=''; rr.on('data',c=>b+=c); rr.on('end',()=>resolve({status:rr.statusCode, body:b})); | |
| }); | |
| rq.on('error',reject); | |
| rq.write(runnerBody); rq.end(); | |
| }).catch(e=>({status:500, body: JSON.stringify({error: e.message})})); | |
| // Try parse runner response | |
| let out; | |
| try{ out = JSON.parse(r.body);}catch{ out={stdout:r.body, stderr:'', exitCode: r.status===200?0:1};} | |
| json(res,200, out); | |
| }catch(e){ json(res,500,{error:e.message}); } | |
| }); | |
| return; | |
| } | |
| // list/info | |
| if (url.pathname === '/' && req.method==='GET'){ json(res,200,{service:'sandbox-api', mode:'rootless-bwrap', runner: RUNNER_GRPC}); return; } | |
| json(res,404,{error:'not found', path:url.pathname}); | |
| }); | |
| server.listen(PORT, '127.0.0.1', ()=>console.log(`[sandbox-api] listening 127.0.0.1:${PORT} runner=${RUNNER_GRPC}`)); | |
| process.on('SIGTERM',()=>server.close(()=>process.exit(0))); | |