Implemented Linux Support

This commit is contained in:
Nicole Rappe 2025-03-27 17:41:51 -06:00
parent 25812d9202
commit a71f188a73
6 changed files with 401 additions and 249 deletions

View File

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="Borealis"
content="Workflow Automation Tool"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Borealis</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

View File

@ -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 (
<ThemeProvider theme={darkTheme}>
<CssBaseline />
<Box display="flex" flexDirection="column" height="100vh" bgcolor="#121212">
{/* Top Menu Bar */}
<AppBar position="static" sx={{ bgcolor: "#092c44" }}>
<Toolbar>
<Typography variant="h6" sx={{ flexGrow: 1 }}>
Borealis - Workflow Automation Tool
</Typography>
return (
<ThemeProvider theme={darkTheme}>
<CssBaseline />
{/*
Main container that:
- fills 100% viewport height
- organizes content with flexbox (vertical)
*/}
<Box display="flex" flexDirection="column" height="100vh">
{/* --- TOP BAR --- */}
<AppBar position="static" sx={{ bgcolor: "#092c44" }}>
<Toolbar>
<Typography variant="h6" sx={{ flexGrow: 1 }}>
Borealis - Workflow Automation Tool
</Typography>
{/* Workflows Menu */}
<Button
color="inherit"
onClick={handleWorkflowsMenuOpen}
endIcon={<KeyboardArrowDownIcon />}
>
Workflows
</Button>
<Menu
anchorEl={workflowsAnchorEl}
open={Boolean(workflowsAnchorEl)}
onClose={handleWorkflowsMenuClose}
>
<MenuItem onClick={handleWorkflowsMenuClose}>Save Workflow</MenuItem>
<MenuItem onClick={handleWorkflowsMenuClose}>Load Workflow</MenuItem>
<MenuItem onClick={handleWorkflowsMenuClose}>Close Workflow</MenuItem>
</Menu>
{/* Workflows Menu */}
<Button
color="inherit"
onClick={handleWorkflowsMenuOpen}
endIcon={<KeyboardArrowDownIcon />}
>
Workflows
</Button>
<Menu
anchorEl={workflowsAnchorEl}
open={Boolean(workflowsAnchorEl)}
onClose={handleWorkflowsMenuClose}
>
<MenuItem onClick={handleWorkflowsMenuClose}>Save Workflow</MenuItem>
<MenuItem onClick={handleWorkflowsMenuClose}>Load Workflow</MenuItem>
<MenuItem onClick={handleWorkflowsMenuClose}>Close Workflow</MenuItem>
</Menu>
{/* About Menu */}
<Button
color="inherit"
onClick={handleAboutMenuOpen}
endIcon={<KeyboardArrowDownIcon />}
>
About
</Button>
<Menu
anchorEl={aboutAnchorEl}
open={Boolean(aboutAnchorEl)}
onClose={handleAboutMenuClose}
>
<MenuItem onClick={handleAboutMenuClose}>Gitea Project</MenuItem>
<MenuItem onClick={handleAboutMenuClose}>Credits</MenuItem>
</Menu>
</Toolbar>
</AppBar>
{/* About Menu */}
<Button
color="inherit"
onClick={handleAboutMenuOpen}
endIcon={<KeyboardArrowDownIcon />}
>
About
</Button>
<Menu
anchorEl={aboutAnchorEl}
open={Boolean(aboutAnchorEl)}
onClose={handleAboutMenuClose}
>
<MenuItem onClick={handleAboutMenuClose}>Gitea Project</MenuItem>
<MenuItem onClick={handleAboutMenuClose}>Credits</MenuItem>
</Menu>
</Toolbar>
</AppBar>
{/* Main Content - React Flow */}
<Box flexGrow={1}>
<FlowEditor updateNodeCount={(count) => {
document.getElementById("nodeCount").innerText = count;
}} />
</Box>
{/* --- 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.
*/}
<Box flexGrow={1} overflow="hidden" sx={{ mt: 0 }}>
<FlowEditor
updateNodeCount={(count) => {
document.getElementById("nodeCount").innerText = count;
}}
/>
</Box>
{/* Status Bar */}
<Box
component="footer"
sx={{ bgcolor: "#1e1e1e", color: "white", padding: "5px 10px", textAlign: "center" }}
>
Nodes: <span id="nodeCount">0</span> | Update Rate: 500ms | Flask API Server:
<a href="http://127.0.0.1:5000/data" style={{ color: "#3c78b4" }}> http://127.0.0.1:5000/data</a>
</Box>
</Box>
</ThemeProvider>
);
{/* --- STATUS BAR at BOTTOM --- */}
<Box
component="footer"
sx={{
bgcolor: "#1e1e1e",
color: "white",
px: 2,
py: 1,
textAlign: "left"
}}
>
<b>Nodes</b>: <span id="nodeCount">0</span> | <b>Update Rate</b>: 500ms | <b>Flask API Server:</b>{" "}
<a
href="http://127.0.0.1:5000/api/nodes"
style={{ color: "#3c78b4" }}
>
http://127.0.0.1:5000/data/api/nodes
</a>
</Box>
</Box>
</ThemeProvider>
);
}

View File

@ -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 */
}

View File

@ -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 (
<div className="flow-editor-container">
<ReactFlow elements={elements} onConnect={onConnect}>
<Controls />
<Background
variant="lines"
gap={100}
size={1}
color="rgba(255, 255, 255, 0.2)" // White grid with 20% opacity
/>
</ReactFlow>
</div>
);
// 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 (
<div className="flow-editor-container">
<ReactFlow
proOptions={{ hideAttribution: true }} // Remove the React Flow watermark
elements={elements}
onConnect={onConnect}
>
<Controls />
<Background
variant="lines"
gap={100}
size={1}
color="rgba(255, 255, 255, 0.2)" // White grid lines at 20% opacity
/>
</ReactFlow>
</div>
);
}

185
Launch-Borealis.sh Normal file
View File

@ -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!"

View File

@ -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<73>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<6E>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