Skip to content

Commit

Permalink
refactor: refactor Electron main process
Browse files Browse the repository at this point in the history
1. Refactor Electron main process into SRP modules.
2. Migrate nx to latest version.
3. Add loading screen before loading into main screen.
  • Loading branch information
WodenWang820118 committed Sep 12, 2024
1 parent b14db08 commit cac2c60
Show file tree
Hide file tree
Showing 20 changed files with 5,439 additions and 6,594 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,6 @@ testem.log
.DS_Store
Thumbs.db

.nx/cache
.angular
.database.sqlite3
*nx
File renamed without changes.
19 changes: 19 additions & 0 deletions hooks.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const { join } = require('path');
const { runCommand } = require('./main-process/command-utils.cjs');

(async () => {
try {
// Run npm install in the specified directory
await runCommand(
'npm install --prefer-offline --no-audit --progress=false --omit=dev && npm install sqlite3 --prefer-offline --no-audit --progress=false --omit=dev',
{
cwd: join(__dirname, 'dist/nest-backend'),
stdio: 'inherit',
}
);

console.log('npm install completed successfully');
} catch (err) {
console.error(`Error: ${err.message}`);
}
})();
38 changes: 0 additions & 38 deletions hooks.js

This file was deleted.

101 changes: 101 additions & 0 deletions main-process/backend.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
'use strict';
// const { utilityProcess } = require('electron');
const { fork } = require('child_process');
const { join } = require('path');
const constants = require('./constants.cjs');
const environmentUtils = require('./environment-utils.cjs');
const fileUtils = require('./file-utils.cjs');
const pathUtils = require('./path-utils.cjs');

function startBackend(resourcesPath) {
let env;
const rootBackendFolderPath = pathUtils.getRootBackendFolderPath(
environmentUtils.getEnvironment(),
resourcesPath
);
const serverPath = join(rootBackendFolderPath, 'main.js');
const databasePath = join(
rootBackendFolderPath,
constants.ROOT_DATABASE_NAME
);
switch (environmentUtils.getEnvironment()) {
case 'dev':
env = {
NODE_ENV: 'dev',
DATABASE_PATH: databasePath,
PORT: 3000,
};
break;
case 'staging':
env = {
NODE_ENV: 'staging',
DATABASE_PATH: databasePath,
PORT: 3000,
};
break;
case 'prod':
env = {
NODE_ENV: 'prod',
DATABASE_PATH: databasePath,
PORT: 5000,
};
break;
default:
env = {
NODE_ENV: 'prod',
DATABASE_PATH: databasePath,
PORT: 5000,
};
break;
}

fileUtils.writePath(
join(
pathUtils.getRootBackendFolderPath(
environmentUtils.getEnvironment(),
resourcesPath
),
'env.txt'
),
JSON.stringify(env, null, 2)
);

fileUtils.writePath(
join(
pathUtils.getRootBackendFolderPath(
environmentUtils.getEnvironment(),
resourcesPath
),
'serverPath.txt'
),
serverPath
);

// return utilityProcess.fork(serverPath, { env });
return fork(serverPath, { env });
}

async function checkIfPortIsOpen(urls, maxAttempts = 20, timeout = 2000) {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
for (const url of urls) {
try {
const response = await fetch(url);
if (response) {
console.log('Server is ready');
return true; // Port is open
}
} catch (error) {
console.log(`Attempt ${attempt}: Waiting for server to start...`);
}
}
await new Promise((resolve) => setTimeout(resolve, timeout)); // Wait for 2 seconds before retrying
}
throw new Error(
`Failed to connect to the server after ${maxAttempts} attempts`
);
}

module.exports = {
startBackend,
checkIfPortIsOpen,
};
25 changes: 25 additions & 0 deletions main-process/command-utils.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use strict';
const { spawn } = require('child_process');

// Helper function to run a command and return a promise
const runCommand = (command, options) => {
return new Promise((resolve, reject) => {
const process = spawn(command, { ...options, shell: true });

process.on('close', (code) => {
if (code !== 0) {
reject(new Error(`${command} failed with code ${code}`));
} else {
resolve();
}
});

process.on('error', (err) => {
reject(err);
});
});
};

