Added Electron Framework
This commit is contained in:
parent
801c58d11a
commit
11f95edf02
4
.gitignore
vendored
4
.gitignore
vendored
@ -14,7 +14,7 @@ Borealis-Server.exe
|
|||||||
# Development Folders
|
# Development Folders
|
||||||
/Server/
|
/Server/
|
||||||
/Agent/
|
/Agent/
|
||||||
|
/ElectronApp/
|
||||||
|
|
||||||
# Misc Files/Folders
|
# Misc Files/Folders
|
||||||
.vs/s
|
.vs/s
|
||||||
AI_Model_Custom_Instructions.md
|
|
108
Data/Electron/main.js
Normal file
108
Data/Electron/main.js
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: <ProjectRoot>/Data/Electron/main.js
|
||||||
|
const { app, BrowserWindow } = require('electron')
|
||||||
|
const path = require('path')
|
||||||
|
const { spawn } = require('child_process')
|
||||||
|
const http = require('http')
|
||||||
|
|
||||||
|
function startBackend() {
|
||||||
|
// In dev, __dirname is ElectronApp/, so base= one level up (project root).
|
||||||
|
// In prod, process.resourcesPath points at the unpacked resources folder.
|
||||||
|
const base = app.isPackaged
|
||||||
|
? process.resourcesPath
|
||||||
|
: path.resolve(__dirname, '..')
|
||||||
|
|
||||||
|
// Path to your bundled venv python.exe (or python3 on Unix)
|
||||||
|
const pythonExe = path.join(
|
||||||
|
base,
|
||||||
|
'Server',
|
||||||
|
'Scripts',
|
||||||
|
process.platform === 'win32' ? 'python.exe' : 'python3'
|
||||||
|
)
|
||||||
|
|
||||||
|
// Path to the Flask server entrypoint
|
||||||
|
const serverScript = path.join(base, 'Server', 'Borealis', 'server.py')
|
||||||
|
|
||||||
|
console.log(`[backend] Launching: ${pythonExe} ${serverScript}`)
|
||||||
|
const proc = spawn(pythonExe, [serverScript], {
|
||||||
|
cwd: path.dirname(serverScript),
|
||||||
|
stdio: ['ignore', 'pipe', 'pipe']
|
||||||
|
})
|
||||||
|
|
||||||
|
proc.stdout.on('data', (data) => {
|
||||||
|
console.log(`[backend stdout] ${data.toString().trim()}`)
|
||||||
|
})
|
||||||
|
proc.stderr.on('data', (data) => {
|
||||||
|
console.error(`[backend stderr] ${data.toString().trim()}`)
|
||||||
|
})
|
||||||
|
proc.on('exit', (code) => {
|
||||||
|
console.log(`[backend] exited with code ${code}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
return proc
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitForServer(url, timeout = 10000) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const start = Date.now()
|
||||||
|
;(function attempt() {
|
||||||
|
http.get(url, (res) => {
|
||||||
|
if (res.statusCode >= 200 && res.statusCode < 400) {
|
||||||
|
resolve()
|
||||||
|
} else if (Date.now() - start > timeout) {
|
||||||
|
reject(new Error('Server did not start in time'))
|
||||||
|
} else {
|
||||||
|
setTimeout(attempt, 200)
|
||||||
|
}
|
||||||
|
}).on('error', () => {
|
||||||
|
if (Date.now() - start > timeout) {
|
||||||
|
reject(new Error('Server did not start in time'))
|
||||||
|
} else {
|
||||||
|
setTimeout(attempt, 200)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createWindow() {
|
||||||
|
const win = new BrowserWindow({
|
||||||
|
width: 1280,
|
||||||
|
height: 800,
|
||||||
|
webPreferences: { nodeIntegration: false }
|
||||||
|
})
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === 'development') {
|
||||||
|
// If you still run CRA dev server for debugging
|
||||||
|
win.loadURL('http://localhost:3000')
|
||||||
|
} else {
|
||||||
|
// Load the built CRA files
|
||||||
|
const indexPath = path.join(__dirname, 'renderer', 'index.html')
|
||||||
|
win.loadFile(indexPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app.whenReady().then(async () => {
|
||||||
|
const backend = startBackend()
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Wait for Flask (default port 5000)
|
||||||
|
await waitForServer('http://localhost:5000')
|
||||||
|
await createWindow()
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
app.quit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
app.on('activate', () => {
|
||||||
|
if (BrowserWindow.getAllWindows().length === 0) {
|
||||||
|
createWindow()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
app.on('window-all-closed', () => {
|
||||||
|
if (process.platform !== 'darwin') {
|
||||||
|
app.quit()
|
||||||
|
}
|
||||||
|
})
|
40
Data/Electron/package.json
Normal file
40
Data/Electron/package.json
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"name": "borealis",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Borealis Desktop",
|
||||||
|
"author": "Nicole Rappe",
|
||||||
|
"main": "main.js",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "electron .",
|
||||||
|
"dist": "electron-builder"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"electron": "26.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"electron-builder": "26.0.12"
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"appId": "io.bunny-lab.borealis",
|
||||||
|
"directories": {
|
||||||
|
"buildResources": "assets",
|
||||||
|
"output": "dist_electron"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"Server/**",
|
||||||
|
"renderer/**",
|
||||||
|
"main.js"
|
||||||
|
],
|
||||||
|
"extraResources": [
|
||||||
|
{
|
||||||
|
"from": "Server/",
|
||||||
|
"to": "Server",
|
||||||
|
"filter": ["**/*"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"win": { "target": "nsis" },
|
||||||
|
"mac": { "target": "dmg" },
|
||||||
|
"linux": { "target": "AppImage" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
16
Data/Electron/vite.config.ts
Normal file
16
Data/Electron/vite.config.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: <ProjectRoot>/Data/Electron/vite.config.ts
|
||||||
|
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import react from '@vitejs/plugin-react'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
root: path.resolve(__dirname, 'renderer'),
|
||||||
|
build: {
|
||||||
|
outDir: path.resolve(__dirname, 'dist/renderer')
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
port: 5173
|
||||||
|
},
|
||||||
|
plugins: [react()]
|
||||||
|
})
|
@ -3,11 +3,13 @@
|
|||||||
<#
|
<#
|
||||||
Deploy-Borealis.ps1
|
Deploy-Borealis.ps1
|
||||||
----------------------
|
----------------------
|
||||||
This script deploys the Borealis Workflow Automation Tool with two modules:
|
This script deploys the Borealis Workflow Automation Tool with three modules:
|
||||||
- Server (Web Dashboard)
|
- Server (Web Dashboard)
|
||||||
- Agent (Client / Data Collector)
|
- Agent (Client / Data Collector)
|
||||||
|
- Desktop App (Electron)
|
||||||
|
|
||||||
It begins by presenting a menu to the user. Based on the selection (1 or 2), the corresponding module is launched.
|
It begins by presenting a menu to the user. Based on the selection (1, 2, or 3),
|
||||||
|
the corresponding module is launched or deployed.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
Set-ExecutionPolicy Unrestricted -Scope Process; .\Launch-Borealis.ps1
|
Set-ExecutionPolicy Unrestricted -Scope Process; .\Launch-Borealis.ps1
|
||||||
@ -18,9 +20,6 @@ Clear-Host
|
|||||||
|
|
||||||
<#
|
<#
|
||||||
Section: Progress Symbols & Helpers
|
Section: Progress Symbols & Helpers
|
||||||
------------------------------------
|
|
||||||
Define symbols for UI feedback and helper functions to run steps with consistent
|
|
||||||
progress/status output and error handling.
|
|
||||||
#>
|
#>
|
||||||
$symbols = @{
|
$symbols = @{
|
||||||
Success = [char]0x2705
|
Success = [char]0x2705
|
||||||
@ -57,12 +56,6 @@ function Run-Step {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# ---------------------- Bundle Executables Setup ----------------------
|
# ---------------------- Bundle Executables Setup ----------------------
|
||||||
<#
|
|
||||||
Section: Locate Bundled Runtimes
|
|
||||||
--------------------------------
|
|
||||||
Identify the directory of this script, then ensure our bundled Python and NodeJS
|
|
||||||
executables are present. Add them to PATH for later use.
|
|
||||||
#>
|
|
||||||
$scriptDir = Split-Path $MyInvocation.MyCommand.Path -Parent
|
$scriptDir = Split-Path $MyInvocation.MyCommand.Path -Parent
|
||||||
$depsRoot = Join-Path $scriptDir 'Dependencies'
|
$depsRoot = Join-Path $scriptDir 'Dependencies'
|
||||||
$pythonExe = Join-Path $depsRoot 'Python\python.exe'
|
$pythonExe = Join-Path $depsRoot 'Python\python.exe'
|
||||||
@ -80,20 +73,21 @@ foreach ($tool in @($pythonExe, $nodeExe, $npmCmd, $npxCmd)) {
|
|||||||
$env:PATH = '{0};{1};{2}' -f (Split-Path $pythonExe), (Split-Path $nodeExe), $env:PATH
|
$env:PATH = '{0};{1};{2}' -f (Split-Path $pythonExe), (Split-Path $nodeExe), $env:PATH
|
||||||
|
|
||||||
# ---------------------- Menu Prompt & User Input ----------------------
|
# ---------------------- Menu Prompt & User Input ----------------------
|
||||||
Write-Host "Deploying Borealis - Workflow Automation Tool..." -ForegroundColor Blue
|
Write-Host "Borealis - Workflow Automation Tool" -ForegroundColor Blue
|
||||||
Write-Host "===================================================================================="
|
Write-Host "===================================================================================="
|
||||||
Write-Host "Please choose which module you want to launch / (re)deploy:"
|
Write-Host "Please choose which function you want to launch / (re)deploy:"
|
||||||
Write-Host "- Server (Web Dashboard) [1]"
|
Write-Host "- Server (Web Dashboard) [1]"
|
||||||
Write-Host "- Agent (Local/Remote Client) [2]"
|
Write-Host "- Agent (Local/Remote Client) [2]"
|
||||||
|
Write-Host "- Desktop App (Electron) ((Run Step 1 Beforehand)) [3]"
|
||||||
|
|
||||||
$choice = Read-Host "Enter 1 or 2"
|
$choice = Read-Host "Enter 1, 2, or 3"
|
||||||
|
|
||||||
switch ($choice) {
|
switch ($choice) {
|
||||||
|
|
||||||
"1" {
|
"1" {
|
||||||
# ---------------------- Server Deployment Setup ----------------------
|
# Server Deployment (Web Dashboard)
|
||||||
Clear-Host
|
Clear-Host
|
||||||
Write-Host "Deploying Borealis - Workflow Automation Tool..." -ForegroundColor Blue
|
Write-Host "Deploying Borealis - Web Dashboard..." -ForegroundColor Blue
|
||||||
Write-Host "===================================================================================="
|
Write-Host "===================================================================================="
|
||||||
|
|
||||||
$venvFolder = "Server"
|
$venvFolder = "Server"
|
||||||
@ -103,9 +97,7 @@ switch ($choice) {
|
|||||||
$webUIDestination = "$venvFolder\web-interface"
|
$webUIDestination = "$venvFolder\web-interface"
|
||||||
$venvPython = Join-Path $venvFolder 'Scripts\python.exe'
|
$venvPython = Join-Path $venvFolder 'Scripts\python.exe'
|
||||||
|
|
||||||
<#
|
# Create Virtual Environment & Copy Server Assets
|
||||||
Step: Create Virtual Environment & Copy Server Assets
|
|
||||||
#>
|
|
||||||
Run-Step "Create Borealis Virtual Python Environment" {
|
Run-Step "Create Borealis Virtual Python Environment" {
|
||||||
if (-not (Test-Path "$venvFolder\Scripts\Activate")) {
|
if (-not (Test-Path "$venvFolder\Scripts\Activate")) {
|
||||||
& $pythonExe -m venv $venvFolder | Out-Null
|
& $pythonExe -m venv $venvFolder | Out-Null
|
||||||
@ -128,19 +120,15 @@ switch ($choice) {
|
|||||||
. "$venvFolder\Scripts\Activate"
|
. "$venvFolder\Scripts\Activate"
|
||||||
}
|
}
|
||||||
|
|
||||||
<#
|
# Install Python Dependencies
|
||||||
Step: Install Python Dependencies
|
|
||||||
#>
|
|
||||||
Run-Step "Install Python Dependencies into Virtual Python Environment" {
|
Run-Step "Install Python Dependencies into Virtual Python Environment" {
|
||||||
if (Test-Path "$dataSource\Server\server-requirements.txt") {
|
if (Test-Path "$dataSource\Server\server-requirements.txt") {
|
||||||
& $venvPython -m pip install --disable-pip-version-check -q -r "$dataSource\Server\server-requirements.txt" | Out-Null
|
& $venvPython -m pip install --disable-pip-version-check -q -r "$dataSource\Server\server-requirements.txt" | Out-Null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
<#
|
# NPM Install for WebUI
|
||||||
Step: NPM Install
|
Run-Step "ReactJS Web Frontend: Install NPM Packages" {
|
||||||
#>
|
|
||||||
Run-Step "ReactJS Web Frontend: Install Necessary NPM Packages" {
|
|
||||||
if (Test-Path "$webUIDestination\package.json") {
|
if (Test-Path "$webUIDestination\package.json") {
|
||||||
Push-Location $webUIDestination
|
Push-Location $webUIDestination
|
||||||
$env:npm_config_loglevel = "silent"
|
$env:npm_config_loglevel = "silent"
|
||||||
@ -149,24 +137,16 @@ switch ($choice) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
<#
|
# Build React App
|
||||||
Step: Build React App
|
Run-Step "ReactJS Web Frontend: Build" {
|
||||||
#>
|
|
||||||
Run-Step "ReactJS Web Frontend: " {
|
|
||||||
Push-Location $webUIDestination
|
Push-Location $webUIDestination
|
||||||
& $npmCmd run build
|
& $npmCmd run build
|
||||||
Pop-Location
|
Pop-Location
|
||||||
}
|
}
|
||||||
|
|
||||||
<#
|
# Launch Flask Server
|
||||||
Step: Launch Flask Server
|
|
||||||
--------------------------
|
|
||||||
Change into the Server folder so server.py’s relative paths to web-interface/build
|
|
||||||
resolve correctly, then invoke Python on the server script.
|
|
||||||
#>
|
|
||||||
Run-Step "Borealis: Launch Flask Server" {
|
Run-Step "Borealis: Launch Flask Server" {
|
||||||
Push-Location (Join-Path $scriptDir 'Server')
|
Push-Location (Join-Path $scriptDir 'Server')
|
||||||
|
|
||||||
$py = Join-Path $scriptDir 'Server\Scripts\python.exe'
|
$py = Join-Path $scriptDir 'Server\Scripts\python.exe'
|
||||||
$server_py = Join-Path $scriptDir 'Server\Borealis\server.py'
|
$server_py = Join-Path $scriptDir 'Server\Borealis\server.py'
|
||||||
|
|
||||||
@ -176,13 +156,12 @@ switch ($choice) {
|
|||||||
Write-Host "$($symbols.Running) Preloading OCR Engines... Please be patient..."
|
Write-Host "$($symbols.Running) Preloading OCR Engines... Please be patient..."
|
||||||
|
|
||||||
& $py $server_py
|
& $py $server_py
|
||||||
|
|
||||||
Pop-Location
|
Pop-Location
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"2" {
|
"2" {
|
||||||
# ---------------------- Agent Deployment Setup ----------------------
|
# Agent Deployment (Client / Data Collector)
|
||||||
Clear-Host
|
Clear-Host
|
||||||
Write-Host "Deploying Borealis Agent..." -ForegroundColor Blue
|
Write-Host "Deploying Borealis Agent..." -ForegroundColor Blue
|
||||||
Write-Host "===================================================================================="
|
Write-Host "===================================================================================="
|
||||||
@ -192,14 +171,8 @@ switch ($choice) {
|
|||||||
$agentRequirements = "Data\Agent\agent-requirements.txt"
|
$agentRequirements = "Data\Agent\agent-requirements.txt"
|
||||||
$agentDestinationFolder = "$venvFolder\Borealis"
|
$agentDestinationFolder = "$venvFolder\Borealis"
|
||||||
$agentDestinationFile = "$venvFolder\Borealis\borealis-agent.py"
|
$agentDestinationFile = "$venvFolder\Borealis\borealis-agent.py"
|
||||||
|
$venvPython = Join-Path $scriptDir $venvFolder | Join-Path -ChildPath 'Scripts\python.exe'
|
||||||
|
|
||||||
# build the absolute path to python.exe inside the venv
|
|
||||||
$venvPython = Join-Path $scriptDir $venvFolder
|
|
||||||
$venvPython = Join-Path $venvPython 'Scripts\python.exe'
|
|
||||||
|
|
||||||
<#
|
|
||||||
Step: Create Virtual Environment & Copy Agent Script
|
|
||||||
#>
|
|
||||||
Run-Step "Create Virtual Python Environment" {
|
Run-Step "Create Virtual Python Environment" {
|
||||||
if (-not (Test-Path "$venvFolder\Scripts\Activate")) {
|
if (-not (Test-Path "$venvFolder\Scripts\Activate")) {
|
||||||
& $pythonExe -m venv $venvFolder
|
& $pythonExe -m venv $venvFolder
|
||||||
@ -212,22 +185,77 @@ switch ($choice) {
|
|||||||
. "$venvFolder\Scripts\Activate"
|
. "$venvFolder\Scripts\Activate"
|
||||||
}
|
}
|
||||||
|
|
||||||
<#
|
|
||||||
Step: Install Agent Dependencies
|
|
||||||
#>
|
|
||||||
Run-Step "Install Python Dependencies" {
|
Run-Step "Install Python Dependencies" {
|
||||||
if (Test-Path $agentRequirements) {
|
if (Test-Path $agentRequirements) {
|
||||||
& $venvPython -m pip install --disable-pip-version-check -q -r $agentRequirements | Out-Null
|
& $venvPython -m pip install --disable-pip-version-check -q -r $agentRequirements | Out-Null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host "`nLaunching Borealis Agent..." -ForegroundColor Blue
|
Write-Host "`nLaunching Borealis Agent..." -ForegroundColor Blue
|
||||||
Write-Host "===================================================================================="
|
Write-Host "===================================================================================="
|
||||||
# call python with the absolute interpreter path and the absolute script path
|
& $venvPython -W ignore::SyntaxWarning $agentDestinationFile
|
||||||
$agentScript = Join-Path $scriptDir $agentDestinationFile
|
}
|
||||||
& $venvPython -W ignore::SyntaxWarning $agentScript
|
|
||||||
|
"3" {
|
||||||
|
# Desktop App Deployment (Electron)
|
||||||
|
Clear-Host
|
||||||
|
Write-Host "Deploying Borealis Desktop App..." -ForegroundColor Cyan
|
||||||
|
Write-Host "===================================================================================="
|
||||||
|
|
||||||
|
$electronSource = "Data\Electron"
|
||||||
|
$electronDestination = "ElectronApp"
|
||||||
|
$scriptDir = Split-Path $MyInvocation.MyCommand.Path -Parent
|
||||||
|
|
||||||
|
# 1) Prepare ElectronApp folder
|
||||||
|
Run-Step "Prepare ElectronApp folder" {
|
||||||
|
if (Test-Path $electronDestination) {
|
||||||
|
Remove-Item $electronDestination -Recurse -Force
|
||||||
|
}
|
||||||
|
New-Item -Path $electronDestination -ItemType Directory | Out-Null
|
||||||
|
|
||||||
|
# Copy deployed Flask server
|
||||||
|
$deployedServer = Join-Path $scriptDir 'Server\Borealis'
|
||||||
|
if (-not (Test-Path $deployedServer)) {
|
||||||
|
throw "Server\Borealis not found - please run choice 1 first."
|
||||||
|
}
|
||||||
|
Copy-Item $deployedServer "$electronDestination\Server" -Recurse
|
||||||
|
|
||||||
|
# Copy Electron scaffold files
|
||||||
|
Copy-Item "$electronSource\package.json" "$electronDestination" -Force
|
||||||
|
Copy-Item "$electronSource\main.js" "$electronDestination" -Force
|
||||||
|
|
||||||
|
# Copy CRA build into renderer
|
||||||
|
$staticBuild = Join-Path $scriptDir 'Server\web-interface\build'
|
||||||
|
if (-not (Test-Path $staticBuild)) {
|
||||||
|
throw "React build not found - run choice 1 to build WebUI first."
|
||||||
|
}
|
||||||
|
Copy-Item "$staticBuild\*" "$electronDestination\renderer" -Recurse -Force
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# 2) Install Electron & Builder
|
||||||
|
Run-Step "ElectronApp: Install Node dependencies" {
|
||||||
|
Push-Location $electronDestination
|
||||||
|
$env:NODE_ENV = ''
|
||||||
|
$env:npm_config_production = ''
|
||||||
|
& $npmCmd install --silent --no-fund --audit=false
|
||||||
|
Pop-Location
|
||||||
|
}
|
||||||
|
|
||||||
|
# 3) Package desktop app
|
||||||
|
Run-Step "ElectronApp: Package with electron-builder" {
|
||||||
|
Push-Location $electronDestination
|
||||||
|
& $npmCmd run dist
|
||||||
|
Pop-Location
|
||||||
|
}
|
||||||
|
|
||||||
|
# 4) Launch in dev mode
|
||||||
|
Run-Step "ElectronApp: Launch in dev mode" {
|
||||||
|
Push-Location $electronDestination
|
||||||
|
& $npmCmd run dev
|
||||||
|
Pop-Location
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
default {
|
default {
|
||||||
Write-Host "Invalid selection. Exiting..." -ForegroundColor Yellow
|
Write-Host "Invalid selection. Exiting..." -ForegroundColor Yellow
|
||||||
exit 1
|
exit 1
|
||||||
|
Loading…
x
Reference in New Issue
Block a user