Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/apps/main/bootstrap/bootstrap-runtime-state.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export type PendingUpdateInfo = { version: string } | null;

let pendingUpdateInfo: PendingUpdateInfo = null;

export function getPendingUpdateInfo() {
return pendingUpdateInfo;
}

export function setPendingUpdateInfo(updateInfo: Exclude<PendingUpdateInfo, null>) {
pendingUpdateInfo = updateInfo;
}
40 changes: 40 additions & 0 deletions src/apps/main/bootstrap/main-process-bootstrap.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { bootstrapMainProcess } from './main-process-bootstrap';
import * as registerAppReadyFlowModule from './register-app-ready-flow';
import * as registerMainIpcHandlersModule from './register-main-ipc-handlers';
import * as registerProcessHandlersModule from './register-process-handlers';
import * as registerSecondInstanceFlowModule from './register-second-instance-flow';
import * as registerSessionEventHandlersModule from './register-session-event-handlers';
import * as setupEnvironmentDebugToolsModule from './setup-environment-debug-tools';
import { partialSpyOn } from 'tests/vitest/utils.helper';

describe('main-process-bootstrap', () => {
const setupEnvironmentDebugToolsMock = partialSpyOn(setupEnvironmentDebugToolsModule, 'setupEnvironmentDebugTools');
const registerMainIpcHandlersMock = partialSpyOn(registerMainIpcHandlersModule, 'registerMainIpcHandlers');
const registerAppReadyFlowMock = partialSpyOn(registerAppReadyFlowModule, 'registerAppReadyFlow');
const registerSecondInstanceFlowMock = partialSpyOn(registerSecondInstanceFlowModule, 'registerSecondInstanceFlow');
const registerSessionEventHandlersMock = partialSpyOn(
registerSessionEventHandlersModule,
'registerSessionEventHandlers',
);
const registerProcessHandlersMock = partialSpyOn(registerProcessHandlersModule, 'registerProcessHandlers');

beforeEach(() => {
setupEnvironmentDebugToolsMock.mockImplementation(() => undefined);
registerMainIpcHandlersMock.mockImplementation(() => undefined);
registerAppReadyFlowMock.mockImplementation(() => undefined);
registerSecondInstanceFlowMock.mockImplementation(() => undefined);
registerSessionEventHandlersMock.mockImplementation(() => undefined);
registerProcessHandlersMock.mockImplementation(() => undefined);
});

it('should register all main process bootstrap flows', () => {
bootstrapMainProcess();

expect(setupEnvironmentDebugToolsMock).toBeCalled();
expect(registerMainIpcHandlersMock).toBeCalled();
expect(registerAppReadyFlowMock).toBeCalled();
expect(registerSecondInstanceFlowMock).toBeCalled();
expect(registerSessionEventHandlersMock).toBeCalled();
expect(registerProcessHandlersMock).toBeCalled();
});
});
15 changes: 15 additions & 0 deletions src/apps/main/bootstrap/main-process-bootstrap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { registerAppReadyFlow } from './register-app-ready-flow';
import { setupEnvironmentDebugTools } from './setup-environment-debug-tools';
import { registerMainIpcHandlers } from './register-main-ipc-handlers';
import { registerProcessHandlers } from './register-process-handlers';
import { registerSecondInstanceFlow } from './register-second-instance-flow';
import { registerSessionEventHandlers } from './register-session-event-handlers';

export function bootstrapMainProcess() {
setupEnvironmentDebugTools();
registerMainIpcHandlers();
registerAppReadyFlow();
registerSecondInstanceFlow();
registerSessionEventHandlers();
registerProcessHandlers();
}
55 changes: 55 additions & 0 deletions src/apps/main/bootstrap/register-app-ready-flow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { app } from 'electron';
import { logger } from '@internxt/drive-desktop-core/build/backend';
import eventBus from '../event-bus';
import { getIsLoggedIn } from '../auth/handlers';
import { createAuthWindow } from '../windows/auth';
import { setTrayStatus } from '../tray/tray';
import { broadcastToWindows } from '../windows';
import { setupThemeListener } from '../../../core/theme';
import { registerAvailableUserProductsHandlers } from '../../../backend/features/payments/ipc/register-available-user-products-handlers';
import { setupAppImageDeeplink } from '../auth/deeplink/setup-appimage-deeplink';
import { INTERNXT_VERSION } from '../../../core/utils/utils';
import { checkForUpdates } from '../auto-update/check-for-updates';
import { setPendingUpdateInfo } from './bootstrap-runtime-state';

