from flask import Flask, send_from_directory, jsonify, request, abort import os import importlib import inspect from OdenGraphQt import BaseNode # Determine the absolute path for the React build folder build_folder = os.path.join(os.getcwd(), "web-interface", "build") if not os.path.exists(build_folder): print("WARNING: web-interface build folder not found. Please run your React build process to generate 'web-interface/build'.") app = Flask(__name__, static_folder=build_folder, static_url_path="/") # Directory where nodes are stored NODES_PACKAGE = "Nodes" # In-memory workflow storage (a simple scaffold) workflow_data = { "nodes": [], "connections": [] } def import_nodes_from_folder(package_name): nodes_by_category = {} package = importlib.import_module(package_name) package_path = package.__path__[0] for root, _, files in os.walk(package_path): rel_path = os.path.relpath(root, package_path).replace(os.sep, ".") module_prefix = f"{package_name}.{rel_path}" if rel_path != '.' else package_name category_name = os.path.basename(root) for file in files: if file.endswith(".py") and file != "__init__.py": module_name = f"{module_prefix}.{file[:-3]}" try: module = importlib.import_module(module_name) for name, obj in inspect.getmembers(module, inspect.isclass): # Filter out only actual node classes you define: if (issubclass(obj, BaseNode) and obj.__module__ == module.__name__): if category_name not in nodes_by_category: nodes_by_category[category_name] = [] nodes_by_category[category_name].append(obj.NODE_NAME) except Exception as e: print(f"Failed to import {module_name}: {e}") return nodes_by_category @app.route("/") def serve_frontend(): index_path = os.path.join(build_folder, "index.html") if os.path.exists(index_path): return send_from_directory(app.static_folder, "index.html") else: return "
Please build the web-interface application.
", 404 @app.route("/api/nodes", methods=["GET"]) def get_available_nodes(): """Returns a list of available node categories and node types.""" nodes = import_nodes_from_folder(NODES_PACKAGE) return jsonify(nodes) @app.route("/api/workflow", methods=["GET"]) def get_workflow(): """Returns the current workflow data.""" return jsonify(workflow_data) @app.route("/api/workflow", methods=["POST"]) def save_workflow(): """Saves the workflow data (nodes and connections) sent from the UI.""" global workflow_data data = request.get_json() if not data: abort(400, "Invalid workflow data") workflow_data = data return jsonify({"status": "success", "workflow": workflow_data}) @app.route("/api/node", methods=["POST"]) def create_node(): """Creates a new node. Expects JSON with 'nodeType' and optionally 'position' and 'properties'.""" data = request.get_json() if not data or "nodeType" not in data: abort(400, "Invalid node data") node = { "id": len(workflow_data["nodes"]) + 1, # simple incremental ID "type": data["nodeType"], "position": data.get("position", {"x": 0, "y": 0}), "properties": data.get("properties", {}) } workflow_data["nodes"].append(node) return jsonify({"status": "success", "node": node}) @app.route("/api/node/