Initial commit
This commit is contained in:
3
requirements.txt
Normal file
3
requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
Flask==2.2.5
|
||||||
|
requests==2.26.0
|
||||||
|
beautifulsoup4==4.10.0
|
||||||
129
server.py
Normal file
129
server.py
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
from flask import Flask, request, jsonify
|
||||||
|
from multiprocessing import Pool, TimeoutError
|
||||||
|
import time
|
||||||
|
import requests
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
# ────── 1. one-time constants ──────────────────────────────────────────
|
||||||
|
PORT = int(os.environ.get('PORT', 5001))
|
||||||
|
TIMEOUT_SECONDS = 5
|
||||||
|
HTTP_STATUS_CODES = {
|
||||||
|
"OK": 200,
|
||||||
|
"BAD_REQUEST": 400,
|
||||||
|
"INTERNAL_SERVER_ERROR": 500,
|
||||||
|
}
|
||||||
|
|
||||||
|
States = {
|
||||||
|
"SUCCESS": "SUCCESS",
|
||||||
|
"NOT_A_FUNCTION": "NOT_A_FUNCTION",
|
||||||
|
"SCRIPT_ERROR": "ERROR",
|
||||||
|
"TIMEOUT": "TIMEOUT",
|
||||||
|
}
|
||||||
|
|
||||||
|
# ────── 2. execution worker ───────────────────────────────────────────
|
||||||
|
def execute_code(code, request_obj, environment):
|
||||||
|
"""
|
||||||
|
Executes the user's code in a restricted environment.
|
||||||
|
"""
|
||||||
|
logs = []
|
||||||
|
start_time = time.time()
|
||||||
|
|
||||||
|
def custom_print(*args, **kwargs):
|
||||||
|
# Concatenate all arguments into a single string, separated by spaces
|
||||||
|
output = ' '.join(map(str, args))
|
||||||
|
logs.append(output)
|
||||||
|
|
||||||
|
restricted_globals = {
|
||||||
|
'__builtins__': {
|
||||||
|
'print': custom_print,
|
||||||
|
# Whitelist safe builtins
|
||||||
|
'abs': abs, 'all': all, 'any': any, 'ascii': ascii, 'bin': bin,
|
||||||
|
'bool': bool, 'bytearray': bytearray, 'bytes': bytes, 'callable': callable,
|
||||||
|
'chr': chr, 'complex': complex, 'dict': dict, 'divmod': divmod,
|
||||||
|
'enumerate': enumerate, 'filter': filter, 'float': float, 'format': format,
|
||||||
|
'frozenset': frozenset, 'getattr': getattr, 'hasattr': hasattr, 'hash': hash,
|
||||||
|
'hex': hex, 'int': int, 'isinstance': isinstance, 'issubclass': issubclass,
|
||||||
|
'iter': iter, 'len': len, 'list': list, 'map': map, 'max': max,
|
||||||
|
'min': min, 'next': next, 'object': object, 'oct': oct, 'ord': ord,
|
||||||
|
'pow': pow, 'range': range, 'repr': repr, 'reversed': reversed,
|
||||||
|
'round': round, 'set': set, 'slice': slice, 'sorted': sorted,
|
||||||
|
'str': str, 'sum': sum, 'super': super, 'tuple': tuple, 'type': type,
|
||||||
|
'zip': zip
|
||||||
|
},
|
||||||
|
'request': request_obj,
|
||||||
|
'environment': environment,
|
||||||
|
'requests': requests,
|
||||||
|
'BeautifulSoup': BeautifulSoup,
|
||||||
|
'json': json
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Execute the code
|
||||||
|
exec(code, restricted_globals)
|
||||||
|
|
||||||
|
# Check if a function (e.g., main) is defined and call it
|
||||||
|
if 'main' in restricted_globals and callable(restricted_globals['main']):
|
||||||
|
result = restricted_globals['main'](request_obj, environment)
|
||||||
|
else:
|
||||||
|
# If no main function, maybe the script just runs top-level
|
||||||
|
result = None
|
||||||
|
|
||||||
|
execution_time = (time.time() - start_time) * 1000 # in milliseconds
|
||||||
|
return {
|
||||||
|
'status': States['SUCCESS'],
|
||||||
|
'result': result,
|
||||||
|
'logs': logs,
|
||||||
|
'environment': environment,
|
||||||
|
'execution_time': execution_time,
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
execution_time = (time.time() - start_time) * 1000 # in milliseconds
|
||||||
|
return {
|
||||||
|
'status': States['SCRIPT_ERROR'],
|
||||||
|
'result': str(e),
|
||||||
|
'logs': logs,
|
||||||
|
'environment': environment,
|
||||||
|
'execution_time': execution_time,
|
||||||
|
}
|
||||||
|
|
||||||
|
# ────── 3. process pool ────────────────────────────────────────────────
|
||||||
|
# It's generally better to initialize the pool once.
|
||||||
|
# For simplicity in this example, we'll create it on demand, but a real app should manage it.
|
||||||
|
# pool = Pool(processes=4)
|
||||||
|
|
||||||
|
# ────── 4. API surface ────────────────────────────────────────────────
|
||||||
|
@app.route("/execute", methods=['POST'])
|
||||||
|
def execute():
|
||||||
|
body = request.json
|
||||||
|
code = body.get('code', '')
|
||||||
|
request_obj = body.get('request', {})
|
||||||
|
environment = body.get('environment', {})
|
||||||
|
|
||||||
|
with Pool(processes=1) as pool:
|
||||||
|
async_result = pool.apply_async(execute_code, (code, request_obj, environment))
|
||||||
|
try:
|
||||||
|
payload = async_result.get(timeout=TIMEOUT_SECONDS)
|
||||||
|
except TimeoutError:
|
||||||
|
payload = {
|
||||||
|
'status': States['TIMEOUT'],
|
||||||
|
'result': f'Execution timed out after {TIMEOUT_SECONDS} seconds.',
|
||||||
|
'logs': [],
|
||||||
|
'environment': environment,
|
||||||
|
'execution_time': TIMEOUT_SECONDS * 1000,
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
payload = {
|
||||||
|
'status': States['SCRIPT_ERROR'],
|
||||||
|
'result': str(e),
|
||||||
|
'logs': [],
|
||||||
|
'environment': environment,
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonify(payload)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(host='0.0.0.0', port=PORT)
|
||||||
Reference in New Issue
Block a user