2024-10-23 21:13:31 +00:00
|
|
|
import { app, BrowserWindow, ipcMain, screen, dialog, Tray, Menu, MenuItemConstructorOptions,
|
2024-11-16 00:23:24 +00:00
|
|
|
IpcMainInvokeEvent, Notification, NotificationConstructorOptions, clipboard, powerMonitor,
|
|
|
|
WebContents,
|
|
|
|
HandlerDetails,
|
|
|
|
Event,
|
|
|
|
WebContentsWillNavigateEventParams
|
2024-10-23 21:13:31 +00:00
|
|
|
} from 'electron';
|
2024-09-17 18:33:24 +00:00
|
|
|
import * as path from 'path';
|
|
|
|
import * as fs from 'fs';
|
2024-10-09 21:40:32 +00:00
|
|
|
import * as os from 'os';
|
2024-11-16 00:23:24 +00:00
|
|
|
import { AppMainProcess, MonerodProcess } from './process';
|
|
|
|
import { BatteryUtils, FileUtils, NetworkUtils } from './utils';
|
2024-09-22 13:55:03 +00:00
|
|
|
|
2024-10-21 14:54:51 +00:00
|
|
|
app.setName('Monero Daemon');
|
2024-10-17 16:52:17 +00:00
|
|
|
|
2024-11-16 13:13:17 +00:00
|
|
|
if (process.platform === 'win32')
|
|
|
|
{
|
|
|
|
app.setAppUserModelId(app.name);
|
|
|
|
}
|
|
|
|
|
2024-09-17 18:33:24 +00:00
|
|
|
let win: BrowserWindow | null = null;
|
2024-10-12 14:36:15 +00:00
|
|
|
let isHidden: boolean = false;
|
2024-10-12 12:28:16 +00:00
|
|
|
let isQuitting: boolean = false;
|
2024-11-01 20:45:25 +00:00
|
|
|
const separator: string = os.platform() == 'win32' ? '\\' : '/';
|
|
|
|
const appApp = `${separator}app${separator}app`;
|
|
|
|
const appSrc = `${separator}app${separator}src`;
|
|
|
|
const _app = `${separator}app`;
|
|
|
|
const _src = `${separator}src`;
|
|
|
|
const dirname = (__dirname.endsWith(appApp) ? __dirname.replace(appApp, appSrc) : __dirname.endsWith(_app) ? __dirname.replace(_app, _src) : __dirname);
|
|
|
|
|
|
|
|
console.log('dirname: ' + dirname);
|
|
|
|
|
2024-11-16 00:23:24 +00:00
|
|
|
//let monerodProcess: ChildProcessWithoutNullStreams | null = null;
|
|
|
|
let monerodProcess: MonerodProcess | null = null;
|
|
|
|
|
2024-10-23 15:17:33 +00:00
|
|
|
const iconRelPath: string = 'assets/icons/monero-symbol-on-white-480.png';
|
2024-11-01 20:45:25 +00:00
|
|
|
//const wdwIcon = `${dirname}/${iconRelPath}`;
|
|
|
|
const wdwIcon = path.join(dirname, iconRelPath);
|
2024-10-12 12:28:16 +00:00
|
|
|
|
2024-10-31 19:35:47 +00:00
|
|
|
let tray: Tray;
|
|
|
|
let trayMenu: Menu;
|
|
|
|
|
2024-10-31 11:37:41 +00:00
|
|
|
// #region Window
|
|
|
|
|
2024-11-01 13:39:47 +00:00
|
|
|
function updateTrayMenu(): void {
|
|
|
|
tray.setContextMenu(trayMenu);
|
|
|
|
}
|
2024-09-17 18:33:24 +00:00
|
|
|
|
2024-11-01 13:39:47 +00:00
|
|
|
function setTrayItemEnabled(id: string, enabled: boolean): void {
|
|
|
|
const item = trayMenu.getMenuItemById(id);
|
|
|
|
|
|
|
|
if (!item) {
|
|
|
|
throw new Error(`Item not found: ${id}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
item.enabled = enabled;
|
|
|
|
|
|
|
|
updateTrayMenu();
|
|
|
|
}
|
2024-10-31 19:35:47 +00:00
|
|
|
|
|
|
|
function createTrayMenuTemplate(): MenuItemConstructorOptions[] {
|
|
|
|
return [
|
2024-10-12 12:28:16 +00:00
|
|
|
{
|
2024-11-01 13:39:47 +00:00
|
|
|
id: "startDaemon",
|
2024-10-31 19:35:47 +00:00
|
|
|
label: "Start",
|
2024-11-01 13:39:47 +00:00
|
|
|
toolTip: "Start Daemon",
|
|
|
|
click: () => {
|
|
|
|
win?.webContents.send('on-tray-start-daemon');
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: "stopDaemon",
|
|
|
|
label: "Stop",
|
|
|
|
toolTip: "Stop Daemon",
|
|
|
|
click: () => {
|
|
|
|
win?.webContents.send('on-tray-stop-daemon');
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: "startSync",
|
|
|
|
label: "Start Sync",
|
|
|
|
toolTip: "Start Daemon Sync",
|
|
|
|
click: () => {
|
|
|
|
win?.webContents.send('on-tray-start-sync');
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: "stopSync",
|
|
|
|
label: "Stop Sync",
|
|
|
|
toolTip: "Stop Daemon Sync",
|
|
|
|
click: () => {
|
|
|
|
win?.webContents.send('on-tray-stop-sync');
|
|
|
|
}
|
2024-10-12 12:28:16 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
id: "quitDaemon",
|
|
|
|
label: "Quit",
|
2024-11-01 13:39:47 +00:00
|
|
|
toolTip: "Quit Daemon",
|
2024-10-12 12:28:16 +00:00
|
|
|
click: () => {
|
2024-11-01 13:39:47 +00:00
|
|
|
win?.webContents.send('on-tray-quit-daemon');
|
2024-10-12 12:28:16 +00:00
|
|
|
}
|
|
|
|
}
|
2024-10-31 19:35:47 +00:00
|
|
|
]
|
|
|
|
}
|
2024-10-12 12:28:16 +00:00
|
|
|
|
2024-10-31 19:35:47 +00:00
|
|
|
function createTray(): Tray {
|
|
|
|
const trayMenuTemplate = createTrayMenuTemplate();
|
2024-10-12 12:28:16 +00:00
|
|
|
const tray = new Tray(wdwIcon);
|
2024-10-31 19:35:47 +00:00
|
|
|
trayMenu = Menu.buildFromTemplate(trayMenuTemplate);
|
2024-10-12 14:36:15 +00:00
|
|
|
|
|
|
|
tray.setToolTip('Monero Daemon');
|
2024-10-12 12:28:16 +00:00
|
|
|
tray.setContextMenu(trayMenu);
|
|
|
|
|
2024-10-31 19:35:47 +00:00
|
|
|
tray.on('click', (event) => {
|
|
|
|
if (isHidden) {
|
|
|
|
win?.show();
|
|
|
|
isHidden = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
win?.hide();
|
|
|
|
isHidden = true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return tray;
|
|
|
|
}
|
|
|
|
|
2024-11-16 13:13:17 +00:00
|
|
|
async function createWindow(): Promise<BrowserWindow> {
|
2024-10-31 19:35:47 +00:00
|
|
|
const size = screen.getPrimaryDisplay().workAreaSize;
|
|
|
|
|
|
|
|
tray = createTray();
|
2024-10-24 14:36:33 +00:00
|
|
|
|
2024-09-17 18:33:24 +00:00
|
|
|
// Create the browser window.
|
|
|
|
win = new BrowserWindow({
|
|
|
|
x: 0,
|
|
|
|
y: 0,
|
|
|
|
width: size.width,
|
|
|
|
height: size.height,
|
|
|
|
webPreferences: {
|
2024-09-30 19:07:57 +00:00
|
|
|
preload: path.join(__dirname, 'preload.js'),
|
|
|
|
nodeIntegration: false,
|
2024-11-16 00:23:24 +00:00
|
|
|
allowRunningInsecureContent: (AppMainProcess.serve),
|
2024-10-15 22:38:57 +00:00
|
|
|
contextIsolation: true,
|
2024-10-17 16:52:17 +00:00
|
|
|
devTools: !app.isPackaged,
|
2024-11-18 01:04:16 +00:00
|
|
|
sandbox: true,
|
|
|
|
defaultFontSize: process.platform == 'win32' ? 12 : 16,
|
|
|
|
defaultMonospaceFontSize: process.platform == 'win32' ? 11 : 13
|
2024-09-17 18:33:24 +00:00
|
|
|
},
|
2024-10-29 17:34:25 +00:00
|
|
|
show: false,
|
2024-10-09 18:53:38 +00:00
|
|
|
autoHideMenuBar: true,
|
2024-10-12 12:28:16 +00:00
|
|
|
icon: wdwIcon
|
2024-10-24 14:36:33 +00:00
|
|
|
});
|
2024-09-17 18:33:24 +00:00
|
|
|
|
2024-11-16 00:23:24 +00:00
|
|
|
isHidden = AppMainProcess.startMinized;
|
2024-10-24 14:36:33 +00:00
|
|
|
|
|
|
|
if (!app.isPackaged) win.webContents.openDevTools();
|
2024-10-07 21:47:52 +00:00
|
|
|
|
2024-11-16 00:23:24 +00:00
|
|
|
if (AppMainProcess.serve) {
|
2024-09-17 18:33:24 +00:00
|
|
|
const debug = require('electron-debug');
|
|
|
|
debug();
|
|
|
|
|
|
|
|
require('electron-reloader')(module);
|
|
|
|
win.loadURL('http://localhost:4200');
|
|
|
|
} else {
|
|
|
|
// Path when running electron executable
|
|
|
|
let pathIndex = './index.html';
|
|
|
|
|
2024-10-23 15:17:33 +00:00
|
|
|
if (fs.existsSync(path.join(dirname, '../dist/index.html'))) {
|
2024-09-17 18:33:24 +00:00
|
|
|
// Path when running electron in local folder
|
|
|
|
pathIndex = '../dist/index.html';
|
|
|
|
}
|
|
|
|
|
2024-10-23 15:17:33 +00:00
|
|
|
const url = new URL(path.join('file:', dirname, pathIndex));
|
2024-11-08 14:57:03 +00:00
|
|
|
console.log(`Main window url: ${url}`);
|
2024-10-24 14:36:33 +00:00
|
|
|
|
2024-11-16 13:13:17 +00:00
|
|
|
await win.loadURL(url.href);
|
2024-09-17 18:33:24 +00:00
|
|
|
}
|
|
|
|
|
2024-10-12 12:28:16 +00:00
|
|
|
win.on('close', (event) => {
|
|
|
|
if (!isQuitting) {
|
|
|
|
event.preventDefault();
|
|
|
|
win?.hide();
|
2024-10-12 14:36:15 +00:00
|
|
|
isHidden = true;
|
2024-10-12 12:28:16 +00:00
|
|
|
//event.returnValue = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
|
2024-09-17 18:33:24 +00:00
|
|
|
// Emitted when the window is closed.
|
|
|
|
win.on('closed', () => {
|
|
|
|
// Dereference the window object, usually you would store window
|
|
|
|
// in an array if your app supports multi windows, this is the time
|
|
|
|
// when you should delete the corresponding element.
|
|
|
|
win = null;
|
|
|
|
});
|
|
|
|
|
|
|
|
return win;
|
|
|
|
}
|
|
|
|
|
2024-11-16 13:13:17 +00:00
|
|
|
const createSplashWindow = async (): Promise<BrowserWindow | undefined> => {
|
|
|
|
console.log("createSplashWindow()");
|
2024-11-01 20:45:25 +00:00
|
|
|
|
2024-10-29 17:34:25 +00:00
|
|
|
const window = new BrowserWindow({
|
|
|
|
width: 480,
|
|
|
|
height: 480,
|
|
|
|
transparent: true,
|
|
|
|
frame: false,
|
|
|
|
alwaysOnTop: true,
|
|
|
|
show: false,
|
|
|
|
icon: wdwIcon,
|
|
|
|
minimizable: false,
|
|
|
|
maximizable: false,
|
2024-11-16 13:13:17 +00:00
|
|
|
fullscreen: false,
|
|
|
|
fullscreenable: false,
|
|
|
|
movable: false,
|
|
|
|
resizable: false,
|
|
|
|
closable: true,
|
|
|
|
center: true
|
2024-10-29 17:34:25 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// Path when running electron executable
|
|
|
|
let pathIndex = './splash.html';
|
|
|
|
|
|
|
|
if (fs.existsSync(path.join(dirname, '../dist/splash.html'))) {
|
|
|
|
// Path when running electron in local folder
|
|
|
|
pathIndex = '../dist/splash.html';
|
|
|
|
}
|
|
|
|
|
2024-11-16 13:13:17 +00:00
|
|
|
if (!fs.existsSync(path.join(dirname, pathIndex))) {
|
|
|
|
console.error("createSplashScreen(): path doesn't exists: " + path.join(dirname, pathIndex));
|
|
|
|
window.close();
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
const indexPath = path.join('file:', dirname, pathIndex);
|
|
|
|
|
|
|
|
const url = new URL(indexPath);
|
2024-10-29 17:34:25 +00:00
|
|
|
|
|
|
|
await window.loadURL(url.href);
|
|
|
|
|
|
|
|
await new Promise<void>((resolve) => {
|
|
|
|
setTimeout(() => {
|
|
|
|
window.show();
|
|
|
|
resolve();
|
2024-11-08 14:57:03 +00:00
|
|
|
}, 400);
|
2024-10-29 17:34:25 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
return window;
|
|
|
|
}
|
|
|
|
|
2024-10-21 20:36:05 +00:00
|
|
|
// #endregion
|
|
|
|
|
|
|
|
// #region WiFi
|
|
|
|
|
2024-11-16 00:23:24 +00:00
|
|
|
async function isWifiConnected() {
|
|
|
|
let connected: boolean = false;
|
2024-11-11 23:26:17 +00:00
|
|
|
|
2024-11-16 00:23:24 +00:00
|
|
|
try {
|
|
|
|
connected = await NetworkUtils.isConnectedToWiFi();
|
2024-11-11 23:26:17 +00:00
|
|
|
}
|
2024-11-16 00:23:24 +00:00
|
|
|
catch (error: any) {
|
|
|
|
console.error(error);
|
|
|
|
connected = false;
|
2024-11-11 23:26:17 +00:00
|
|
|
}
|
2024-10-14 21:08:02 +00:00
|
|
|
|
2024-11-16 00:23:24 +00:00
|
|
|
win?.webContents.send('is-wifi-connected-result', connected);
|
2024-10-14 21:08:02 +00:00
|
|
|
}
|
|
|
|
|
2024-10-21 20:36:05 +00:00
|
|
|
// #endregion
|
|
|
|
|
|
|
|
// #region monerod
|
|
|
|
|
2024-11-16 00:23:24 +00:00
|
|
|
async function getMonerodVersion(monerodFilePath: string): Promise<void> {
|
|
|
|
const proc = new MonerodProcess({
|
|
|
|
monerodCmd: monerodFilePath,
|
|
|
|
isExe: true
|
2024-11-15 18:15:04 +00:00
|
|
|
});
|
|
|
|
|
2024-11-16 00:23:24 +00:00
|
|
|
try {
|
|
|
|
console.log("Before proc.getVersion()");
|
|
|
|
const version = await proc.getVersion();
|
|
|
|
console.log("After proc.getVersion()");
|
|
|
|
win?.webContents.send('monero-version', version);
|
|
|
|
}
|
|
|
|
catch(error: any) {
|
|
|
|
const err = (error instanceof Error) ? error.message : `${error}`;
|
|
|
|
win?.webContents.send('monero-version-error', err);
|
|
|
|
}
|
2024-09-26 16:45:28 +00:00
|
|
|
}
|
|
|
|
|
2024-11-16 00:23:24 +00:00
|
|
|
async function checkValidMonerodPath(monerodPath: string): Promise<void> {
|
|
|
|
const valid = await MonerodProcess.isValidMonerodPath(monerodPath);
|
|
|
|
|
|
|
|
win?.webContents.send('on-check-valid-monerod-path', valid);
|
2024-10-18 20:41:53 +00:00
|
|
|
}
|
|
|
|
|
2024-11-16 00:23:24 +00:00
|
|
|
async function startMoneroDaemon(commandOptions: string[]): Promise<MonerodProcess> {
|
2024-10-03 20:35:30 +00:00
|
|
|
const monerodPath = commandOptions.shift();
|
|
|
|
|
|
|
|
if (!monerodPath) {
|
2024-10-18 20:41:53 +00:00
|
|
|
const error = `Invalid monerod path provided: ${monerodPath}`;
|
|
|
|
win?.webContents.send('monero-stderr', error);
|
2024-10-16 16:18:43 +00:00
|
|
|
throw new Error("Invalid monerod path provided");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (monerodProcess != null) {
|
2024-10-18 20:41:53 +00:00
|
|
|
const error: string = 'Monero daemon already started';
|
|
|
|
win?.webContents.send('monero-stderr', error);
|
2024-10-16 16:18:43 +00:00
|
|
|
throw new Error("Monerod already started");
|
2024-10-03 20:35:30 +00:00
|
|
|
}
|
2024-10-14 21:08:02 +00:00
|
|
|
|
2024-11-01 20:45:25 +00:00
|
|
|
commandOptions.push('--non-interactive');
|
|
|
|
|
2024-11-16 13:13:17 +00:00
|
|
|
const monerodProc = new MonerodProcess({
|
2024-11-16 00:23:24 +00:00
|
|
|
monerodCmd: monerodPath,
|
|
|
|
flags: commandOptions,
|
|
|
|
isExe: true
|
|
|
|
});
|
2024-10-14 21:08:02 +00:00
|
|
|
|
2024-11-16 13:13:17 +00:00
|
|
|
monerodProc.onStdOut((data) => {
|
2024-09-25 21:24:46 +00:00
|
|
|
win?.webContents.send('monero-stdout', `${data}`);
|
2024-09-22 13:55:03 +00:00
|
|
|
});
|
|
|
|
|
2024-11-16 13:13:17 +00:00
|
|
|
monerodProc.onStdErr((data) => {
|
2024-09-25 21:24:46 +00:00
|
|
|
win?.webContents.send('monero-stderr', `${data}`);
|
2024-09-22 13:55:03 +00:00
|
|
|
});
|
|
|
|
|
2024-11-16 13:13:17 +00:00
|
|
|
monerodProc.onError((err: Error) => {
|
2024-11-15 18:15:04 +00:00
|
|
|
win?.webContents.send('monero-stderr', `${err.message}`);
|
|
|
|
});
|
|
|
|
|
2024-11-16 13:13:17 +00:00
|
|
|
monerodProc.onClose((_code: number | null) => {
|
2024-11-16 00:23:24 +00:00
|
|
|
const code = _code != null ? _code : -Number.MAX_SAFE_INTEGER;
|
2024-11-16 13:13:17 +00:00
|
|
|
const msg = `Process monerod ${monerodProc.pid} exited with code: ${code}`;
|
|
|
|
console.log(msg);
|
|
|
|
win?.webContents.send('monero-stdout', msg);
|
2024-09-28 14:31:51 +00:00
|
|
|
win?.webContents.send('monero-close', code);
|
2024-10-16 16:18:43 +00:00
|
|
|
monerodProcess = null;
|
2024-09-22 13:55:03 +00:00
|
|
|
});
|
|
|
|
|
2024-11-16 13:13:17 +00:00
|
|
|
monerodProcess = null;
|
|
|
|
monerodProcess = monerodProc;
|
|
|
|
|
2024-11-16 00:23:24 +00:00
|
|
|
try {
|
|
|
|
await monerodProcess.start();
|
|
|
|
win?.webContents.send('monerod-started', true);
|
|
|
|
}
|
|
|
|
catch(error: any) {
|
|
|
|
win?.webContents.send('monerod-started', false);
|
|
|
|
}
|
|
|
|
|
2024-09-22 13:55:03 +00:00
|
|
|
return monerodProcess;
|
|
|
|
}
|
|
|
|
|
2024-11-16 00:23:24 +00:00
|
|
|
async function monitorMonerod(): Promise<void> {
|
2024-10-16 16:18:43 +00:00
|
|
|
if (!monerodProcess) {
|
|
|
|
win?.webContents.send('on-monitor-monerod-error', 'Monerod not running');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-11-16 00:23:24 +00:00
|
|
|
try {
|
|
|
|
const stats = await monerodProcess.getStats();
|
|
|
|
win?.webContents.send('on-monitor-monerod', stats);
|
2024-10-16 16:18:43 +00:00
|
|
|
}
|
2024-11-16 00:23:24 +00:00
|
|
|
catch(error: any) {
|
|
|
|
let message: string;
|
2024-10-16 16:18:43 +00:00
|
|
|
|
2024-11-16 00:23:24 +00:00
|
|
|
if (error instanceof Error) {
|
|
|
|
message = error.message;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
message = `${error}`;
|
2024-10-16 16:18:43 +00:00
|
|
|
}
|
|
|
|
|
2024-11-16 00:23:24 +00:00
|
|
|
win?.webContents.send('on-monitor-monerod-error', message);
|
|
|
|
}
|
2024-10-16 16:18:43 +00:00
|
|
|
}
|
|
|
|
|
2024-10-21 20:36:05 +00:00
|
|
|
// #endregion
|
|
|
|
|
|
|
|
// #region Download Utils
|
|
|
|
|
2024-11-16 00:23:24 +00:00
|
|
|
async function downloadAndVerifyHash(hashUrl: string, fileName: string, filePath: string): Promise<boolean> {
|
|
|
|
const hashFileName = await FileUtils.downloadFile(hashUrl, app.getPath('temp'), () => {});
|
2024-09-29 22:29:16 +00:00
|
|
|
const hashFilePath = `${app.getPath('temp')}/${hashFileName}`;
|
2024-09-28 14:31:51 +00:00
|
|
|
|
|
|
|
const hashContent = fs.readFileSync(hashFilePath, 'utf8');
|
|
|
|
const hashLines = hashContent.split('\n');
|
|
|
|
let expectedHash: string | null = null;
|
|
|
|
|
|
|
|
for (const line of hashLines) {
|
|
|
|
const match = line.match(/^(\w+)\s+(\S+)/);
|
|
|
|
if (match && match[2] === fileName) {
|
|
|
|
expectedHash = match[1];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!expectedHash) {
|
|
|
|
throw new Error('Hash not found for the downloaded file.');
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verifica l'hash del file scaricato
|
2024-11-16 00:23:24 +00:00
|
|
|
return await FileUtils.checkFileHash(`${filePath}/${fileName}`, expectedHash);
|
2024-09-28 14:31:51 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Funzione per verificare l'hash del file
|
2024-10-25 16:17:48 +00:00
|
|
|
|
2024-10-21 20:36:05 +00:00
|
|
|
// #endregion
|
|
|
|
|
2024-10-18 20:41:53 +00:00
|
|
|
function showNotification(options?: NotificationConstructorOptions): void {
|
2024-10-19 13:57:57 +00:00
|
|
|
if (!options) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!options.icon) {
|
|
|
|
options.icon = wdwIcon;
|
|
|
|
}
|
|
|
|
|
2024-10-18 20:41:53 +00:00
|
|
|
new Notification(options).show();
|
|
|
|
}
|
|
|
|
|
2024-11-16 00:23:24 +00:00
|
|
|
function showSecurityWarning(msg: string): void {
|
|
|
|
if (win) {
|
|
|
|
dialog.showMessageBoxSync(win, {
|
|
|
|
type: 'warning',
|
|
|
|
title: 'Security Warning',
|
|
|
|
message: msg
|
|
|
|
});
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
dialog.showErrorBox('Security Warning', msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
console.warn(msg);
|
|
|
|
}
|
|
|
|
|
2024-09-17 18:33:24 +00:00
|
|
|
try {
|
|
|
|
// This method will be called when Electron has finished
|
|
|
|
// initialization and is ready to create browser windows.
|
|
|
|
// Some APIs can only be used after this event occurs.
|
|
|
|
// Added 400 ms to fix the black background issue while using transparent window. More detais at https://github.com/electron/electron/issues/15947
|
2024-10-29 14:42:58 +00:00
|
|
|
app.on('ready', () => {
|
|
|
|
Menu.setApplicationMenu(null);
|
|
|
|
const gotInstanceLock = app.requestSingleInstanceLock();
|
|
|
|
|
|
|
|
if (!gotInstanceLock) {
|
2024-11-16 13:13:17 +00:00
|
|
|
dialog.showErrorBox('', 'Another instance of Monerod GUI is running');
|
2024-10-29 14:42:58 +00:00
|
|
|
app.quit();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-10-29 17:34:25 +00:00
|
|
|
setTimeout(async () => {
|
|
|
|
const splash = await createSplashWindow();
|
2024-11-16 13:13:17 +00:00
|
|
|
await createWindow();
|
2024-10-29 17:34:25 +00:00
|
|
|
|
|
|
|
await new Promise<void>((resolve, reject) => {
|
|
|
|
try {
|
|
|
|
setTimeout(() => {
|
2024-11-06 20:19:30 +00:00
|
|
|
if (splash) splash.close();
|
2024-11-16 00:23:24 +00:00
|
|
|
if (!AppMainProcess.startMinized) {
|
2024-11-06 20:19:30 +00:00
|
|
|
win?.show();
|
|
|
|
win?.maximize();
|
|
|
|
}
|
2024-10-29 17:34:25 +00:00
|
|
|
resolve();
|
|
|
|
}, 2600);
|
|
|
|
}
|
|
|
|
catch(error: any) {
|
|
|
|
reject(error);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}, 400);
|
2024-10-29 14:42:58 +00:00
|
|
|
});
|
2024-09-17 18:33:24 +00:00
|
|
|
|
|
|
|
// Quit when all windows are closed.
|
|
|
|
app.on('window-all-closed', () => {
|
|
|
|
// On OS X it is common for applications and their menu bar
|
|
|
|
// to stay active until the user quits explicitly with Cmd + Q
|
|
|
|
if (process.platform !== 'darwin') {
|
|
|
|
app.quit();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2024-11-16 13:13:17 +00:00
|
|
|
app.on('activate', async () => {
|
2024-09-17 18:33:24 +00:00
|
|
|
// On OS X it's common to re-create a window in the app when the
|
|
|
|
// dock icon is clicked and there are no other windows open.
|
|
|
|
if (win === null) {
|
2024-11-16 13:13:17 +00:00
|
|
|
await createWindow();
|
2024-09-17 18:33:24 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2024-10-12 12:28:16 +00:00
|
|
|
app.on('before-quit', () => {
|
|
|
|
isQuitting = true;
|
|
|
|
});
|
|
|
|
|
2024-10-23 15:17:33 +00:00
|
|
|
// #region Security
|
|
|
|
|
2024-11-16 00:23:24 +00:00
|
|
|
app.on('web-contents-created', (event, webContents: WebContents) => {
|
|
|
|
webContents.setWindowOpenHandler((details: HandlerDetails) => {
|
|
|
|
const msg = `Prevented unsafe content: ${details.url}`;
|
|
|
|
showSecurityWarning(msg);
|
2024-10-23 15:17:33 +00:00
|
|
|
console.warn(details);
|
2024-11-16 00:23:24 +00:00
|
|
|
|
2024-10-23 15:17:33 +00:00
|
|
|
return { action: 'deny' };
|
|
|
|
});
|
|
|
|
|
2024-11-16 00:23:24 +00:00
|
|
|
webContents.on('will-navigate', (event: Event<WebContentsWillNavigateEventParams>, navigationUrl: string) => {
|
2024-10-23 15:17:33 +00:00
|
|
|
event.preventDefault();
|
2024-11-16 00:23:24 +00:00
|
|
|
const msg = `Prevented unsage window navigation to ${navigationUrl}`;
|
|
|
|
showSecurityWarning(msg);
|
|
|
|
});
|
|
|
|
});
|
2024-11-12 21:05:49 +00:00
|
|
|
|
2024-11-16 00:23:24 +00:00
|
|
|
// #endregion
|
2024-11-12 21:05:49 +00:00
|
|
|
|
2024-11-16 00:23:24 +00:00
|
|
|
ipcMain.handle('is-on-battery-power', async (event: IpcMainInvokeEvent) => {
|
|
|
|
const onBattery = await BatteryUtils.isOnBatteryPower();
|
|
|
|
win?.webContents.send('on-is-on-battery-power', onBattery);
|
2024-11-10 20:40:33 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
powerMonitor.on('on-ac', () => win?.webContents.send('on-ac'));
|
|
|
|
powerMonitor.on('on-battery', () => win?.webContents.send('on-battery'));
|
|
|
|
|
2024-11-16 00:23:24 +00:00
|
|
|
ipcMain.handle('is-auto-launched', (event: IpcMainInvokeEvent) => {
|
|
|
|
win?.webContents.send('on-is-auto-launched', AppMainProcess.autoLaunched);
|
2024-10-23 17:01:24 +00:00
|
|
|
});
|
|
|
|
|
2024-11-16 00:23:24 +00:00
|
|
|
ipcMain.handle('quit', async (event: IpcMainInvokeEvent) => {
|
2024-10-13 18:47:25 +00:00
|
|
|
isQuitting = true;
|
2024-11-16 00:23:24 +00:00
|
|
|
|
|
|
|
if (monerodProcess) {
|
|
|
|
await monerodProcess.stop();
|
|
|
|
}
|
|
|
|
|
2024-11-07 17:07:31 +00:00
|
|
|
tray.destroy();
|
|
|
|
win?.close();
|
|
|
|
win?.destroy();
|
2024-10-13 18:47:25 +00:00
|
|
|
app.quit();
|
|
|
|
});
|
|
|
|
|
2024-10-24 14:36:33 +00:00
|
|
|
ipcMain.handle('start-monerod', (event: IpcMainInvokeEvent, configFilePath: string[]) => {
|
2024-09-26 16:45:28 +00:00
|
|
|
startMoneroDaemon(configFilePath);
|
2024-09-22 13:55:03 +00:00
|
|
|
})
|
|
|
|
|
2024-10-24 14:36:33 +00:00
|
|
|
ipcMain.handle('get-monero-version', (event: IpcMainInvokeEvent, configFilePath: string) => {
|
2024-09-26 16:45:28 +00:00
|
|
|
getMonerodVersion(configFilePath);
|
|
|
|
});
|
|
|
|
|
2024-09-28 14:31:51 +00:00
|
|
|
// Gestione IPC
|
2024-10-24 14:36:33 +00:00
|
|
|
ipcMain.handle('download-monerod', async (event: IpcMainInvokeEvent, downloadUrl: string, destination: string) => {
|
2024-09-28 14:31:51 +00:00
|
|
|
try {
|
2024-09-29 22:29:16 +00:00
|
|
|
//const fileName = path.basename(downloadUrl);
|
|
|
|
//const filePath = path.join(destination, fileName);
|
2024-09-28 14:31:51 +00:00
|
|
|
const hashUrl = 'https://www.getmonero.org/downloads/hashes.txt';
|
|
|
|
|
|
|
|
// Inizializza il progresso
|
2024-10-09 18:53:38 +00:00
|
|
|
event.sender.send('download-progress', { progress: 0, status: 'Starting download' });
|
2024-10-29 15:29:32 +00:00
|
|
|
|
|
|
|
win?.setProgressBar(0, {
|
|
|
|
mode: 'normal'
|
|
|
|
});
|
2024-09-28 14:31:51 +00:00
|
|
|
|
|
|
|
// Scarica il file Monero
|
2024-11-16 00:23:24 +00:00
|
|
|
const fileName = await FileUtils.downloadFile(downloadUrl, destination, (progress) => {
|
2024-10-29 15:29:32 +00:00
|
|
|
win?.setProgressBar(progress, {
|
|
|
|
mode: 'normal'
|
|
|
|
});
|
|
|
|
|
2024-10-09 18:53:38 +00:00
|
|
|
event.sender.send('download-progress', { progress, status: 'Downloading' });
|
2024-09-28 14:31:51 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// Scarica e verifica l'hash
|
2024-10-09 18:53:38 +00:00
|
|
|
event.sender.send('download-progress', { progress: 100, status: 'Verifying hash' });
|
2024-10-29 15:29:32 +00:00
|
|
|
win?.setProgressBar(100, {
|
|
|
|
mode: 'indeterminate'
|
|
|
|
});
|
|
|
|
|
2024-09-29 22:29:16 +00:00
|
|
|
await downloadAndVerifyHash(hashUrl, fileName, destination);
|
2024-09-28 14:31:51 +00:00
|
|
|
|
|
|
|
// Estrai il file
|
2024-10-09 18:53:38 +00:00
|
|
|
const fPath = `${destination}/${fileName}`;
|
|
|
|
event.sender.send('download-progress', { progress: 100, status: 'Extracting' });
|
2024-11-16 00:23:24 +00:00
|
|
|
const extractedDir = await FileUtils.extract(fPath, destination);
|
2024-09-28 14:31:51 +00:00
|
|
|
|
2024-10-09 18:53:38 +00:00
|
|
|
event.sender.send('download-progress', { progress: 100, status: 'Download and extraction completed successfully' });
|
2024-11-01 20:45:25 +00:00
|
|
|
event.sender.send('download-progress', { progress: 200, status: os.platform() == 'win32' ? extractedDir : `${destination}/${extractedDir}` });
|
2024-10-29 15:29:32 +00:00
|
|
|
|
|
|
|
win?.setProgressBar(100, {
|
|
|
|
mode: 'none'
|
|
|
|
});
|
|
|
|
|
2024-09-28 14:31:51 +00:00
|
|
|
} catch (error) {
|
2024-11-13 17:00:45 +00:00
|
|
|
event.sender.send('download-progress', { progress: 0, status: `${error}` });
|
2024-11-01 20:45:25 +00:00
|
|
|
win?.setProgressBar(0, {
|
|
|
|
mode: 'error'
|
|
|
|
});
|
2024-09-28 14:31:51 +00:00
|
|
|
}
|
|
|
|
});
|
2024-10-16 15:19:49 +00:00
|
|
|
|
2024-11-13 17:00:45 +00:00
|
|
|
ipcMain.handle('download-file', async (event: IpcMainInvokeEvent, url: string, destination: string) => {
|
|
|
|
try {
|
|
|
|
event.sender.send('download-file-progress', { progress: 0, status: 'Starting download' });
|
|
|
|
|
2024-11-16 00:23:24 +00:00
|
|
|
const fileName = await FileUtils.downloadFile(url, destination, (progress) => {
|
2024-11-13 17:00:45 +00:00
|
|
|
win?.setProgressBar(progress, {
|
|
|
|
mode: 'normal'
|
|
|
|
});
|
|
|
|
|
|
|
|
event.sender.send('download-file-progress', { progress, status: 'Downloading' });
|
|
|
|
});
|
|
|
|
|
|
|
|
win?.setProgressBar(0, {
|
|
|
|
mode: 'none'
|
|
|
|
});
|
|
|
|
|
|
|
|
event.sender.send('download-file-complete', `${destination}${separator}${fileName}`);
|
|
|
|
}
|
|
|
|
catch(error: any) {
|
|
|
|
console.error(error);
|
|
|
|
event.sender.send('download-file-error', `${error}`);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2024-10-31 15:47:48 +00:00
|
|
|
ipcMain.handle('read-file', (event: IpcMainInvokeEvent, filePath: string) => {
|
|
|
|
fs.readFile(filePath, 'utf-8', (err, data) => {
|
|
|
|
if (err != null) {
|
|
|
|
win?.webContents.send('on-read-file-error', `${err}`);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
win?.webContents.send('on-read-file', data);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
ipcMain.handle('save-file', async (event: IpcMainInvokeEvent, defaultPath: string, content: string) => {
|
|
|
|
if (!win) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const result = await dialog.showSaveDialog(win, {
|
|
|
|
title: 'Save File',
|
|
|
|
defaultPath: defaultPath,
|
|
|
|
properties: [
|
|
|
|
'showOverwriteConfirmation'
|
|
|
|
]
|
|
|
|
});
|
|
|
|
|
|
|
|
if (result.canceled) {
|
|
|
|
win.webContents.send('on-save-file', '');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
fs.writeFileSync(result.filePath, content);
|
|
|
|
|
|
|
|
win.webContents.send('on-save-file', result.filePath);
|
|
|
|
}
|
|
|
|
catch(error: any) {
|
|
|
|
win.webContents.send('on-save-file-error', `${error}`);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2024-10-24 14:36:33 +00:00
|
|
|
ipcMain.handle('select-file', async (event: IpcMainInvokeEvent, extensions?: string[]) => {
|
2024-10-16 15:19:49 +00:00
|
|
|
if (!win)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const result = await dialog.showOpenDialog(win, {
|
|
|
|
title: 'Select File',
|
2024-10-19 10:27:40 +00:00
|
|
|
filters: extensions ? [{
|
|
|
|
name: 'filter',
|
|
|
|
extensions: extensions
|
|
|
|
}] : [],
|
2024-10-16 15:19:49 +00:00
|
|
|
properties: ['openFile']
|
|
|
|
});
|
|
|
|
|
|
|
|
const path = result.canceled ? null : result.filePaths[0];
|
|
|
|
|
|
|
|
win.webContents.send('selected-file', path ? `${path}` : '');
|
|
|
|
});
|
2024-09-28 14:31:51 +00:00
|
|
|
|
2024-10-24 14:36:33 +00:00
|
|
|
ipcMain.handle('select-folder', async (event: IpcMainInvokeEvent) => {
|
2024-10-16 15:19:49 +00:00
|
|
|
if (!win) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const result = await dialog.showOpenDialog(win, {
|
|
|
|
title: 'Select Folder',
|
2024-10-09 18:53:38 +00:00
|
|
|
properties: ['openDirectory'], // Specifica che vogliamo solo cartelle
|
|
|
|
});
|
|
|
|
|
|
|
|
const path = result.canceled ? null : result.filePaths[0];
|
|
|
|
|
2024-10-16 15:19:49 +00:00
|
|
|
win.webContents.send('selected-folder', path ? `${path}` : '');
|
2024-10-09 18:53:38 +00:00
|
|
|
});
|
2024-09-28 14:31:51 +00:00
|
|
|
|
2024-10-31 15:47:48 +00:00
|
|
|
ipcMain.handle('get-path', (event: IpcMainInvokeEvent, path: 'home' | 'appData' | 'userData' | 'sessionData' | 'temp' | 'exe' | 'module' | 'desktop' | 'documents' | 'downloads' | 'music' | 'pictures' | 'videos' | 'recent' | 'logs' | 'crashDumps') => {
|
|
|
|
win?.webContents.send('on-get-path', app.getPath(path));
|
|
|
|
});
|
|
|
|
|
2024-10-24 14:36:33 +00:00
|
|
|
ipcMain.handle('is-wifi-connected', async (event: IpcMainInvokeEvent) => {
|
2024-10-14 21:08:02 +00:00
|
|
|
isWifiConnected();
|
2024-10-09 21:40:32 +00:00
|
|
|
});
|
|
|
|
|
2024-10-24 14:36:33 +00:00
|
|
|
ipcMain.handle('get-os-type', (event: IpcMainInvokeEvent) => {
|
2024-10-09 21:40:32 +00:00
|
|
|
win?.webContents.send('got-os-type', { platform: os.platform(), arch: os.arch() });
|
|
|
|
})
|
|
|
|
|
2024-10-16 16:18:43 +00:00
|
|
|
ipcMain.handle('monitor-monerod', (event: IpcMainInvokeEvent) => {
|
|
|
|
monitorMonerod();
|
|
|
|
});
|
|
|
|
|
2024-10-18 20:41:53 +00:00
|
|
|
ipcMain.handle('check-valid-monerod-path', (event: IpcMainInvokeEvent, path: string) => {
|
|
|
|
checkValidMonerodPath(path);
|
|
|
|
})
|
|
|
|
|
|
|
|
ipcMain.handle('show-notification', (event: IpcMainInvokeEvent, options?: NotificationConstructorOptions) => {
|
|
|
|
showNotification(options);
|
|
|
|
});
|
|
|
|
|
2024-10-24 14:36:33 +00:00
|
|
|
// #region Auto Launch
|
|
|
|
|
2024-11-16 00:23:24 +00:00
|
|
|
ipcMain.handle('is-auto-launch-enabled', async (event: IpcMainInvokeEvent) => {
|
|
|
|
const enabled = await AppMainProcess.isAutoLaunchEnabled();
|
|
|
|
win?.webContents.send('on-is-auto-launch-enabled', enabled);
|
2024-10-21 20:36:05 +00:00
|
|
|
});
|
|
|
|
|
2024-11-16 00:23:24 +00:00
|
|
|
ipcMain.handle('enable-auto-launch', async (event: IpcMainInvokeEvent, minimized: boolean) => {
|
|
|
|
try {
|
|
|
|
await AppMainProcess.enableAutoLaunch(minimized);
|
|
|
|
win?.webContents.send('on-enable-auto-launch-success');
|
|
|
|
}
|
|
|
|
catch(error: any) {
|
|
|
|
const err = (error instanceof Error) ? error.message : `${error}`;
|
2024-10-21 20:36:05 +00:00
|
|
|
|
2024-11-16 00:23:24 +00:00
|
|
|
win?.webContents.send('on-enable-auto-launch-error', err);
|
|
|
|
}
|
2024-10-21 20:36:05 +00:00
|
|
|
});
|
|
|
|
|
2024-11-16 00:23:24 +00:00
|
|
|
ipcMain.handle('get-battery-level', async (event: IpcMainInvokeEvent) => {
|
|
|
|
win?.webContents.send('on-get-battery-level', await BatteryUtils.getLevel());
|
2024-11-08 17:36:05 +00:00
|
|
|
});
|
|
|
|
|
2024-11-16 00:23:24 +00:00
|
|
|
ipcMain.handle('disable-auto-launch', async (event: IpcMainInvokeEvent) => {
|
|
|
|
try {
|
|
|
|
await AppMainProcess.disableAutoLaunch();
|
|
|
|
win?.webContents.send('on-disable-auto-launch-success');
|
|
|
|
}
|
|
|
|
catch(error: any) {
|
|
|
|
const err = (error instanceof Error) ? error.message : `${error}`;
|
|
|
|
win?.webContents.send('on-disable-auto-launch-error', err);
|
|
|
|
}
|
2024-10-21 20:36:05 +00:00
|
|
|
});
|
|
|
|
|
2024-10-24 14:36:33 +00:00
|
|
|
// #endregion
|
|
|
|
|
2024-11-13 17:00:45 +00:00
|
|
|
ipcMain.handle('show-error-box', (event: IpcMainInvokeEvent, title: string, content: string) => {
|
2024-11-18 01:04:16 +00:00
|
|
|
if (win) {
|
|
|
|
dialog.showMessageBoxSync(win, {
|
|
|
|
message: content,
|
|
|
|
type: 'error',
|
|
|
|
title: title != '' ? title : 'Error'
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
2024-11-13 17:00:45 +00:00
|
|
|
dialog.showErrorBox(title, content);
|
|
|
|
});
|
|
|
|
|
2024-11-01 13:39:47 +00:00
|
|
|
ipcMain.handle('set-tray-item-enabled', (event: IpcMainInvokeEvent, id: string, enabled: boolean) => {
|
|
|
|
setTrayItemEnabled(id, enabled);
|
|
|
|
});
|
|
|
|
|
|
|
|
ipcMain.handle('set-tray-tool-tip', (event: IpcMainInvokeEvent, toolTip: string) => {
|
|
|
|
tray.setToolTip(toolTip);
|
|
|
|
});
|
|
|
|
|
2024-11-16 00:23:24 +00:00
|
|
|
ipcMain.handle('is-portable', (event: IpcMainInvokeEvent) => {
|
|
|
|
win?.webContents.send('on-is-portable', AppMainProcess.isPortable);
|
2024-10-21 20:36:05 +00:00
|
|
|
});
|
|
|
|
|
2024-11-06 20:19:30 +00:00
|
|
|
ipcMain.handle('copy-to-clipboard', (event: IpcMainInvokeEvent, content: string) => {
|
|
|
|
clipboard.writeText(content, "selection");
|
|
|
|
});
|
|
|
|
|
2024-11-13 17:00:45 +00:00
|
|
|
} catch (e: any) {
|
2024-09-17 18:33:24 +00:00
|
|
|
// Catch Error
|
2024-10-16 16:18:43 +00:00
|
|
|
console.error(e);
|
2024-11-13 17:00:45 +00:00
|
|
|
|
|
|
|
dialog.showErrorBox('', `${e}`);
|
|
|
|
|
|
|
|
app.quit();
|
2024-09-17 18:33:24 +00:00
|
|
|
}
|
2024-11-08 14:57:03 +00:00
|
|
|
|