export function registerAppReadyFlow() {
app
.whenReady()
.then(async () => {
/**
* v.2.5.1
* Esteban Galvis Triana
* .AppImage users may experience login issues because the deeplink protocol
* is not registered automatically, unlike with .deb packages.
* This function manually registers the protocol handler for .AppImage installations.
*/
await setupAppImageDeeplink();
/**
* TODO: Nautilus extension disabled temporarily

Check warning on line 28 in src/apps/main/bootstrap/register-app-ready-flow.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Complete the task associated to this "TODO" comment.

See more on https://sonarcloud.io/project/issues?id=internxt_drive-desktop-linux&issues=AZ2c9T6jF9k_KGYgrtUR&open=AZ2c9T6jF9k_KGYgrtUR&pullRequest=314
* v.2.5.4
* Esteban Galvis Triana
* The Nautilus extension will be temporarily disabled
* while the exact behavior of the context menu options is being determined.
*/
// await installNautilusExtension();

Check warning on line 34 in src/apps/main/bootstrap/register-app-ready-flow.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this commented out code.

See more on https://sonarcloud.io/project/issues?id=internxt_drive-desktop-linux&issues=AZ2c9T6jF9k_KGYgrtUS&open=AZ2c9T6jF9k_KGYgrtUS&pullRequest=314
setupThemeListener();

eventBus.emit('APP_IS_READY');
const isLoggedIn = getIsLoggedIn();

if (!isLoggedIn) {
await createAuthWindow();
setTrayStatus('IDLE');
}

await checkForUpdates({
currentVersion: INTERNXT_VERSION,
onUpdateAvailable: (updateInfo) => {
setPendingUpdateInfo(updateInfo);
broadcastToWindows('update-available', updateInfo);
},
});
registerAvailableUserProductsHandlers();
})
.catch((exc) => logger.error({ msg: 'Error starting app', exc }));

Check warning on line 54 in src/apps/main/bootstrap/register-app-ready-flow.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

The catch parameter `exc` should be named `error_`.

See more on https://sonarcloud.io/project/issues?id=internxt_drive-desktop-linux&issues=AZ2c9T6jF9k_KGYgrtUT&open=AZ2c9T6jF9k_KGYgrtUT&pullRequest=314
}
17 changes: 17 additions & 0 deletions src/apps/main/bootstrap/register-main-ipc-handlers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import dns from 'node:dns';
import { ipcMain } from 'electron';
import { getPendingUpdateInfo } from './bootstrap-runtime-state';

export function registerMainIpcHandlers() {
ipcMain.handle('get-update-status', () => getPendingUpdateInfo());

ipcMain.handle('check-internet-connection', async () => {
return new Promise((resolve) => {
dns.lookup('google.com', (err) => {
resolve(!err);
});

setTimeout(() => resolve(false), 3000);
});
});
}
25 changes: 25 additions & 0 deletions src/apps/main/bootstrap/register-process-handlers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { logger } from '@internxt/drive-desktop-core/build/backend';

export function registerProcessHandlers() {
process.on('uncaughtException', (error) => {
/**
* v.2.5.1
* Esteban Galvis Triana
* EPIPE errors close stdout, so they must be handled specially to avoid infinite logging loops.
*/
if ('code' in error && error.code === 'EPIPE') {
return;
}

if (error.name === 'AbortError') {
logger.debug({ msg: 'Fetch request was aborted' });
return;
}

try {
logger.error({ msg: 'Uncaught exception in main process: ', error });
} catch {
return;
}
});
}
19 changes: 19 additions & 0 deletions src/apps/main/bootstrap/register-second-instance-flow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { app } from 'electron';
import { logger } from '@internxt/drive-desktop-core/build/backend';
import { handleDeeplink } from '../auth/deeplink/handle-deeplink';