module.exports = {
runCommand,
};
9 changes: 9 additions & 0 deletions main-process/constants.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use strict';

const URLs = ['http://localhost:5000', 'http://localhost:3000'];
const ROOT_DATABASE_NAME = 'database.sqlite3';

module.exports = {
URLs,
ROOT_DATABASE_NAME,
};
6 changes: 6 additions & 0 deletions main-process/environment-utils.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
'use strict';
function getEnvironment() {
return process.env.NODE_ENV || 'prod';
}

module.exports = { getEnvironment };
10 changes: 10 additions & 0 deletions main-process/file-utils.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
'use strict';
const { writeFileSync } = require('fs');

function writePath(filePath, content) {
writeFileSync(filePath, content, 'utf8');
}

module.exports = {
writePath,
};
77 changes: 77 additions & 0 deletions main-process/frontend.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
'use strict';
const { BrowserWindow } = require('electron');
const { join } = require('path');
const pathUtils = require('./path-utils.cjs');
const fileUtils = require('./file-utils.cjs');

let loadingWindow = null;

function createLoadingWindow() {
console.log('Creating loading window');
if (loadingWindow) {
console.log('Loading window already exists');
return loadingWindow;
}

try {
loadingWindow = new BrowserWindow({
width: 400,
height: 200,
frame: false,
transparent: true,
alwaysOnTop: true,
webPreferences: {
nodeIntegration: true,
},
});

loadingWindow.loadFile(join(__dirname, 'loading.html'));
loadingWindow.center();

loadingWindow.on('closed', () => {
loadingWindow = null;
});

return loadingWindow;
} catch (error) {
console.error('Error creating loading window:', error);
return null;
}
}

function createWindow(env, resourcesPath) {
const mainWindow = new BrowserWindow({
width: 1400,
height: 900,
webPreferences: {
nodeIntegration: true,
},
});

try {
const entryPath = pathUtils.getFrontendPath(env, resourcesPath);
fileUtils.writePath(
join(
pathUtils.getRootBackendFolderPath(env, resourcesPath),
'entryPath.txt'
),
entryPath
);
mainWindow.loadFile(entryPath);
mainWindow.webContents.openDevTools(); // Open DevTools in development
} catch (e) {
console.error(e);
fileUtils.writePath(
join(
pathUtils.getRootBackendFolderPath(env, resourcesPath),
'entryPathError.txt'
),
e.message
);
}
}

module.exports = {
createLoadingWindow,
createWindow,
};
36 changes: 36 additions & 0 deletions main-process/loading.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!DOCTYPE html>
<html>
<head>
<title>Loading...</title>
<style>
body {
background: transparent;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
font-family: Arial, sans-serif;
}
.loader {
border: 5px solid #f3f3f3;
border-top: 5px solid #3498db;
border-radius: 50%;
width: 50px;
height: 50px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
</head>
<body>
<div class="loader"></div>
</body>
</html>
41 changes: 41 additions & 0 deletions main-process/path-utils.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use strict';
const { join } = require('path');

function getRootBackendFolderPath(env, resourcesPath) {
switch (env) {
case 'dev':
case 'staging':
return join('dist', 'nest-backend');
case 'prod':
return resourcesPath;
default:
// Default to production path
return resourcesPath;
}
}

function getFrontendPath(env, resourcesPath) {
const devFrontendPath = join('dist', 'ng-tracker', 'browser', 'index.html');

const prodFrontendPath = join(
resourcesPath,
'ng-tracker',
'browser',
'index.html'
);

switch (env) {
case 'dev':
case 'staging':
return devFrontendPath;
case 'prod':
return prodFrontendPath;
default:
return prodFrontendPath;
}
}

module.exports = {
getRootBackendFolderPath,
getFrontendPath,
};
Loading

0 comments on commit cac2c60

Please sign in to comment.