From a71f188a734f14ae03ee6f8a9dd21148331518db Mon Sep 17 00:00:00 2001 From: Nicole Rappe Date: Thu, 27 Mar 2025 17:41:51 -0600 Subject: [PATCH] Implemented Linux Support --- Data/WebUI/public/index.html | 43 +++++ Data/WebUI/src/App.js | 215 +++++++++++++---------- Data/WebUI/src/components/FlowEditor.css | 6 +- Data/WebUI/src/components/FlowEditor.jsx | 88 ++++++---- Launch-Borealis.sh | 185 +++++++++++++++++++ Start_Linux.sh | 113 ------------ 6 files changed, 401 insertions(+), 249 deletions(-) create mode 100644 Data/WebUI/public/index.html create mode 100644 Launch-Borealis.sh delete mode 100644 Start_Linux.sh diff --git a/Data/WebUI/public/index.html b/Data/WebUI/public/index.html new file mode 100644 index 0000000..da117a9 --- /dev/null +++ b/Data/WebUI/public/index.html @@ -0,0 +1,43 @@ + + + + + + + + + + + + + Borealis + + + +
+ + + diff --git a/Data/WebUI/src/App.js b/Data/WebUI/src/App.js index 87576be..a26f26b 100644 --- a/Data/WebUI/src/App.js +++ b/Data/WebUI/src/App.js @@ -1,118 +1,139 @@ import React from "react"; import FlowEditor from "./components/FlowEditor"; import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; - import { - AppBar, - Toolbar, - Typography, - Box, - Menu, - MenuItem, - Button, - CssBaseline, - ThemeProvider, - createTheme + AppBar, + Toolbar, + Typography, + Box, + Menu, + MenuItem, + Button, + CssBaseline, + ThemeProvider, + createTheme } from "@mui/material"; const darkTheme = createTheme({ - palette: { - mode: "dark", - background: { - default: "#121212", - paper: "#1e1e1e" - }, - text: { - primary: "#ffffff" - } + palette: { + mode: "dark", + background: { + default: "#121212", + paper: "#1e1e1e" + }, + text: { + primary: "#ffffff" } + } }); export default function App() { - // Separate menu state for each dropdown - const [workflowsAnchorEl, setWorkflowsAnchorEl] = React.useState(null); - const [aboutAnchorEl, setAboutAnchorEl] = React.useState(null); + const [workflowsAnchorEl, setWorkflowsAnchorEl] = React.useState(null); + const [aboutAnchorEl, setAboutAnchorEl] = React.useState(null); - const handleWorkflowsMenuOpen = (event) => { - setWorkflowsAnchorEl(event.currentTarget); - }; + const handleWorkflowsMenuOpen = (event) => { + setWorkflowsAnchorEl(event.currentTarget); + }; - const handleAboutMenuOpen = (event) => { - setAboutAnchorEl(event.currentTarget); - }; + const handleAboutMenuOpen = (event) => { + setAboutAnchorEl(event.currentTarget); + }; - const handleWorkflowsMenuClose = () => { - setWorkflowsAnchorEl(null); - }; + const handleWorkflowsMenuClose = () => { + setWorkflowsAnchorEl(null); + }; - const handleAboutMenuClose = () => { - setAboutAnchorEl(null); - }; + const handleAboutMenuClose = () => { + setAboutAnchorEl(null); + }; - return ( - - - - {/* Top Menu Bar */} - - - - Borealis - Workflow Automation Tool - + return ( + + + {/* + Main container that: + - fills 100% viewport height + - organizes content with flexbox (vertical) + */} + + {/* --- TOP BAR --- */} + + + + Borealis - Workflow Automation Tool + - {/* Workflows Menu */} - - - Save Workflow - Load Workflow - Close Workflow - + {/* Workflows Menu */} + + + Save Workflow + Load Workflow + Close Workflow + - {/* About Menu */} - - - Gitea Project - Credits - - - + {/* About Menu */} + + + Gitea Project + Credits + + + - {/* Main Content - React Flow */} - - { - document.getElementById("nodeCount").innerText = count; - }} /> - + {/* --- REACT FLOW EDITOR --- */} + {/* + flexGrow={1} ⇒ This box expands to fill remaining vertical space + overflow="hidden" ⇒ No scroll bars, so React Flow does internal panning + mt: 1 ⇒ Add top margin so the gradient starts closer to the AppBar. + */} + + { + document.getElementById("nodeCount").innerText = count; + }} + /> + - {/* Status Bar */} - - Nodes: 0 | Update Rate: 500ms | Flask API Server: - http://127.0.0.1:5000/data - - - - ); + {/* --- STATUS BAR at BOTTOM --- */} + + Nodes: 0 | Update Rate: 500ms | Flask API Server:{" "} + + http://127.0.0.1:5000/data/api/nodes + + + + + ); } diff --git a/Data/WebUI/src/components/FlowEditor.css b/Data/WebUI/src/components/FlowEditor.css index 576bfe8..fbc6a2f 100644 --- a/Data/WebUI/src/components/FlowEditor.css +++ b/Data/WebUI/src/components/FlowEditor.css @@ -14,10 +14,10 @@ width: 100%; height: 100%; pointer-events: none; /* Ensures grid and nodes remain fully interactive */ - background: linear-gradient( to bottom, rgba(9, 44, 68, 0.8) 0%, /* Deep blue at the top */ - rgba(30, 30, 30, 0) 25%, /* Fade out towards center */ + background: linear-gradient( to bottom, rgba(9, 44, 68, 0.9) 0%, /* Deep blue at the top */ + rgba(30, 30, 30, 0) 45%, /* Fade out towards center */ rgba(30, 30, 30, 0) 75%, /* No gradient in the middle */ - rgba(9, 44, 68, 0.8) 100% /* Deep blue at the bottom */ + rgba(9, 44, 68, 0.7) 100% /* Deep blue at the bottom */ ); z-index: -1; /* Ensures it stays behind the React Flow elements */ } diff --git a/Data/WebUI/src/components/FlowEditor.jsx b/Data/WebUI/src/components/FlowEditor.jsx index b341d84..4c72a61 100644 --- a/Data/WebUI/src/components/FlowEditor.jsx +++ b/Data/WebUI/src/components/FlowEditor.jsx @@ -1,52 +1,68 @@ import React, { useState, useEffect, useCallback } from "react"; import ReactFlow, { - addEdge, - Controls, - Background, + addEdge, + Controls, + Background, } from "reactflow"; import "reactflow/dist/style.css"; -import "./FlowEditor.css"; +import "./FlowEditor.css"; const fetchNodes = async () => { - const response = await fetch("/api/workflow"); - return response.json(); + const response = await fetch("/api/workflow"); + return response.json(); }; const saveWorkflow = async (workflow) => { - await fetch("/api/workflow", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(workflow), - }); + await fetch("/api/workflow", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(workflow), + }); }; export default function FlowEditor() { - const [elements, setElements] = useState([]); + const [elements, setElements] = useState([]); - useEffect(() => { - fetchNodes().then((data) => setElements([...data.nodes, ...data.edges])); - }, []); + useEffect(() => { + fetchNodes().then((data) => { + // Data should contain nodes and edges arrays + const newElements = [...data.nodes, ...data.edges]; + setElements(newElements); + }); + }, []); - const onConnect = useCallback( - (params) => { - const newEdge = { id: `e${params.source}-${params.target}`, ...params }; - setElements((els) => [...els, newEdge]); - saveWorkflow({ nodes: elements.filter(e => e.type), edges: [...elements.filter(e => !e.type), newEdge] }); - }, - [elements] - ); + const onConnect = useCallback( + (params) => { + const newEdge = { id: `e${params.source}-${params.target}`, ...params }; + setElements((els) => [...els, newEdge]); - return ( -
- - - - -
- ); + // Separate nodes/edges for saving: + const nodes = elements.filter((el) => el.type); + const edges = elements.filter((el) => !el.type); + + saveWorkflow({ + nodes, + edges: [...edges, newEdge], + }); + }, + [elements] + ); + + return ( +
+ + + + +
+ ); } diff --git a/Launch-Borealis.sh b/Launch-Borealis.sh new file mode 100644 index 0000000..d6eb4c2 --- /dev/null +++ b/Launch-Borealis.sh @@ -0,0 +1,185 @@ +#!/usr/bin/env bash +# -------------------------------------------------------------------- +# Deploying Borealis - Workflow Automation Tool +# +# This script deploys the Borealis Workflow Automation Tool by: +# - Detecting the Linux distro and installing required system dependencies. +# - Creating a Python virtual environment. +# - Copying server data. +# - Setting up a React UI application. +# - Installing Python and Node dependencies. +# - Building the React app. +# - Launching the Flask server. +# +# Usage: +# chmod +x deploy_borealis.sh +# ./deploy_borealis.sh +# -------------------------------------------------------------------- + +# ---------------------- Initialization & Visuals ---------------------- +GREEN="\033[0;32m" +YELLOW="\033[1;33m" +RED="\033[0;31m" +RESET="\033[0m" +CHECKMARK="✅" +HOURGLASS="⏳" +CROSSMARK="❌" +INFO="ℹ️" + +# Function to run a step with progress visuals and error checking +run_step() { + local message="$1" + shift + echo -ne "${HOURGLASS} ${message}... " + if "$@"; then + echo -e "\r${CHECKMARK} ${message}" + else + echo -e "\r${CROSSMARK} ${message} - Failed${RESET}" + exit 1 + fi +} + +echo -e "${GREEN}Deploying Borealis - Workflow Automation Tool...${RESET}" +echo "====================================================================================" + +# ---------------------- Detect Linux Distribution ---------------------- +detect_distro() { + # This function detects the Linux distribution by sourcing /etc/os-release. + if [ -f /etc/os-release ]; then + . /etc/os-release + DISTRO_ID=$ID + else + DISTRO_ID="unknown" + fi + echo -e "${INFO} Detected OS: ${DISTRO_ID}" +} +detect_distro + +# ---------------------- Install System Dependencies ---------------------- +install_core_dependencies() { + # Install required packages based on detected Linux distribution. + case "$DISTRO_ID" in + ubuntu|debian) + sudo apt update -qq + sudo apt install -y python3 python3-venv python3-pip nodejs npm git curl + ;; + rhel|centos|fedora|rocky) + # For Fedora and similar distributions, the venv module is built-in so we omit python3-venv. + sudo dnf install -y python3 python3-pip nodejs npm git curl + ;; + arch) + sudo pacman -Sy --noconfirm python python-venv python-pip nodejs npm git curl + ;; + *) + echo -e "${RED}${CROSSMARK} Unsupported Linux distribution: ${DISTRO_ID}${RESET}" + exit 1 + ;; + esac +} +run_step "Install System Dependencies" install_core_dependencies + +# ---------------------- Path Setup ---------------------- +# Variables and path definitions +venvFolder="Borealis-Workflow-Automation-Tool" +dataSource="Data" +dataDestination="${venvFolder}/Borealis" +customUIPath="${dataSource}/WebUI" +webUIDestination="${venvFolder}/web-interface" + +# ---------------------- Create Python Virtual Environment ---------------------- +run_step "Create Virtual Python Environment" bash -c " + # Check if virtual environment already exists; if not, create one. + if [ ! -f '${venvFolder}/bin/activate' ]; then + python3 -m venv '${venvFolder}' + fi +" + +# ---------------------- Copy Borealis Data ---------------------- +run_step "Copy Borealis Server Data into Virtual Python Environment" bash -c " + # If the Data folder exists, remove any existing server data folder and copy fresh data. + if [ -d \"$dataSource\" ]; then + rm -rf \"$dataDestination\" + mkdir -p \"$dataDestination\" + cp -r \"$dataSource/\"* \"$dataDestination\" + else + echo -e \"\r${INFO} Warning: Data folder not found, skipping copy.${RESET}\" + fi + true +" + +# ---------------------- React UI Setup ---------------------- +run_step "Create a new ReactJS App in ${webUIDestination}" bash -c " + # Create a React app if the destination folder does not exist. + if [ ! -d \"$webUIDestination\" ]; then + # Set CI=true and add --loglevel=error to suppress funding and audit messages + CI=true npx create-react-app \"$webUIDestination\" --silent --use-npm --loglevel=error + fi +" + +run_step "Overwrite React App with Custom Files" bash -c " + # If custom UI files exist, copy them into the React app folder. + if [ -d \"$customUIPath\" ]; then + cp -r \"$customUIPath/\"* \"$webUIDestination\" + else + echo -e \"\r${INFO} No custom UI found, using default React app.${RESET}\" + fi + true +" + +run_step "Remove Existing React Build (if any)" bash -c " + # Remove the build folder if it exists to ensure a fresh build. + if [ -d \"$webUIDestination/build\" ]; then + rm -rf \"$webUIDestination/build\" + fi + true +" + +# ---------------------- Activate Python Virtual Environment ---------------------- +# Activate the Python virtual environment for subsequent commands. +source "${venvFolder}/bin/activate" + +# ---------------------- Install Python Dependencies ---------------------- +run_step "Install Python Dependencies into Virtual Python Environment" bash -c " + # Install Python packages if a requirements.txt file is present. + if [ -f \"requirements.txt\" ]; then + pip install -q -r requirements.txt + else + echo -e \"\r${INFO} No requirements.txt found, skipping Python packages.${RESET}\" + fi + true +" + +# ---------------------- Install Node Dependencies & Build React UI ---------------------- +run_step "Install React App Dependencies" bash -c " + # Install npm dependencies if package.json exists. + if [ -f \"$webUIDestination/package.json\" ]; then + cd \"$webUIDestination\" + # Add --loglevel=error to suppress npm's funding and audit messages + npm install --silent --no-fund --audit=false --loglevel=error + cd - + fi +" + +run_step "Install React Flow and UI Libraries" bash -c " + # Install additional React libraries. + cd \"$webUIDestination\" + npm install reactflow --silent --no-fund --audit=false --loglevel=error + npm install --silent @mui/material @mui/icons-material @emotion/react @emotion/styled --no-fund --audit=false --loglevel=error + cd - +" + +run_step "Build React App" bash -c " + # Build the React app to create production-ready files. + cd \"$webUIDestination\" + npm run build --silent --loglevel=error + cd - +" + +# ---------------------- Launch Flask Server ---------------------- +cd "${venvFolder}" +echo -e "\n${GREEN}Launching Borealis...${RESET}" +echo "====================================================================================" +echo -ne "${HOURGLASS} Starting Flask server... " +python3 Borealis/server.py +echo -e "\r${CHECKMARK} Borealis Launched Successfully!" + diff --git a/Start_Linux.sh b/Start_Linux.sh deleted file mode 100644 index 879b809..0000000 --- a/Start_Linux.sh +++ /dev/null @@ -1,113 +0,0 @@ -#!/bin/bash -# -# Start_Linux.sh -# ----------------------------------------------- -# Bootstrap Borealis Virtual Python Environment -# Usage: chmod +x Start_Linux.sh && ./Start_Linux.sh -# - -: ' ---------------------------------------------------- -SECTION 1: Script Initialization & Path Definitions ---------------------------------------------------- -This section defines all necessary paths and variables to be used -throughout the script, ensuring clarity and maintainability. -' - -# Define paths -venvPath="Borealis-Workflow-Automation-Tool" -dataSource="Data" -dataDestination="$venvPath/Borealis" - - -: ' ---------------------------------------------------- -SECTION 2: Virtual Environment Creation ---------------------------------------------------- -In this section, we check if the virtual environment already exists -by verifying the presence of the "activate" script. If it doesn�t -exist, we create it. -' - -if [ ! -f "$venvPath/bin/activate" ]; then - echo "Creating virtual environment '$venvPath'..." - python3 -m venv "$venvPath" -else - echo "Virtual environment '$venvPath' already exists." -fi - - -: ' ---------------------------------------------------- -SECTION 3: Copy Data Folder ---------------------------------------------------- -If the "Data" folder is present, we remove any previously copied data -in the virtual environment�s "Borealis" directory, create a new -"Borealis" folder, and then copy the "Data" folder into it. -' - -if [ -d "$dataSource" ]; then - echo "Copying Data folder into virtual environment..." - - # Remove old data if it exists - if [ -d "$dataDestination" ]; then - rm -rf "$dataDestination" - fi - - # Create the Borealis directory inside the virtual environment - mkdir -p "$dataDestination" - - # Copy Data into the virtual environment under Borealis - cp -r "$dataSource/"* "$dataDestination/" -else - echo "Warning: Data folder not found, skipping copy." -fi - - -: ' ---------------------------------------------------- -SECTION 4: Activate Environment & Install Dependencies ---------------------------------------------------- -This section activates the newly created (or existing) virtual -environment and installs required dependencies based on the -"requirements.txt" file if it exists. -' - -echo "Activating virtual environment..." -source "$venvPath/bin/activate" - -if [ -f "requirements.txt" ]; then - echo "Installing dependencies..." - - pip install -q -r requirements.txt -else - echo "No requirements.txt found, skipping installation." -fi - - -: ' ---------------------------------------------------- -SECTION 5: Run Main Script ---------------------------------------------------- -Run the main Python script from within the copied Data folder -inside the virtual environment. -' - -if [ -f "$dataDestination/borealis.py" ]; then - echo "Starting Borealis Workflow Automation Tool..." - python "$dataDestination/borealis.py" -else - echo "borealis.py not found in $dataDestination. Skipping execution." -fi - - -: ' ---------------------------------------------------- -SECTION 6: Deactivate Environment ---------------------------------------------------- -After the main script completes execution, the virtual environment -is deactivated. -' - -echo "Deactivating virtual environment..." -deactivate