const {app, BrowserWindow, screen, ipcMain, dialog} = require('electron'); const fs = require('fs'); const path = require('path'); const crypto = require('crypto'); const {fork} = require('child_process'); const {encryptFileInPlace, decryptFileInPlace} = require('./aes_encrypt'); const {lockFile, unlockFile} = require("../../helpers/lock_mechanism"); const isMac = process.platform === 'darwin'; let html_page = undefined; let mainWindow = undefined; let alertWindow = undefined; let fetcherProcess = null; let backupProcess = null; let externalEndpointsProcess = null; let sendFileProcess = null; const createInitialKeys = () => { const SECRET_KEY = crypto.randomBytes(32); const IV = crypto.randomBytes(16); const secretKeyPath = path.join(__dirname, '..', '..', 'secret.key'); const ivPath = path.join(__dirname, '..', '..', 'iv.key'); fs.writeFileSync(secretKeyPath, SECRET_KEY); console.log(`Secret Key saved to ${secretKeyPath}`); fs.writeFileSync(ivPath, IV); console.log(`IV saved to ${ivPath}`); } const deleteMainComponentsAtErrorStart = () => { const ipConfigPath = path.join(__dirname, '..', '..', 'ipConfig.json'); const loginDataPath = path.join(__dirname, '..', '..', 'loginData.json'); fs.unlink(ipConfigPath, (err) => { if (err) { console.error('Error deleting file:', err); return; } console.log('File deleted successfully: ' + 'ipConfig.json'); }); fs.unlink(loginDataPath, (err) => { if (err) { console.error('Error deleting file:', err); return; } console.log('File deleted successfully: ' + 'loginData.json'); }); } const checkForServerConnection = async () => { const pathToIpConfig = path.join(__dirname, '..', '..', 'ipConfig.json'); try { await fs.promises.access(pathToIpConfig); await lockFile(pathToIpConfig); await decryptFileInPlace(pathToIpConfig); const ipConfig = await fs.promises.readFile(pathToIpConfig, 'utf-8'); const { ip } = JSON.parse(ipConfig); const response = await fetch(`http://${ip}/heartbeat`); await encryptFileInPlace(pathToIpConfig) await unlockFile(pathToIpConfig); return response.ok; } catch (error) { console.error("Error:", error); return false; } }; const createMainWindow = (async (title, width, height) => { mainWindow = new BrowserWindow({ title: title, width: width, height: height, resizable: false, icon: path.join(__dirname, '..', 'renderer', 'assets', 'hard-disk.png'), webPreferences: { preload: path.join(__dirname, 'preload.js') } }); try { await fs.promises.access(path.join(__dirname, '..', '..', 'secret.key'), fs.constants.F_OK); await fs.promises.access(path.join(__dirname, '..', '..', 'iv.key'), fs.constants.F_OK); } catch (err) { createInitialKeys(); deleteMainComponentsAtErrorStart(); } html_page = await checkForServerConnection() ? 'login.html' : 'ip_submit.html'; //mainWindow.setMenu(null); mainWindow.loadFile(path.join(__dirname, '..', 'renderer', 'html', html_page)) .then(() => { console.log('Main window loaded!') }) .catch(err => console.error('Failed to load main window:', err)); }); const createAlertWindow = (title, width, height) => { alertWindow = new BrowserWindow({ width: width, height: height, title: title, icon: path.join(__dirname, '..', 'renderer', 'assets', 'hard-disk.png'), resizable: false, webPreferences: { nodeIntegration: false, preload: path.join(__dirname, 'preload.js') } }); //alertWindow.setMenu(null); alertWindow.loadFile(path.join(__dirname, '..', 'renderer', 'html', 'alert_modal.html')).then(() => { console.log('Alert window loaded!') }) .catch(err => console.error('Failed to load alert window:', err)); alertWindow.on('closed', () => { alertWindow = undefined; }); } function showAlert(message) { if (alertWindow === undefined) { const title = 'Alert'; const mainScreen = screen.getPrimaryDisplay(); const {width, height} = mainScreen.size; createAlertWindow(title, width / 4, height / 4); } alertWindow.webContents.once('dom-ready', () => { alertWindow.webContents.executeJavaScript(`showAlert("${message}")`); }); } app.whenReady().then(() => { const title = "Application"; const mainScreen = screen.getPrimaryDisplay(); const {width, height} = mainScreen.size; createMainWindow(title, width / 1.5, height / 1.5); app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) { createMainWindow(title, width, height); } }); }); app.on('before-quit', () => { if (backupProcess !== null) { backupProcess.kill(); } if (externalEndpointsProcess !== null) { externalEndpointsProcess.kill(); } if (fetcherProcess !== null) { fetcherProcess.kill(); } if (sendFileProcess !== null) { sendFileProcess.kill(); } }); app.on('window-all-closed', () => { if (!isMac) { app.quit(); } }); ipcMain.handle('write-file', async (event, fileName, content) => { try { let filePath = path.join(__dirname, '..', '..', fileName); await lockFile(filePath); await fs.promises.writeFile(filePath, content); await encryptFileInPlace(filePath); await unlockFile(filePath) console.log(`File successfully written to ${filePath}`); return {success: true}; } catch (error) { console.error('Failed to write file:', error); return {success: false, error: error.message}; } }); ipcMain.handle('delete-file', async (event, fileName) => { try { let filePath = path.join(__dirname, '..', '..', fileName); await fs.promises.unlink(filePath); console.log(`File ${filePath} successfully deleted`); return {success: true}; } catch (error) { console.error('Failed to delete file:', error); return {success: false, error: error.message}; } }); ipcMain.handle('read-file', async (event, fileName) => { try { let filePath = path.join(__dirname, '..', '..', fileName); await lockFile(filePath); await decryptFileInPlace(filePath); const content = await fs.promises.readFile(filePath, 'utf-8'); await encryptFileInPlace(filePath); await unlockFile(filePath); return {success: true, content}; } catch (error) { console.error('Error reading file:', error); return {success: false, error: error.message}; } }); ipcMain.handle('change-content', async (event, nextPage) => { try { html_page = nextPage; await mainWindow.loadFile(path.join(__dirname, '..', 'renderer', 'html', nextPage)); return true; } catch (error) { console.error('Error changing content:', error); return false; } }); ipcMain.handle('open-dir-dialog', async (event) => { try { const result = await dialog.showOpenDialog({ properties: ['openDirectory'], }); if (result.canceled || result.filePaths.length === 0) { return {canceled: true} } return result.filePaths[0]; } catch (error) { console.error('Error opening file dialog:', error); return null; } }) ipcMain.handle('open-json-dir-config', async (event, fileName) => { try { const result = await dialog.showOpenDialog({ properties: ['openDirectory'], }); if (result.canceled || result.filePaths.length === 0) { return {canceled: true} } const dirPath = result.filePaths[0]; await fs.promises.writeFile( path.join(__dirname, '..', '..', fileName), JSON.stringify({ path: dirPath }, null, 2)); return true; } catch (error) { console.error('Error opening file dialog:', error); return {error: error.message}; } }); ipcMain.handle('open-file-dialog', async (event) => { const result = await dialog.showOpenDialog({ properties: ['openFile'] }); return result.filePaths[0] || ''; }); ipcMain.handle('check-file-exists', async (event, fileName) => { try { const filePath = path.join(__dirname, '..', '..', fileName); return await fs.promises.access(filePath) .then(() => true) .catch(() => false); } catch (error) { console.error('Error checking file existence:', error); throw error; // Propagate the error to the renderer process } }); ipcMain.handle('show-alert', async (event, message) => { showAlert(message); }); ipcMain.on('close-alert-window', () => { if (alertWindow) { alertWindow.close(); alertWindow = undefined; } }); //External processes ipcMain.handle('start-main-processes', async (event, args) => { if (!backupProcess) { backupProcess = fork(path.join(__dirname, '..', '..', 'jobs', 'backup.js'), args, { silent: false }); backupProcess.on('exit', () => { backupProcess = null; }); backupProcess.on('error', (err) => { console.log('Backup process error:', err); }); } if (!fetcherProcess) { fetcherProcess = fork(path.join(__dirname, '..', '..', 'jobs', 'fetcher.js'), args, { silent: false }); fetcherProcess.on('exit', () => { fetcherProcess = null; }); fetcherProcess.on('error', (err) => { console.log('Fetcher process error:', err); }); fetcherProcess.on('message', (message) => { if (message.type === 'startBackup') { backupProcess.send({ type: 'startBackup' }); } }); } if (externalEndpointsProcess) { externalEndpointsProcess = fork(path.join(__dirname, '..', '..', 'jobs', 'external_endpoints.js'), args, { silent: false }); externalEndpointsProcess.on('exit', () => { externalEndpointsProcess = null; }); externalEndpointsProcess.on('error', (err) => { console.log('External endpoints process error:', err); }); } return true; // Indicate that the operation has started }); ipcMain.handle('start-send-file-process', async (event, args) => { if (sendFileProcess === null) { sendFileProcess = fork(path.join(__dirname, '..', '..', 'jobs', 'send_file.js'), args, {silent: false}); sendFileProcess.on('exit', () => { sendFileProcess = null; }); } return true; // Indicate that the operation has started }); ipcMain.handle('kill-before-logout', async(event) =>{ if (backupProcess !== null) { backupProcess.kill('SIGINT'); } if (externalEndpointsProcess !== null) { externalEndpointsProcess.kill('SIGINT'); } if (fetcherProcess !== null) { fetcherProcess.kill('SIGINT'); } if (sendFileProcess !== null) { sendFileProcess.kill('SIGINT'); } }) ipcMain.handle('decrypt-backup-files', async(event, destPath) => { console.log('ai intrat in handle') if(backupProcess != null){ console.log('esti in process'); backupProcess.send({ type: 'decryptBackup', decryptDestPath: destPath }); } })