diff --git a/app.py b/app.py index 2a54fd4..c8b82e4 100644 --- a/app.py +++ b/app.py @@ -48,7 +48,7 @@ app.register_blueprint(auth, url_prefix='/auth') # https://stackoverflow.com/questions/76886643/linking-two-not-exposed-dokku-apps NODE_API_URL = os.environ.get('NODE_API_URL', 'http://isolator.web:5000/execute') DENO_API_URL = os.environ.get('DENO_API_URL', 'http://deno-isolator.web:5000/execute') - +PYTHON_API_URL = os.environ.get('PYTHON_API_URL', 'http://python-isolator.web:5000/execute') def map_isolator_response_to_flask_response(response): """ @@ -84,7 +84,12 @@ async def execute_code(): # Extract code and convert request to a format acceptable by Node.js app code = request.json.get('code') runtime = request.json.get('runtime', 'node') # Default to node - api_url = DENO_API_URL if runtime == 'deno' else NODE_API_URL + if runtime == 'deno': + api_url = DENO_API_URL + elif runtime == 'python': + api_url = PYTHON_API_URL + else: + api_url = NODE_API_URL request_obj = { 'method': request.method, @@ -181,7 +186,12 @@ async def execute_http_function(user_id, function): request_data['text'] = request.data.decode('utf-8') # Call the Node.js API asynchronously - api_url = DENO_API_URL if runtime == 'deno' else NODE_API_URL + if runtime == 'deno': + api_url = DENO_API_URL + elif runtime == 'python': + api_url = PYTHON_API_URL + else: + api_url = NODE_API_URL async with aiohttp.ClientSession() as session: async with session.post(api_url, json={'code': code, 'request': request_data, 'environment': environment, 'name': function_name}) as response: response_data = await response.json() diff --git a/routes/http.py b/routes/http.py index 91262f1..96c1f7c 100644 --- a/routes/http.py +++ b/routes/http.py @@ -99,6 +99,11 @@ DEFAULT_ENVIRONMENT = """{ "lines": 3 }""" +DEFAULT_PYTHON_SCRIPT = """def main(request, environment): + print(f"Method: {request['method']}") + return {"body": "Hello from Python!"} +""" + http = Blueprint('http', __name__) @http.route("/overview", methods=["GET"]) @@ -117,9 +122,19 @@ def overview(): def new(): user_id = current_user.id if request.method == "GET": + context = { + 'user_id': user_id, + 'name': 'foo', + 'script': DEFAULT_SCRIPT, + 'default_python_script': DEFAULT_PYTHON_SCRIPT, + 'environment_info': DEFAULT_ENVIRONMENT, + 'is_public': False, + 'log_request': True, + 'log_response': False + } if htmx: - return render_block(environment, 'dashboard/http_functions/new.html', 'page', user_id=user_id, name='foo', script=DEFAULT_SCRIPT, environment_info=DEFAULT_ENVIRONMENT, is_public=False, log_request=True, log_response=False) - return render_template("dashboard/http_functions/new.html", user_id=user_id, name='foo', script=DEFAULT_SCRIPT, environment_info=DEFAULT_ENVIRONMENT, is_public=False, log_request=True, log_response=False) + return render_block(environment, 'dashboard/http_functions/new.html', 'page', **context) + return render_template("dashboard/http_functions/new.html", **context) try: name = request.json.get('name') script_content = request.json.get('script_content') diff --git a/static/js/mithril/editor.js b/static/js/mithril/editor.js index f674e4c..32dfb45 100644 --- a/static/js/mithril/editor.js +++ b/static/js/mithril/editor.js @@ -83,7 +83,9 @@ const Editor = { this.editorJS = ace.edit("js-editor"); this.editorJS.setOptions({ maxLines: 100 }); this.editorJS.setTheme("ace/theme/github_dark"); - this.editorJS.session.setMode("ace/mode/javascript"); + this.editorJS.session.setMode( + this.runtime === "python" ? "ace/mode/python" : "ace/mode/javascript" + ); this.editorJS.setValue(this.jsValue, -1); this.editorJS.session.on("change", () => { @@ -397,61 +399,51 @@ const Editor = { // Right side: Runtime toggle and Execute button m("div", { class: "flex items-center space-x-4" }, [ - // Runtime Toggle Switch + // Runtime Dropdown m( - "label", + "select", { - for: "runtime-toggle", - class: "inline-flex items-center cursor-pointer", + key: "runtime-selector", + class: + "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500", + onchange: (e) => { + this.runtime = e.target.value; + this.editorJS.session.setMode( + this.runtime === "python" + ? "ace/mode/python" + : "ace/mode/javascript" + ); + }, }, [ m( - "span", - { - class: - "mr-3 text-sm font-medium " + - (this.runtime === "node" - ? "text-green-500" - : "text-gray-400 dark:text-gray-500"), - }, + "option", + { value: "node", selected: this.runtime === "node" }, "Node" ), - m("div", { class: "relative" }, [ - m("input[type=checkbox]", { - id: "runtime-toggle", - class: "sr-only peer", - checked: this.runtime === "deno", - onchange: (e) => { - this.runtime = e.target.checked ? "deno" : "node"; - }, - }), - m("div", { - class: - "w-11 h-6 bg-gray-200 rounded-full peer peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600", - }), - ]), m( - "span", - { - class: - "ml-3 text-sm font-medium " + - (this.runtime === "deno" - ? "text-blue-500" - : "text-gray-400 dark:text-gray-500"), - }, + "option", + { value: "deno", selected: this.runtime === "deno" }, "Deno" ), + m( + "option", + { value: "python", selected: this.runtime === "python" }, + "Python" + ), ] ), this.executeLoading ? m("div", { + key: "spinner", class: "animate-spin h-6 w-6 border-4 border-green-300 border-t-transparent rounded-full", }) : m( "button", { + key: "execute-button", class: "p-2 rounded-full hover:bg-gray-200 text-green-700", onclick: () => this.execute(), title: "Execute", diff --git a/templates/dashboard/http_functions/new.html b/templates/dashboard/http_functions/new.html index 17f8df0..9a5eead 100644 --- a/templates/dashboard/http_functions/new.html +++ b/templates/dashboard/http_functions/new.html @@ -24,6 +24,7 @@ title='New HTTP Function') view: () => m(Editor, { name: '{{ name }}', jsValue: {{ script | tojson | safe }}, + pythonValue: {{ default_python_script | tojson | safe }}, jsonValue: {{ environment_info | tojson | safe }}, isEdit: false, isAdd: true,