export function registerSecondInstanceFlow() {
app.on('second-instance', async (_, argv) => {
logger.debug({ tag: 'AUTH', msg: 'Deeplink received on second instance, processing...' });
const deeplinkArg = argv.find((arg) => arg.startsWith('internxt://'));
if (!deeplinkArg) {
return;
}

try {
await handleDeeplink({ url: deeplinkArg });
} catch (error) {
logger.error({ tag: 'AUTH', msg: 'Error handling deeplink', error });
}
});
}
87 changes: 87 additions & 0 deletions src/apps/main/bootstrap/register-session-event-handlers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { logger } from '@internxt/drive-desktop-core/build/backend';
import eventBus from '../event-bus';
import { AppDataSource, resetAppDataSourceOnLogout } from '../database/data-source';
import { getOrCreateWidged, getWidget, setBoundsOfWidgetByPath } from '../windows/widget';
import { createAuthWindow, getAuthWindow } from '../windows/auth';
import configStore from '../config';
import { getTray, setTrayStatus } from '../tray/tray';
import { openOnboardingWindow } from '../windows/onboarding';
import { getTheme } from '../../../core/theme';
import { getAntivirusManager } from '../antivirus/antivirusManager';
import { trySetupAntivirusIpcAndInitialize } from '../background-processes/antivirus/try-setup-antivirus-ipc-and-initialize';
import { getUserAvailableProductsAndStore } from '../../../backend/features/payments/services/get-user-available-products-and-store';
import { registerBackupHandlers } from '../../../backend/features/backup/register-backup-handlers';
import { startBackupsIfAvailable } from '../../../backend/features/backup/start-backups-if-available';

function onWidgetIsReady() {
registerBackupHandlers();
startBackupsIfAvailable();
}

async function onUserLoggedIn() {
try {
if (!AppDataSource.isInitialized) {
await AppDataSource.initialize();
eventBus.emit('APP_DATA_SOURCE_INITIALIZED');
}

getAuthWindow()?.hide();

getTheme();

setTrayStatus('IDLE');
const widget = await getOrCreateWidged();
const tray = getTray();
if (widget && tray) {
setBoundsOfWidgetByPath(widget, tray);
}

setTimeout(() => {
const authWin = getAuthWindow();
if (authWin && !authWin.isDestroyed()) {
authWin.destroy();
}
}, 300);

const lastOnboardingShown = configStore.get('lastOnboardingShown');

if (!lastOnboardingShown) {
openOnboardingWindow();
} else if (widget) {
widget.show();
}
await getUserAvailableProductsAndStore();
await trySetupAntivirusIpcAndInitialize();
} catch (error) {
logger.error({
msg: 'Error on main process while handling USER_LOGGED_IN event:',
error,
});
}
}

async function onUserLoggedOut() {
setTrayStatus('IDLE');
const widget = getWidget();

if (widget) {
widget.hide();

void getAntivirusManager().shutdown();
}

await createAuthWindow();

if (widget) {
widget.destroy();
}
await resetAppDataSourceOnLogout();

// await uninstallNautilusExtension();

Check warning on line 80 in src/apps/main/bootstrap/register-session-event-handlers.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this commented out code.

See more on https://sonarcloud.io/project/issues?id=internxt_drive-desktop-linux&issues=AZ2c9T9gF9k_KGYgrtUU&open=AZ2c9T9gF9k_KGYgrtUU&pullRequest=314
}

export function registerSessionEventHandlers() {
eventBus.on('WIDGET_IS_READY', onWidgetIsReady);
eventBus.on('USER_LOGGED_IN', onUserLoggedIn);
eventBus.on('USER_LOGGED_OUT', onUserLoggedOut);
}
12 changes: 12 additions & 0 deletions src/apps/main/bootstrap/setup-environment-debug-tools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export function setupEnvironmentDebugTools() {
if (process.env.NODE_ENV === 'production') {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const sourceMapSupport = require('source-map-support');
sourceMapSupport.install();
}

if (process.env.NODE_ENV === 'development') {
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('electron-debug')({ showDevTools: false });
}
}
Loading
Loading