diff --git a/.github/workflows/build_server_nsis.yml b/.github/workflows/build_nsis_bundle.yml similarity index 75% rename from .github/workflows/build_server_nsis.yml rename to .github/workflows/build_nsis_bundle.yml index 4fe89779..45e13bb5 100644 --- a/.github/workflows/build_server_nsis.yml +++ b/.github/workflows/build_nsis_bundle.yml @@ -14,6 +14,15 @@ jobs: with: python-version: "3.11" + - name: Electron Builder + working-directory: gui + run: | + npm i + npm run build + + - name: LIST ALL FILES + run: ls -R + - name: Install Python dependencies run: | python -m pip install --upgrade pip @@ -36,9 +45,9 @@ jobs: run: ls -R - name: Create installer - uses: joncloud/makensis-action@v4 + uses: joncloud/makensis-action@v4.1 with: - script-file: "freedata-server-nsis-config.nsi" + script-file: "freedata-nsis-config.nsi" arguments: '/V3' - name: LIST ALL FILES @@ -47,12 +56,14 @@ jobs: - name: Upload artifact uses: actions/upload-artifact@v4 with: - name: 'FreeData-Server-Installer' - path: ./FreeData-Server-Installer.exe + name: 'FreeDATA-Installer' + path: ./FreeDATA-Installer.exe - name: Upload Installer to Release uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/v') with: draft: true - files: ./FreeData-Server-Installer.exe + files: ./FreeDATA-Installer.exe + tag_name: ${{ github.ref_name }} + name: 'FreeDATA-Installer-${{ github.ref_name }}' \ No newline at end of file diff --git a/.github/workflows/build_server.yml b/.github/workflows/build_server.yml index efa1f73e..b6f53874 100644 --- a/.github/workflows/build_server.yml +++ b/.github/workflows/build_server.yml @@ -48,7 +48,7 @@ jobs: brew install portaudio python -m pip install --upgrade pip pip3 install pyaudio - export PYTHONPATH=/opt/homebrew/opt/portaudio/lib/:$PYTHONPATH + export PYTHONPATH=/usr/local/lib/:$PYTHONPATH - name: Install Python dependencies run: | diff --git a/freedata-nsis-config.nsi b/freedata-nsis-config.nsi new file mode 100644 index 00000000..ced5ad91 --- /dev/null +++ b/freedata-nsis-config.nsi @@ -0,0 +1,132 @@ +!include "MUI2.nsh" + +; Request administrative rights +RequestExecutionLevel admin + +; The name and file name of the installer +Name "FreeDATA Installer" +OutFile "FreeDATA-Installer.exe" + +; Default installation directory for the server +InstallDir "$LOCALAPPDATA\FreeDATA" + +; Registry key to store the installation directory +InstallDirRegKey HKCU "Software\FreeDATA" "Install_Dir" + +; Modern UI settings +!define MUI_ABORTWARNING + +; Installer interface settings +!define MUI_ICON "documentation\icon.ico" +!define MUI_UNICON "documentation\icon.ico" ; Icon for the uninstaller + +; Define the welcome page text +!define MUI_WELCOMEPAGE_TEXT "Welcome to the FreeDATA Setup Wizard. This wizard will guide you through the installation process." +!define MUI_FINISHPAGE_TEXT "Folder: $INSTDIR" +!define MUI_DIRECTORYPAGE_TEXT_TOP "Please select the installation folder. It's recommended to use the suggested one to avoid permission problems." + +; Pages +!insertmacro MUI_PAGE_WELCOME +!insertmacro MUI_PAGE_LICENSE "LICENSE" +!insertmacro MUI_PAGE_COMPONENTS +!insertmacro MUI_PAGE_DIRECTORY +!insertmacro MUI_PAGE_INSTFILES +!insertmacro MUI_PAGE_FINISH + +; Uninstaller +!insertmacro MUI_UNPAGE_WELCOME +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES +!insertmacro MUI_UNPAGE_FINISH + +; Language (you can choose and configure the language(s) you want) +!insertmacro MUI_LANGUAGE "English" + + +; Installer Sections +Section "FreeData Server" SEC01 + ; Set output path to the installation directory + SetOutPath $INSTDIR\freedata-server + + ; Check if "config.ini" exists and back it up + IfFileExists $INSTDIR\freedata-server\config.ini backupConfig + +doneBackup: + ; Add your application files here + File /r "modem\server.dist\*" + +; Restore the original "config.ini" if it was backed up + IfFileExists $INSTDIR\freedata-server\config.ini.bak restoreConfig + + ; Create a shortcut in the user's desktop + CreateShortCut "$DESKTOP\FreeDATA Server.lnk" "$INSTDIR\freedata-server\freedata-server.exe" + + ; Create Uninstaller + WriteUninstaller "$INSTDIR\Uninstall.exe" + + ; Create a Start Menu directory + CreateDirectory "$SMPROGRAMS\FreeDATA" + + ; Create shortcut in the Start Menu directory + CreateShortCut "$SMPROGRAMS\FreeDATA\FreeDATA Server.lnk" "$INSTDIR\freedata-server\freedata-server.exe" + + ; Create an Uninstall shortcut + CreateShortCut "$SMPROGRAMS\FreeDATA\Uninstall FreeDATA.lnk" "$INSTDIR\Uninstall.exe" + + + ; Backup "config.ini" before overwriting files +backupConfig: + Rename $INSTDIR\freedata-server\config.ini $INSTDIR\freedata-server\config.ini.bak + Goto doneBackup + +; Restore the original "config.ini" +restoreConfig: + Delete $INSTDIR\freedata-server\config.ini + Rename $INSTDIR\freedata-server\config.ini.bak $INSTDIR\freedata-server\config.ini + + + +SectionEnd + +Section "FreeData x64 GUI" SEC02 + ; Set output path to the GUI installation directory + SetOutPath $INSTDIR\freedata-gui + + ; Add GUI files here + File /r "gui\release\win-unpacked\*" + + ; Create a shortcut on the desktop for the GUI + CreateShortCut "$DESKTOP\FreeDATA GUI.lnk" "$INSTDIR\freedata-gui\freedata.exe" + + ; Create a start menu shortcut + CreateShortCut "$SMPROGRAMS\FreeDATA\FreeDATA GUI.lnk" "$INSTDIR\freedata-gui\freedata.exe" + + ; Create an Uninstall shortcut + CreateShortCut "$SMPROGRAMS\FreeDATA\Uninstall FreeDATA.lnk" "$INSTDIR\Uninstall.exe" + +SectionEnd + +; Uninstaller Section +Section "Uninstall" + ; Delete files and directories for the server + Delete $INSTDIR\freedata-server\*.* + RMDir /r $INSTDIR\freedata-server + + ; Delete files and directories for the GUI + Delete $INSTDIR\freedata-gui\*.* + RMDir /r $INSTDIR\freedata-gui + + ; Remove the desktop shortcuts + Delete "$DESKTOP\FreeDATA Server.lnk" + Delete "$DESKTOP\FreeDATA GUI.lnk" + +; Remove Start Menu shortcuts + Delete "$SMPROGRAMS\FreeDATA\*.*" + RMDir "$SMPROGRAMS\FreeDATA" + + ; Attempt to delete the uninstaller itself + Delete $EXEPATH + + ; Now remove the installation directory if it's empty + RMDir /r $INSTDIR +SectionEnd diff --git a/freedata-server-nsis-config.nsi b/freedata-server-nsis-config.nsi deleted file mode 100644 index a7ec2948..00000000 --- a/freedata-server-nsis-config.nsi +++ /dev/null @@ -1,102 +0,0 @@ -!include "MUI2.nsh" - -; Request administrative rights -RequestExecutionLevel admin - -; The name and file name of the installer -Name "FreeData Server" -OutFile "FreeData-Server-Installer.exe" - -; Default installation directory -; InstallDir "$PROGRAMFILES\FreeData\freedata-server" - -InstallDir "$LOCALAPPDATA\FreeData\freedata-server" - -; Registry key to store the installation directory -InstallDirRegKey HKCU "Software\FreeData\freedata-server" "Install_Dir" - -; Modern UI settings -!define MUI_ABORTWARNING - -; Installer interface settings -!define MUI_ICON "documentation\icon.ico" -!define MUI_UNICON "documentation\icon.ico" ; Icon for the uninstaller - - -; Define the welcome page text -!define MUI_WELCOMEPAGE_TEXT "Welcome to the FreeData Server Setup Wizard. This wizard will guide you through the installation process." -!define MUI_FINISHPAGE_TEXT "Folder: $INSTDIR" - - -!define MUI_DIRECTORYPAGE_TEXT_TOP "Please select the installation folder. Its recommended using the suggested one for avoiding permission problems." - - -; Pages -!insertmacro MUI_PAGE_WELCOME -!insertmacro MUI_PAGE_LICENSE "LICENSE" -;!insertmacro MUI_PAGE_COMPONENTS -!insertmacro MUI_PAGE_DIRECTORY -!insertmacro MUI_PAGE_INSTFILES -!insertmacro MUI_PAGE_FINISH - -; Uninstaller -!insertmacro MUI_UNPAGE_WELCOME -!insertmacro MUI_UNPAGE_CONFIRM -!insertmacro MUI_UNPAGE_INSTFILES -!insertmacro MUI_UNPAGE_FINISH - -; Language (you can choose and configure the language(s) you want) -!insertmacro MUI_LANGUAGE "English" - -; Installer Sections -Section "FreeData Server" SEC01 - - ; Set output path to the installation directory - SetOutPath $INSTDIR - - ; Check if "config.ini" exists and back it up - IfFileExists $INSTDIR\config.ini backupConfig - -doneBackup: - ; Add your application files here - File /r "modem\server.dist\*.*" - - ; Restore the original "config.ini" if it was backed up - IfFileExists $INSTDIR\config.ini.bak restoreConfig - - - - ; Create a shortcut in the user's desktop - CreateShortCut "$DESKTOP\FreeData Server.lnk" "$INSTDIR\freedata-server.exe" - - ; Create Uninstaller - WriteUninstaller "$INSTDIR\Uninstall.exe" - -; Backup "config.ini" before overwriting files -backupConfig: - Rename $INSTDIR\config.ini $INSTDIR\config.ini.bak - Goto doneBackup - -; Restore the original "config.ini" -restoreConfig: - Delete $INSTDIR\config.ini - Rename $INSTDIR\config.ini.bak $INSTDIR\config.ini - - -SectionEnd - -; Uninstaller Section -Section "Uninstall" - - ; Delete files and directories - Delete $INSTDIR\freedata-server.exe - RMDir /r $INSTDIR - - ; Remove the shortcut - Delete "$DESKTOP\FreeData Server.lnk" - - ; Additional uninstallation commands here - -SectionEnd - - diff --git a/gui/electron-builder.json5 b/gui/electron-builder.json5 index cf11795a..a9b83fec 100644 --- a/gui/electron-builder.json5 +++ b/gui/electron-builder.json5 @@ -45,18 +45,12 @@ "icon": "build/icon.png", "target": [ { - "target": "nsis", + "target": "portable", "arch": ["arm64", "x64"] } ], "artifactName": "${productName}-GUI-Windows-${version}.${ext}" }, - "nsis": { - "oneClick": false, - "perMachine": false, - "allowToChangeInstallationDirectory": true, - "deleteAppDataOnUninstall": true - }, "linux": { "category": "Development", "target": [ diff --git a/gui/electron/main/index.ts b/gui/electron/main/index.ts index cf4e9636..112cca9b 100644 --- a/gui/electron/main/index.ts +++ b/gui/electron/main/index.ts @@ -1,7 +1,6 @@ import { app, BrowserWindow, shell, ipcMain } from "electron"; -import { release, platform } from "node:os"; -import { join } from "node:path"; -import { autoUpdater } from "electron-updater"; +import { release, platform } from "os"; +import { join, dirname } from "path"; import { existsSync } from "fs"; import { spawn } from "child_process"; @@ -20,7 +19,6 @@ process.env.DIST = join(process.env.DIST_ELECTRON, "../dist"); process.env.VITE_PUBLIC = process.env.VITE_DEV_SERVER_URL ? join(process.env.DIST_ELECTRON, "../public") : process.env.DIST; -process.env.FDUpdateAvail = "0"; // Disable GPU Acceleration for Windows 7 if (release().startsWith("6.1")) app.disableHardwareAcceleration(); @@ -40,7 +38,7 @@ if (!app.requestSingleInstanceLock()) { // process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true' // set daemon process var -var daemonProcess = null; +var serverProcess = null; let win: BrowserWindow | null = null; // Here, you can also use other preload const preload = join(__dirname, "../preload/index.js"); @@ -75,9 +73,9 @@ async function createWindow() { } // Test actively push message to the Electron-Renderer - win.webContents.on("did-finish-load", () => { - win?.webContents.send("main-process-message", new Date().toLocaleString()); - }); + //win.webContents.on("did-finish-load", () => { + // win?.webContents.send("main-process-message", new Date().toLocaleString()); + //}); // Make all links open with the browser, not with the application win.webContents.setWindowOpenHandler(({ url }) => { @@ -87,12 +85,7 @@ async function createWindow() { // win.webContents.on('will-navigate', (event, url) => { }) #344 win.once("ready-to-show", () => { - //autoUpdater.logger = log.scope("updater"); - //autoUpdater.channel = config.update_channel; - autoUpdater.autoInstallOnAppQuit = false; - autoUpdater.autoDownload = true; - autoUpdater.checkForUpdatesAndNotify(); - //autoUpdater.quitAndInstall(); + // }); } @@ -102,68 +95,61 @@ app.whenReady().then(() => { console.log(platform()); //Generate daemon binary path - var daemonPath = ""; + var serverPath = ""; + console.log(process.env); + + // Attempt to find Installation Folder + console.log(app.getAppPath()); + console.log(join(app.getAppPath(), "..", "..")); + console.log(join(app.getAppPath(), "..", "..", "..")); + + //var basePath = join(app.getAppPath(), '..', '..', '..') || join(process.env.PWD, '..') || join(process.env.INIT_CWD, '..') || join(process.env.DIST, '..', '..', '..'); + var basePath = join(app.getAppPath(), "..", "..", ".."); switch (platform().toLowerCase()) { - case "darwin": - daemonPath = join(process.resourcesPath, "modem", "freedata-server"); - case "linux": - daemonPath = join(process.resourcesPath, "modem", "freedata-server"); - break; + //case "darwin": + //serverPath = join(basePath, "freedata-server", "freedata-server.exe"); + //serverProcess = spawn(serverPath, [], { detached: true }); + //serverProcess.unref(); // Allow the server process to continue running independently of the parent process + // break; + //case "linux": + //serverPath = join(basePath, "freedata-server", "freedata-server.exe"); + //serverProcess = spawn(serverPath, [], { detached: true }); + //serverProcess.unref(); // Allow the server process to continue running independently of the parent process + // break; case "win32": - daemonPath = join(process.resourcesPath, "modem", "freedata-server.exe"); - break; - case "win64": - daemonPath = join(process.resourcesPath, "modem", "freedata-server.exe"); + serverPath = join(basePath, "freedata-server", "freedata-server.exe"); + console.log(`Starting server with path: ${serverPath}`); + serverProcess = spawn( + "cmd.exe", + ["/c", "start", "cmd.exe", "/c", serverPath], + { shell: true }, + ); + console.log(`Started server | PID: ${serverProcess.pid}`); break; + default: console.log("Unhandled OS Platform: ", platform()); + serverProcess = null; + serverPath = null; break; } - //Start daemon binary if it exists - if (existsSync(daemonPath)) { - console.log("Starting freedata-server binary"); - console.log("daemonPath:", daemonPath); - console.log("CWD:", join(daemonPath, "..")); - /* - var daemonProcess = spawn("freedata-server", [], { - cwd: join(process.env.DIST, "modem"), - shell: true - }); -*/ - /* -daemonProcess = spawn(daemonPath, [], { - shell: true - }); - console.log(daemonProcess) -*/ - daemonProcess = spawn(daemonPath, [], {}); + serverProcess.on("error", (err) => { + console.error("Failed to start server process:", err); + serverProcess = null; + serverPath = null; + }); + serverProcess.stdout.on("data", (data) => { + //console.log(`stdout: ${data}`); + }); - // return process messages - daemonProcess.on("error", (err) => { - //daemonProcessLog.error(`error when starting daemon: ${err}`); - console.log(err); - }); - daemonProcess.on("message", () => { - // daemonProcessLog.info(`${data}`); - }); - daemonProcess.stdout.on("data", () => { - // daemonProcessLog.info(`${data}`); - }); - daemonProcess.stderr.on("data", (data) => { - // daemonProcessLog.info(`${data}`); - console.log(data); - }); - daemonProcess.on("close", (code) => { - // daemonProcessLog.warn(`daemonProcess exited with code ${code}`); - }); - } else { - daemonProcess = null; - daemonPath = null; - console.log("Daemon binary doesn't exist--normal for dev environments."); - } + serverProcess.stderr.on("data", (data) => { + console.error(`stderr: ${data}`); + }); +}); - //) +app.on("before-quit", () => { + close_sub_processes(); }); app.on("window-all-closed", () => { @@ -205,104 +191,33 @@ ipcMain.handle("open-win", (_, arg) => { } }); -//restart and install udpate -ipcMain.on("request-restart-and-install-update", (event, data) => { - close_sub_processes(); - autoUpdater.quitAndInstall(); -}); - -// LISTENER FOR UPDATER EVENTS -autoUpdater.on("update-available", (info) => { - process.env.FDUpdateAvail = "1"; - console.log("update available"); - - let arg = { - status: "update-available", - info: info, - }; - win.webContents.send("action-updater", arg); -}); - -autoUpdater.on("update-not-available", (info) => { - console.log("update not available"); - let arg = { - status: "update-not-available", - info: info, - }; - win.webContents.send("action-updater", arg); -}); - -autoUpdater.on("update-downloaded", (info) => { - process.env.FDUpdateAvail = "1"; - console.log("update downloaded"); - let arg = { - status: "update-downloaded", - info: info, - }; - win.webContents.send("action-updater", arg); - // we need to call this at this point. - // if an update is available and we are force closing the app - // the entire screen crashes... - //console.log('quit application and install update'); - //autoUpdater.quitAndInstall(); -}); - -autoUpdater.on("checking-for-update", () => { - console.log("checking for update"); - let arg = { - status: "checking-for-update", - version: app.getVersion(), - }; - win.webContents.send("action-updater", arg); -}); - -autoUpdater.on("download-progress", (progress) => { - let arg = { - status: "download-progress", - progress: progress, - }; - win.webContents.send("action-updater", arg); -}); - -autoUpdater.on("error", (error) => { - console.log("update error"); - let arg = { - status: "error", - progress: error, - }; - win.webContents.send("action-updater", arg); - console.log("AUTO UPDATER : " + error); -}); - function close_sub_processes() { - console.log("closing sub processes"); + console.log("Closing sub processes..."); - // closing the modem binary if not closed when closing application and also our daemon which has been started by the gui - try { - if (daemonProcess != null) { - daemonProcess.kill(); - } - } catch (e) { - console.log(e); - } + if (serverProcess != null) { + try { + console.log(`Killing server process with PID: ${serverProcess.pid}`); - console.log("closing modem and daemon"); - try { - if (platform() == "win32") { - spawn("Taskkill", ["/IM", "freedata-modem.exe", "/F"]); - spawn("Taskkill", ["/IM", "freedata-server.exe", "/F"]); - } + switch (platform().toLowerCase()) { + //case "darwin": + // process.kill(serverProcess.pid); + // break; + //case "linux": + // process.kill(serverProcess.pid); + // break; + case "win32": + // For Windows, use taskkill to ensure all child processes are also terminated + spawn("taskkill", ["/pid", serverProcess.pid.toString(), "/f", "/t"]); + break; - if (platform() == "linux") { - spawn("pkill", ["-9", "freedata-modem"]); - spawn("pkill", ["-9", "freedata-server"]); + default: + console.log("Unhandled OS Platform: ", platform()); + serverProcess = null; + serverPath = null; + break; + } + } catch (error) { + console.error(`Error killing server process: ${error}`); } - - if (platform() == "darwin") { - spawn("pkill", ["-9", "freedata-modem"]); - spawn("pkill", ["-9", "freedata-server"]); - } - } catch (e) { - console.log(e); } } diff --git a/gui/electron/preload/index.ts b/gui/electron/preload/index.ts index 69e18a1e..03708df3 100644 --- a/gui/electron/preload/index.ts +++ b/gui/electron/preload/index.ts @@ -111,83 +111,4 @@ window.onmessage = (ev) => { ev.data.payload === "removeLoading" && removeLoading(); }; -setTimeout(removeLoading, 4999); - -// IPC ACTION FOR AUTO UPDATER -ipcRenderer.on("action-updater", (event, arg) => { - if (arg.status == "download-progress") { - var progressinfo = - "(" + - Math.round(arg.progress.transferred / 1024) + - "kB /" + - Math.round(arg.progress.total / 1024) + - "kB)" + - " @ " + - Math.round(arg.progress.bytesPerSecond / 1024) + - "kByte/s"; - document.getElementById("UpdateProgressInfo").innerHTML = progressinfo; - - document - .getElementById("UpdateProgressBar") - .setAttribute("aria-valuenow", arg.progress.percent); - document - .getElementById("UpdateProgressBar") - .setAttribute("style", "width:" + arg.progress.percent + "%;"); - } - - if (arg.status == "checking-for-update") { - //document.title = document.title + ' - v' + arg.version; - //updateTitle( - // config.myCall, - // config.tnc_host, - // config.tnc_port, - // " -v " + arg.version, - //); - document.getElementById("updater_status").innerHTML = - ''; - - document.getElementById("updater_status").className = - "btn btn-secondary btn-sm"; - document.getElementById("update_and_install").style.display = "none"; - } - if (arg.status == "update-downloaded") { - document.getElementById("update_and_install").removeAttribute("style"); - document.getElementById("updater_status").innerHTML = - ''; - document.getElementById("updater_status").className = - "btn btn-success btn-sm"; - - // HERE WE NEED TO RUN THIS SOMEHOW... - //mainLog.info('quit application and install update'); - //autoUpdater.quitAndInstall(); - } - if (arg.status == "update-not-available") { - document.getElementById("updater_last_version").innerHTML = - arg.info.releaseName; - document.getElementById("updater_last_update").innerHTML = - arg.info.releaseDate; - document.getElementById("updater_release_notes").innerHTML = - arg.info.releaseNotes; - - document.getElementById("updater_status").innerHTML = - ''; - document.getElementById("updater_status").className = - "btn btn-success btn-sm"; - document.getElementById("update_and_install").style.display = "none"; - } - if (arg.status == "update-available") { - document.getElementById("updater_status").innerHTML = - ''; - document.getElementById("updater_status").className = - "btn btn-warning btn-sm"; - document.getElementById("update_and_install").style.display = "none"; - } - - if (arg.status == "error") { - document.getElementById("updater_status").innerHTML = - ''; - document.getElementById("updater_status").className = - "btn btn-danger btn-sm"; - document.getElementById("update_and_install").style.display = "none"; - } -}); +setTimeout(removeLoading, 3999); diff --git a/gui/package.json b/gui/package.json index 0a4216be..b23c4de7 100644 --- a/gui/package.json +++ b/gui/package.json @@ -13,7 +13,8 @@ "release": "vue-tsc --noEmit && vite build && electron-builder -p onTag", "preview": "vite preview", "lint": "eslint --ext .js,.vue src", - "lint-fix": "eslint --ext .js,.vue --fix src" + "lint-fix": "eslint --ext .js,.vue --fix src", + "install-deps": "npm install && npm update" }, "repository": { "type": "git", diff --git a/gui/src/components/main.vue b/gui/src/components/main.vue index c63fdf7c..fa4e28aa 100644 --- a/gui/src/components/main.vue +++ b/gui/src/components/main.vue @@ -21,15 +21,13 @@ import main_modem_healthcheck from "./main_modem_healthcheck.vue"; import Dynamic_components from "./dynamic_components.vue"; import { getFreedataMessages } from "../js/api"; +import { getRemote } from "../store/settingsStore.js"; +import { loadAllData } from "../js/eventHandler";