Implemented ReactJS Application Server with basic functionality.

This commit is contained in:
Nicole Rappe 2025-03-20 02:43:13 -06:00
parent 5e6ad27f71
commit cffad033fa
11 changed files with 240 additions and 0 deletions

3
Data/WebUI/Canary.txt Normal file
View File

@ -0,0 +1,3 @@
This file exists to show that the custom reactjs code is being copied into the reactJS app before building it.
Additional code to flesh out the application will be added in the future.

25
Data/WebUI/src/App.js Normal file
View File

@ -0,0 +1,25 @@
import logo from './logo.svg';
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Borealis Workflow Automation Tool
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;

117
Data/server.py Normal file
View File

@ -0,0 +1,117 @@
from flask import Flask, send_from_directory, jsonify, request, abort
import os
import importlib
import inspect
# 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):
"""
Recursively import all modules from the given package.
Returns a dictionary where keys are subfolder names, and values are lists of node names.
"""
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):
if hasattr(obj, "NODE_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 "<h1>React App Not Found</h1><p>Please build the web-interface application.</p>", 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/<int:node_id>", methods=["DELETE"])
def delete_node(node_id):
"""Deletes the node with the given ID."""
global workflow_data
nodes = workflow_data["nodes"]
workflow_data["nodes"] = [n for n in nodes if n["id"] != node_id]
return jsonify({"status": "success", "deletedNode": node_id})
@app.route("/api/node/<int:node_id>", methods=["PUT"])
def update_node(node_id):
"""Updates an existing node's position or properties."""
data = request.get_json()
if not data:
abort(400, "Invalid node data")
for node in workflow_data["nodes"]:
if node["id"] == node_id:
node["position"] = data.get("position", node["position"])
node["properties"] = data.get("properties", node["properties"])
return jsonify({"status": "success", "node": node})
abort(404, "Node not found")
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000, debug=True)

95
Launch-Borealis.ps1 Normal file
View File

@ -0,0 +1,95 @@
# Start_Windows - WebServer.ps1
# Run this script with:
# Set-ExecutionPolicy Unrestricted -Scope Process; .\Start_Windows -WebServer.ps1
# --- Check for Node.js ---
if (-not (Get-Command node -ErrorAction SilentlyContinue)) {
Write-Error "Node.js is not installed. Please install Node.js and try again."
exit 1
}
# --- Define paths ---
$venvFolder = "Borealis-Workflow-Automation-Tool" # Virtual environment root.
$dataSource = "Data" # Contains server.py and optionally WebUI.
$dataDestination = "$venvFolder\Borealis" # Data folder copy destination in the venv.
$customUIPath = "$dataSource\WebUI" # Custom UI folder source in the project root.
$webUIDestination = "$venvFolder\web-interface" # Destination for the React UI in the venv.
# --- Create virtual environment if needed ---
if (!(Test-Path "$venvFolder\Scripts\Activate")) {
Write-Output "Creating virtual environment in '$venvFolder'..."
python -m venv $venvFolder
}
# --- Copy Data folder (includes server.py) ---
if (Test-Path $dataSource) {
Write-Output "Copying Data folder into virtual environment..."
if (Test-Path $dataDestination) {
Remove-Item -Recurse -Force $dataDestination
}
New-Item -Path $dataDestination -ItemType Directory -Force | Out-Null
Copy-Item -Path "$dataSource\*" -Destination $dataDestination -Recurse
} else {
Write-Output "Warning: Data folder not found, skipping copy."
}
# --- React UI Deployment ---
# Check if the React UI folder exists in the virtual environment root.
if (-not (Test-Path $webUIDestination)) {
Write-Output "React UI folder '$webUIDestination' not found. Generating default ReactJS app there..."
npx create-react-app $webUIDestination
} else {
Write-Output "React UI folder '$webUIDestination' already exists."
}
# If a custom UI folder exists at <ProjectRoot>\Data\WebUI, copy its contents over (overwrite defaults).
if (Test-Path $customUIPath) {
Write-Output "Custom UI folder found at '$customUIPath'. Overwriting default React app with Borealis-related files..."
Copy-Item -Path "$customUIPath\*" -Destination $webUIDestination -Recurse -Force
} else {
Write-Output "No custom UI folder found at '$customUIPath'. Using default ReactJS app."
}
# --- Activate virtual environment ---
Write-Output "Activating virtual environment..."
. "$venvFolder\Scripts\Activate"
# --- Install Python dependencies ---
if (Test-Path "requirements.txt") {
Write-Output "Installing Python dependencies..."
pip install -q -r requirements.txt
} else {
Write-Output "No requirements.txt found, skipping Python dependency installation."
}
# --- Build the React (UI) app in the web-interface folder if needed ---
$packageJsonPath = Join-Path $webUIDestination "package.json"
$buildFolder = Join-Path $webUIDestination "build"
if (-not (Test-Path $buildFolder)) {
if (Test-Path $packageJsonPath) {
Write-Output "React UI build not found in '$webUIDestination'. Installing dependencies and building the React app..."
Push-Location $webUIDestination
npm install
npm run build
Pop-Location
}
else {
Write-Output "No package.json found in '$webUIDestination'; skipping npm install/build."
}
} else {
Write-Output "React UI build found. Skipping npm install/build."
}
# --- Change directory to the virtual environment root ---
Push-Location $venvFolder
Write-Output "Current working directory: $(Get-Location)"
$expectedBuildPath = Join-Path (Get-Location) "web-interface\build"
Write-Output "Looking for build folder at: $expectedBuildPath"
# --- Start the Flask server (server.py is inside Borealis folder) ---
Write-Output "Starting the Flask server..."
python "Borealis\server.py"
# --- Return to original directory and deactivate virtual environment ---
Pop-Location
deactivate