diff --git a/.eslintrc.js b/.eslintrc.js index cd133e4868..a81e34f4df 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -31,6 +31,7 @@ module.exports = { '@typescript-eslint/no-explicit-any': 'warn', // TODO: Change back to 'error' after fixing existing violations '@typescript-eslint/no-unused-vars': 'warn', // TODO: Change back to 'error' after fixing existing violations '@typescript-eslint/no-unsafe-declaration-merging': 'warn', // TODO: Change back to 'error' after fixing existing violations + 'object-shorthand': ['warn', 'always'], }, parser: '@typescript-eslint/parser', settings: { diff --git a/package.json b/package.json index c3c1230f5a..94745a8bfe 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "build:renderer": "cross-env NODE_ENV=production TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.renderer.prod.ts", "rebuild": "electron-rebuild --parallel --types prod,dev,optional --module-dir .", "reinstall:nautilus-extension": "NODE_ENV=development ts-node src/apps/nautilus-extension/reload.ts", - "lint": "cross-env NODE_ENV=development eslint . --ext .ts,.tsx --max-warnings=215", + "lint": "cross-env NODE_ENV=development eslint . --ext .ts,.tsx --max-warnings=235", "lint:fix": "npm run lint --fix", "format": "prettier src --check", "format:fix": "prettier src --write", @@ -37,7 +37,7 @@ "prepare": "husky install", "generate_schema": "openapi-typescript http://localhost:3005/api-json -o ./src/infra/schemas.d.ts", "generate_schema:prod": "openapi-typescript https://gateway.internxt.com/drive/api-json -o ./src/infra/schemas.d.ts", - "find-deadcode": "knip --max-issues=284" + "find-deadcode": "knip --max-issues=206" }, "build": { "productName": "Internxt", diff --git a/src/apps/main/background-processes/backups/BackupConfiguration/BackupConfiguration.ts b/src/apps/main/background-processes/backups/BackupConfiguration/BackupConfiguration.ts index 0515284c3d..97057cefe7 100644 --- a/src/apps/main/background-processes/backups/BackupConfiguration/BackupConfiguration.ts +++ b/src/apps/main/background-processes/backups/BackupConfiguration/BackupConfiguration.ts @@ -30,12 +30,12 @@ export class BackupConfiguration { } async obtainBackupsInfo(): Promise> { - const device = await DeviceModule.getOrCreateDevice(); - if (device instanceof Error) return []; + const { error, data } = await DeviceModule.getOrCreateDevice(); + if (error) return []; - const enabledBackupEntries = await DeviceModule.getBackupsFromDevice(device, true); + const enabledBackupEntries = await DeviceModule.getBackupsFromDevice(data, true); - return this.map(enabledBackupEntries, device.bucket); + return this.map(enabledBackupEntries, data.bucket); } hasDiscoveredBackups(): boolean { diff --git a/src/apps/main/backups/add-backup.test.ts b/src/apps/main/backups/add-backup.test.ts index 6bdb025cfb..2dbab8d95c 100644 --- a/src/apps/main/backups/add-backup.test.ts +++ b/src/apps/main/backups/add-backup.test.ts @@ -1,26 +1,19 @@ +import * as getPathFromDialogModule from '../device/service'; +import * as createBackupModule from './create-backup'; +import * as DeviceModuleModule from './../../../backend/features/device/device.module'; +import * as enableExistingBackupModule from './enable-existing-backup'; +import * as fetchDeviceModule from '../../../backend/features/device/fetchDevice'; +import configStoreModule from '../config'; import { addBackup } from './add-backup'; -import { getPathFromDialog } from '../device/service'; -import configStore from '../config'; -import { createBackup } from './create-backup'; -import { DeviceModule } from './../../../backend/features/device/device.module'; -import { logger } from '@internxt/drive-desktop-core/build/backend'; -import { enableExistingBackup } from './enable-existing-backup'; - -vi.mock('../device/service'); -vi.mock('../config'); -vi.mock('./create-backup'); -vi.mock('./../../../backend/features/device/device.module'); -vi.mock('./enable-existing-backup'); -vi.mock('../../../backend/features/device/fetchDevice', () => ({ - fetchDevice: vi.fn(), -})); - -const mockedGetPathFromDialog = vi.mocked(getPathFromDialog); -const mockedConfigStore = vi.mocked(configStore); -const mockedCreateBackup = vi.mocked(createBackup); -const mockedDeviceModule = vi.mocked(DeviceModule); -const mockedLogger = vi.mocked(logger); -const mockedEnableExistingBackup = vi.mocked(enableExistingBackup); +import { loggerMock } from 'tests/vitest/mocks.helper'; +import { call, partialSpyOn } from 'tests/vitest/utils.helper'; + +const mockedGetPathFromDialog = partialSpyOn(getPathFromDialogModule, 'getPathFromDialog'); +const mockedConfigStoreGet = partialSpyOn(configStoreModule, 'get'); +const mockedCreateBackup = partialSpyOn(createBackupModule, 'createBackup'); +const mockedGetOrCreateDevice = partialSpyOn(DeviceModuleModule.DeviceModule, 'getOrCreateDevice'); +const mockedEnableExistingBackup = partialSpyOn(enableExistingBackupModule, 'enableExistingBackup'); +const mockedFetchDevice = partialSpyOn(fetchDeviceModule, 'fetchDevice'); describe('addBackup', () => { const mockDevice = { @@ -33,25 +26,21 @@ describe('addBackup', () => { }; beforeEach(() => { - vi.clearAllMocks(); + mockedFetchDevice.mockResolvedValue({ error: undefined, data: mockDevice }); }); it('should throw error when device is not found', async () => { const mockError = new Error('Device not found'); - mockedDeviceModule.getOrCreateDevice.mockResolvedValue(mockError); - mockedLogger.error.mockImplementation(() => { - throw new Error('Error message'); - }); + mockedGetOrCreateDevice.mockResolvedValue({ error: mockError, data: undefined }); await expect(addBackup()).rejects.toThrow('Error message'); - expect(mockedLogger.error).toBeCalledWith({ - tag: 'BACKUPS', + call(loggerMock.error).toMatchObject({ msg: 'Error adding backup: No device found', }); }); it('should return undefined when no path is chosen', async () => { - mockedDeviceModule.getOrCreateDevice.mockResolvedValue(mockDevice); + mockedGetOrCreateDevice.mockResolvedValue({ error: undefined, data: mockDevice }); mockedGetPathFromDialog.mockResolvedValue(null); const result = await addBackup(); @@ -70,14 +59,14 @@ describe('addBackup', () => { backupsBucket: 'test-bucket', }; - mockedDeviceModule.getOrCreateDevice.mockResolvedValue(mockDevice); + mockedGetOrCreateDevice.mockResolvedValue({ error: undefined, data: mockDevice }); mockedGetPathFromDialog.mockResolvedValue({ path: chosenPath, itemName: 'backup' }); - mockedConfigStore.get.mockReturnValue({}); + mockedConfigStoreGet.mockReturnValue({}); mockedCreateBackup.mockResolvedValue(mockBackupInfo); const result = await addBackup(); - expect(mockedCreateBackup).toBeCalledWith({ + call(mockedCreateBackup).toMatchObject({ pathname: chosenPath, device: mockDevice, }); @@ -100,14 +89,14 @@ describe('addBackup', () => { backupsBucket: 'test-bucket', }; - mockedDeviceModule.getOrCreateDevice.mockResolvedValue(mockDevice); + mockedGetOrCreateDevice.mockResolvedValue({ error: undefined, data: mockDevice }); mockedGetPathFromDialog.mockResolvedValue({ path: chosenPath, itemName: 'existing' }); - mockedConfigStore.get.mockReturnValue({ [chosenPath]: existingBackupData }); + mockedConfigStoreGet.mockReturnValue({ [chosenPath]: existingBackupData }); mockedEnableExistingBackup.mockResolvedValue(mockBackupInfo); const result = await addBackup(); - expect(mockedEnableExistingBackup).toBeCalledWith(chosenPath, mockDevice); + call(mockedEnableExistingBackup).toMatchObject([chosenPath, mockDevice]); expect(result).toStrictEqual(mockBackupInfo); }); }); diff --git a/src/apps/main/backups/add-backup.ts b/src/apps/main/backups/add-backup.ts index 7d84d9e062..bd58531eeb 100644 --- a/src/apps/main/backups/add-backup.ts +++ b/src/apps/main/backups/add-backup.ts @@ -6,8 +6,8 @@ import { logger } from '@internxt/drive-desktop-core/build/backend'; import { enableExistingBackup } from './enable-existing-backup'; export async function addBackup() { - const device = await DeviceModule.getOrCreateDevice(); - if (device instanceof Error) { + const { error, data } = await DeviceModule.getOrCreateDevice(); + if (error) { throw logger.error({ tag: 'BACKUPS', msg: 'Error adding backup: No device found' }); } @@ -19,8 +19,8 @@ export async function addBackup() { const existingBackup = backupList[chosenPath]; if (!existingBackup) { - return await createBackup({ pathname: chosenPath, device }); + return await createBackup({ pathname: chosenPath, device: data }); } else { - return await enableExistingBackup(chosenPath, device); + return await enableExistingBackup(chosenPath, data); } } diff --git a/src/apps/main/device/service.ts b/src/apps/main/device/service.ts index 5b00542da8..55cb8d2e5c 100644 --- a/src/apps/main/device/service.ts +++ b/src/apps/main/device/service.ts @@ -299,11 +299,11 @@ export function findBackupPathnameFromId(id: number): string | undefined { export async function createBackupsFromLocalPaths(folderPaths: string[]) { configStore.set('backupsEnabled', true); - const result = await DeviceModule.getOrCreateDevice(); - if (result instanceof Error) { - throw result; + const { error, data } = await DeviceModule.getOrCreateDevice(); + if (error) { + throw error; } - const operations = folderPaths.map((folderPath) => createBackup({ pathname: folderPath, device: result })); + const operations = folderPaths.map((folderPath) => createBackup({ pathname: folderPath, device: data })); await Promise.all(operations); } diff --git a/src/apps/main/interface.d.ts b/src/apps/main/interface.d.ts index 1f2aa653e3..e3537dc4b7 100644 --- a/src/apps/main/interface.d.ts +++ b/src/apps/main/interface.d.ts @@ -24,7 +24,7 @@ export interface IElectronAPI { setBackupsInterval(value: number): Promise; - getOrCreateDevice: () => Promise; + getOrCreateDevice: () => Promise>; getBackupsFromDevice: (device: Device, isCurrent?: boolean) => Promise>; diff --git a/src/apps/renderer/context/DeviceContext.tsx b/src/apps/renderer/context/DeviceContext.tsx index bad6218056..bb8c9ec3db 100644 --- a/src/apps/renderer/context/DeviceContext.tsx +++ b/src/apps/renderer/context/DeviceContext.tsx @@ -36,14 +36,13 @@ export function DeviceProvider({ children }: { children: ReactNode }) { const refreshDevice = () => { setDeviceState({ status: 'LOADING' }); - window.electron - .getOrCreateDevice() - .then((device) => { - setCurrentDevice(device); - }) - .catch(() => { + window.electron.getOrCreateDevice().then(({ error, data: device }) => { + if (error) { setDeviceState({ status: 'ERROR' }); - }); + return; + } + setCurrentDevice(device); + }); }; const setCurrentDevice = (newDevice: Device) => { diff --git a/src/backend/features/device/createAndSetupNewDevice.ts b/src/backend/features/device/createAndSetupNewDevice.ts index 3e46d68ce4..e2ae0e6adb 100644 --- a/src/backend/features/device/createAndSetupNewDevice.ts +++ b/src/backend/features/device/createAndSetupNewDevice.ts @@ -1,35 +1,37 @@ import { DependencyInjectionUserProvider } from './../../../apps/shared/dependency-injection/DependencyInjectionUserProvider'; -import { Device } from './../../../apps/main/device/service'; import { createNewDevice } from './createNewDevice'; import { BrowserWindow } from 'electron'; import { broadcastToWindows } from '../../../apps/main/windows'; import { logger } from '@internxt/drive-desktop-core/build/backend'; import { getDeviceIdentifier } from './getDeviceIdentifier'; -export async function createAndSetupNewDevice(): Promise { - const getDeviceIdentifierResult = getDeviceIdentifier(); - if (getDeviceIdentifierResult.isLeft()) { - return getDeviceIdentifierResult.getLeft(); - } - const deviceIdentifier = getDeviceIdentifierResult.getRight(); +export async function createAndSetupNewDevice() { + const { error, data: deviceIdentifier } = getDeviceIdentifier(); + if (error) return { error }; const createNewDeviceEither = await createNewDevice(deviceIdentifier); - if (createNewDeviceEither.isRight()) { - const device = createNewDeviceEither.getRight(); - const user = DependencyInjectionUserProvider.get(); - user.backupsBucket = device.bucket; - - const mainWindow = BrowserWindow.getAllWindows()[0]; - if (mainWindow) { - mainWindow.webContents.send('reinitialize-backups'); - } - broadcastToWindows('device-created', device); - logger.debug({ + if (createNewDeviceEither.isLeft()) { + logger.error({ tag: 'BACKUPS', - msg: '[DEVICE] Created new device', - deviceUUID: device.uuid, + msg: '[DEVICE] Error creating new device', + error: createNewDeviceEither.getLeft(), }); - return device; + return { error: createNewDeviceEither.getLeft() }; + } + + const device = createNewDeviceEither.getRight(); + const user = DependencyInjectionUserProvider.get(); + user.backupsBucket = device.bucket; + + const mainWindow = BrowserWindow.getAllWindows()[0]; + if (mainWindow) { + mainWindow.webContents.send('reinitialize-backups'); } - return createNewDeviceEither.getLeft(); + broadcastToWindows('device-created', device); + logger.debug({ + tag: 'BACKUPS', + msg: '[DEVICE] Created new device', + deviceUUID: device.uuid, + }); + return { data: device }; } diff --git a/src/backend/features/device/fetchDevice.ts b/src/backend/features/device/fetchDevice.ts index 9c918d312b..1de7a3338b 100644 --- a/src/backend/features/device/fetchDevice.ts +++ b/src/backend/features/device/fetchDevice.ts @@ -1,86 +1,59 @@ import { driveServerModule } from './../../../infra/drive-server/drive-server.module'; -import { Either, left, right } from '../../../context/shared/domain/Either'; -import { Device } from '../../../apps/main/device/service'; import { logger } from '@internxt/drive-desktop-core/build/backend'; import { BackupError } from '../../../infra/drive-server/services/backup/backup.error'; import { addUnknownDeviceIssue } from './addUnknownDeviceIssue'; import { DeviceIdentifierDTO } from './device.types'; + export type FetchDeviceProps = { deviceIdentifier: DeviceIdentifierDTO } | { uuid: string } | { legacyId: string }; -async function getDeviceByProps(props: FetchDeviceProps): Promise> { +async function getDeviceByProps(props: FetchDeviceProps) { if ('deviceIdentifier' in props) { const query = { key: props.deviceIdentifier.key, platform: props.deviceIdentifier.platform, hostname: props.deviceIdentifier.hostname, + limit: 1, + offset: 0, }; - const result = await driveServerModule.backup.getDevicesByIdentifier(query); - - if (result.isLeft()) return left(result.getLeft()); + const { error, data } = await driveServerModule.backup.getDevicesByIdentifier({ query }); + if (error) return { error }; - const devices = result.getRight(); - if (devices.length === 0) return right(null); - if (devices.length > 1) return left(new Error('Multiple devices found for the same identifier')); + if (data.length === 0) { + return { error: new BackupError('Device not found with given identifier', 'NOT_FOUND') }; + } - return right(devices[0]); + return { data: data[0] }; } else { const deviceResult = 'uuid' in props ? await driveServerModule.backup.getDevice(props.uuid) : await driveServerModule.backup.getDeviceById(props.legacyId); - if (deviceResult.isLeft()) return left(deviceResult.getLeft()); + if (deviceResult.isLeft()) return { error: deviceResult.getLeft() }; - return right(deviceResult.getRight()); + return { data: deviceResult.getRight() }; } } -/** - * Checks if a device exists using the provided identifier. - * @param props - Union type object containing either: - * - { uuid: string } for UUID-based lookup - * - { legacyId: string } for legacy ID-based lookup - * - { deviceIdentifier: DeviceIdentifierDTO } for lookup by device identifier (key, platform, hostname) - * - * The function will automatically select the correct lookup method based on the provided property. - * - * @returns Either - Right(Device) if found, Right(null) if not found, Left(Error) if error - */ -export async function fetchDevice(props: FetchDeviceProps): Promise> { - const getDeviceEither = await getDeviceByProps(props); - - if (getDeviceEither.isRight()) { - const device = getDeviceEither.getRight(); - if (device && !device.removed) { - logger.debug({ - tag: 'BACKUPS', - msg: '[DEVICE] Found device', - device: device.name, - }); - return right(device); - } - } - - if (getDeviceEither.isLeft()) { - const error = getDeviceEither.getLeft(); +export async function fetchDevice(props: FetchDeviceProps) { + const { error, data } = await getDeviceByProps(props); + if (error) { if (error instanceof BackupError && error.code === 'NOT_FOUND') { const msg = 'Device not found'; logger.debug({ tag: 'BACKUPS', msg: `[DEVICE] ${msg}` }); addUnknownDeviceIssue(new Error(msg)); - return right(null); } if (error instanceof BackupError && error.code === 'FORBIDDEN') { const msg = 'Device request returned forbidden'; logger.debug({ tag: 'BACKUPS', msg: `[DEVICE] ${msg}` }); addUnknownDeviceIssue(new Error(msg)); - return right(null); } logger.error({ tag: 'BACKUPS', msg: '[DEVICE] Error fetching device', error: error.name }); - return left(error); + return { error }; } - return right(null); + return { data }; } diff --git a/src/backend/features/device/fetchDeviceByIdentifier.ts b/src/backend/features/device/fetchDeviceByIdentifier.ts deleted file mode 100644 index 2ecb9b13ad..0000000000 --- a/src/backend/features/device/fetchDeviceByIdentifier.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { left } from './../../../context/shared/domain/Either'; -import { getDeviceIdentifier } from './getDeviceIdentifier'; -import { fetchDevice } from './fetchDevice'; -import { Either } from './../../../context/shared/domain/Either'; -import { Device } from './../../../apps/main/device/service'; - -export async function fetchDeviceByIdentifier(): Promise> { - const result = getDeviceIdentifier(); - if (result.isLeft()) { - return left(result.getLeft()); - } - return await fetchDevice({ - deviceIdentifier: result.getRight(), - }); -} diff --git a/src/backend/features/device/fetchDeviceLegacyAndMigrate.ts b/src/backend/features/device/fetchDeviceLegacyAndMigrate.ts index 7ba0694ea1..301d8b672c 100644 --- a/src/backend/features/device/fetchDeviceLegacyAndMigrate.ts +++ b/src/backend/features/device/fetchDeviceLegacyAndMigrate.ts @@ -1,19 +1,18 @@ -import { Either } from '../../../context/shared/domain/Either'; import { fetchDevice, FetchDeviceProps } from './fetchDevice'; import { migrateLegacyDeviceIdentifier } from './migrateLegacyDeviceIdentifier'; -import { Device } from '../../../apps/main/device/service'; import configStore from '../../../apps/main/config'; +import { BackupError } from '../../../infra/drive-server/services/backup/backup.error'; -export async function fetchDeviceLegacyAndMigrate(props: FetchDeviceProps): Promise> { - const deviceResult = await fetchDevice(props); - - if (deviceResult.isRight()) { - const device = deviceResult.getRight(); - if (device) { - return await migrateLegacyDeviceIdentifier(device); +export async function fetchDeviceLegacyAndMigrate(props: FetchDeviceProps) { + const { error, data } = await fetchDevice(props); + if (error) { + if (error instanceof BackupError && error.code === 'NOT_FOUND') { + configStore.set('deviceId', -1); + configStore.set('deviceUUID', ''); } - configStore.set('deviceId', -1); - configStore.set('deviceUUID', ''); + + return { error }; } - return deviceResult; + + return await migrateLegacyDeviceIdentifier({ device: data }); } diff --git a/src/backend/features/device/getCurrentDevice.ts b/src/backend/features/device/getCurrentDevice.ts deleted file mode 100644 index 0d819f3b7d..0000000000 --- a/src/backend/features/device/getCurrentDevice.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { left, right } from './../../../context/shared/domain/Either'; -import { Device } from './../../../apps/main/device/service'; -import { Either } from '../../../context/shared/domain/Either'; -import { driveServerModule } from './../../../infra/drive-server/drive-server.module'; -import { getDeviceIdentifier } from './getDeviceIdentifier'; - -export async function getCurrentDevice(): Promise> { - const getDeviceIdentifierEither = getDeviceIdentifier(); - - if (getDeviceIdentifierEither.isLeft()) { - return left(getDeviceIdentifierEither.getLeft()); - } - - const { platform, key, hostname } = getDeviceIdentifierEither.getRight(); - if (!platform || !key || !hostname) { - return left(new Error('Missing required device identifier fields')); - } - - const getDeviceEither = await driveServerModule.backup.getDevicesByIdentifier({ - platform, - key, - hostname, - limit: 50, - offset: 0, - }); - - if (getDeviceEither.isLeft()) { - return left(getDeviceEither.getLeft()); - } - - if (getDeviceEither.getRight().length > 0) { - return left(new Error('Multiple devices found for the same identifier')); - } - - return right(getDeviceEither.getRight()[0]); -} diff --git a/src/backend/features/device/getDeviceIdentifier.ts b/src/backend/features/device/getDeviceIdentifier.ts index 1eeed2d0d0..67cb644d6c 100644 --- a/src/backend/features/device/getDeviceIdentifier.ts +++ b/src/backend/features/device/getDeviceIdentifier.ts @@ -1,21 +1,21 @@ -import { Either, left, right } from './../../../context/shared/domain/Either'; import { logger } from '@internxt/drive-desktop-core/build/backend'; import { getMachineId } from '../../../infra/device/getMachineId'; import os from 'os'; import { DeviceIdentifierDTO, isAllowedPlatform } from './device.types'; -export function getDeviceIdentifier(): Either { +export function getDeviceIdentifier() { const hostname = os.hostname().trim(); const platform = os.platform(); if (!isAllowedPlatform(platform)) { - return left(new Error(`Unsupported platform: ${platform}`)); + return { error: new Error(`Unsupported platform: ${platform}`) }; } const { data: key, error } = getMachineId(); if (key && platform && hostname) { - return right({ key, platform, hostname }); + const data: DeviceIdentifierDTO = { key, platform, hostname }; + return { data }; } const err = logger.error({ @@ -24,5 +24,5 @@ export function getDeviceIdentifier(): Either { error, context: { platform, hostname, key }, }); - return left(err); + return { error: err }; } diff --git a/src/backend/features/device/getOrCreateDevice.ts b/src/backend/features/device/getOrCreateDevice.ts index 9028e36cb3..2fadbd5172 100644 --- a/src/backend/features/device/getOrCreateDevice.ts +++ b/src/backend/features/device/getOrCreateDevice.ts @@ -1,58 +1,39 @@ import { Device } from '../../../apps/main/device/service'; -import { Either } from './../../../context/shared/domain/Either'; import configStore from '../../../apps/main/config'; import { logger } from '@internxt/drive-desktop-core/build/backend'; import { fetchDeviceLegacyAndMigrate } from './fetchDeviceLegacyAndMigrate'; -import { fetchDeviceByIdentifier } from './fetchDeviceByIdentifier'; +import { fetchDevice } from './fetchDevice'; import { createAndSetupNewDevice } from './createAndSetupNewDevice'; -/** - * Handles the result of a device fetch operation. - * - * - If the result is Right and contains a Device, returns the Device. - * - If the result is Right and contains null, logs and creates a new device, then returns it. - * - If the result is Left, returns the error. - * - If the result is neither, returns a generic error. - * - * @param deviceResult - The Either result from a device fetch operation. - * @returns The Device if found or created, or an Error if an error occurred. - */ -async function handleFetchDeviceResult(deviceResult: Either) { - if (deviceResult.isRight()) { - const device = deviceResult.getRight(); - if (device) { - return device; - } else { - logger.debug({ tag: 'BACKUPS', msg: '[DEVICE] Device not found, creating a new one' }); - return await createAndSetupNewDevice(); - } - } +import { getDeviceIdentifier } from './getDeviceIdentifier'; +import { Result } from './../../../context/shared/domain/Result'; - if (deviceResult.isLeft()) { - return deviceResult.getLeft(); +async function handleFetchDeviceResult(deviceResult: Result) { + if (deviceResult.error) { + logger.debug({ tag: 'BACKUPS', msg: '[DEVICE] Device not found, creating a new one' }); + return await createAndSetupNewDevice(); } - return new Error('Unknown error: Device not found or removed'); + return { data: deviceResult.data }; } -export async function getOrCreateDevice(): Promise { +export async function getOrCreateDevice(): Promise> { + const { error, data } = getDeviceIdentifier(); + if (error) return { error }; + const legacyId = configStore.get('deviceId'); const savedUUID = configStore.get('deviceUUID'); logger.debug({ tag: 'BACKUPS', - msg: '[DEVICE] Saved device with legacy deviceId', - savedDeviceId: legacyId, - }); - logger.debug({ - tag: 'BACKUPS', - msg: '[DEVICE] Saved device with UUID', - savedDeviceId: savedUUID, + msg: '[DEVICE] Checking saved device identifiers', + legacyId, + savedUUID, }); + const hasLegacyId = legacyId !== -1; const hasUuid = savedUUID !== ''; - if (!hasLegacyId && !hasUuid) { - const result = await fetchDeviceByIdentifier(); - return handleFetchDeviceResult(result); + const result = await fetchDevice({ deviceIdentifier: data }); + return await handleFetchDeviceResult(result); } /* eventually, this whole if section is going to be replaced @@ -60,5 +41,5 @@ export async function getOrCreateDevice(): Promise { const prop = hasUuid ? { uuid: savedUUID } : { legacyId: legacyId.toString() }; const deviceResult = await fetchDeviceLegacyAndMigrate(prop); - return handleFetchDeviceResult(deviceResult); + return await handleFetchDeviceResult(deviceResult); } diff --git a/src/backend/features/device/migrateLegacyDeviceIdentifier.ts b/src/backend/features/device/migrateLegacyDeviceIdentifier.ts index c1255e1389..05854de1db 100644 --- a/src/backend/features/device/migrateLegacyDeviceIdentifier.ts +++ b/src/backend/features/device/migrateLegacyDeviceIdentifier.ts @@ -1,56 +1,55 @@ import { logger } from '@internxt/drive-desktop-core/build/backend'; -import { right } from './../../../context/shared/domain/Either'; import { driveServerModule } from './../../../infra/drive-server/drive-server.module'; -import { Either } from './../../../context/shared/domain/Either'; import { Device } from './../../../apps/main/device/service'; import { getDeviceIdentifier } from './getDeviceIdentifier'; import configStore from './../../../apps/main/config'; import { BackupError } from '../../../infra/drive-server/services/backup/backup.error'; -export async function migrateLegacyDeviceIdentifier(device: Device): Promise> { - const getDeviceIdentifierResult = getDeviceIdentifier(); - if (getDeviceIdentifierResult.isLeft()) { +type Props = { + device: Device; +}; + +export async function migrateLegacyDeviceIdentifier({ device }: Props) { + const { error, data } = getDeviceIdentifier(); + if (error) { logger.warn({ tag: 'BACKUPS', msg: 'No valid identifier available for migration', }); - return right(device); + return { data: device }; } - const deviceIdentifier = getDeviceIdentifierResult.getRight(); const addIdentifierResult = await driveServerModule.backup.addDeviceIdentifier({ - key: deviceIdentifier.key, - hostname: deviceIdentifier.hostname, - platform: deviceIdentifier.platform, + key: data.key, + hostname: data.hostname, + platform: data.platform, name: device.name, folderUuid: device.uuid, }); - if (addIdentifierResult.isRight()) { - configStore.set('deviceId', -1); - configStore.set('deviceUUID', ''); - logger.debug({ - tag: 'BACKUPS', - msg: 'Successfully migrated legacy device identifier', - device: addIdentifierResult.getRight(), - }); - return right(addIdentifierResult.getRight()); - } - const error = addIdentifierResult.getLeft(); - if (error instanceof BackupError && error.code === 'ALREADY_EXISTS') { + const migrationError = addIdentifierResult.getLeft(); + const isSuccessful = + addIdentifierResult.isRight() || + (migrationError instanceof BackupError && migrationError.code === 'ALREADY_EXISTS'); + + if (isSuccessful) { configStore.set('deviceId', -1); configStore.set('deviceUUID', ''); + + const migratedDevice = addIdentifierResult.isRight() ? addIdentifierResult.getRight() : device; + logger.debug({ tag: 'BACKUPS', msg: 'Successfully migrated legacy device identifier', - device: addIdentifierResult.getRight(), + device: migratedDevice, }); - return right(device); + return { data: migratedDevice }; } + logger.warn({ tag: 'BACKUPS', msg: 'Failed to migrate legacy device identifier', - error, + error: migrationError, }); - return right(device); + return { data: device }; } diff --git a/src/infra/drive-server/services/backup/backup.service.ts b/src/infra/drive-server/services/backup/backup.service.ts index 995df9c3c9..39bdd2a8ad 100644 --- a/src/infra/drive-server/services/backup/backup.service.ts +++ b/src/infra/drive-server/services/backup/backup.service.ts @@ -223,7 +223,7 @@ export class BackupService { } } - async getDevicesByIdentifier(query: getDevicesByIdentifierQuery): Promise>> { + async getDevicesByIdentifier({ query }: { query: getDevicesByIdentifierQuery }) { try { const response = await driveServerClient.GET('/backup/v2/devices', { headers: getNewApiHeaders(), @@ -235,9 +235,12 @@ export class BackupService { msg: 'Get devices by identifier request was not successful', attributes: { endpoint: '/backup/v2/devices' }, }); - return left(new Error('Get devices by identifier request was not successful')); + const error = new Error('Get devices by identifier request was not successful'); + return { error }; } - return right(response.data.map(({ folder }) => mapDeviceAsFolderToDevice(folder!))); + + const deviceMapped = response.data.map(({ folder }) => mapDeviceAsFolderToDevice(folder!)); + return { data: deviceMapped }; } catch (err) { const error = mapError(err); logger.error({ @@ -246,7 +249,7 @@ export class BackupService { error: error.message, attributes: { endpoint: '/backup/v2/devices' }, }); - return left(error); + return { error }; } }