diff --git a/src/apps/renderer/localize/locales/en.json b/src/apps/renderer/localize/locales/en.json index 04b2169f6..d5d0c02a4 100644 --- a/src/apps/renderer/localize/locales/en.json +++ b/src/apps/renderer/localize/locales/en.json @@ -1,7 +1,7 @@ { "login": { "email": { - "section": "Email adress" + "section": "Email address" }, "password": { "section": "Password", @@ -11,7 +11,7 @@ "show": "Show" }, "action": { - "login": "Login", + "login": "Log in", "is-logging-in": "Logging you in..." }, "create-account": "Create account", diff --git a/src/apps/sync-engine/BindingManager.ts b/src/apps/sync-engine/BindingManager.ts index 76c4ea410..0c112941e 100644 --- a/src/apps/sync-engine/BindingManager.ts +++ b/src/apps/sync-engine/BindingManager.ts @@ -37,6 +37,7 @@ export class BindingsManager { async start(version: string, providerId: string) { await this.stop(); + await this.pollingStart(); const controllers = buildControllers(this.container); @@ -215,6 +216,7 @@ export class BindingsManager { async stop() { await this.container.virtualDrive.disconnectSyncRoot(); + this.container.pollingMonitorStop.run(); } async cleanUp() { @@ -276,4 +278,16 @@ export class BindingsManager { Logger.error('[SYNC ENGINE] ', error); } } + + private async pollingStart() { + return this.container.pollingMonitorStart.run(this.polling.bind(this)); + } + + private async polling(): Promise { + Logger.info('[SYNC ENGINE] Monitoring polling...'); + const fileInPendingPaths = + (await this.container.virtualDrive.getPlaceholderWithStatePending()) as Array; + Logger.info('[SYNC ENGINE] fileInPendingPaths', fileInPendingPaths); + await this.container.fileSyncOrchestrator.run(fileInPendingPaths); + } } diff --git a/src/apps/sync-engine/callbacks-controllers/controllers/AddController.ts b/src/apps/sync-engine/callbacks-controllers/controllers/AddController.ts index c56cc9ecc..2645c6ce4 100644 --- a/src/apps/sync-engine/callbacks-controllers/controllers/AddController.ts +++ b/src/apps/sync-engine/callbacks-controllers/controllers/AddController.ts @@ -89,6 +89,7 @@ export class AddController extends CallbackController { const posixDir = PlatformPathConverter.getFatherPathPosix(posixRelativePath); try { + await new Promise((resolve) => setTimeout(resolve, 1000)); await this.runFolderCreator(posixDir); } catch (error) { Logger.error('Error creating folder father creation:', error); diff --git a/src/apps/sync-engine/dependency-injection/boundaryBridge/BoundaryBridgeContainer.ts b/src/apps/sync-engine/dependency-injection/boundaryBridge/BoundaryBridgeContainer.ts index a885d570f..eb12b6e6c 100644 --- a/src/apps/sync-engine/dependency-injection/boundaryBridge/BoundaryBridgeContainer.ts +++ b/src/apps/sync-engine/dependency-injection/boundaryBridge/BoundaryBridgeContainer.ts @@ -1,5 +1,7 @@ import { FileCreationOrchestrator } from '../../../../context/virtual-drive/boundaryBridge/application/FileCreationOrchestrator'; +import { FileSyncOrchestrator } from '../../../../context/virtual-drive/boundaryBridge/application/FileSyncOrchestrator'; export interface BoundaryBridgeContainer { fileCreationOrchestrator: FileCreationOrchestrator; + fileSyncOrchestrator: FileSyncOrchestrator; } diff --git a/src/apps/sync-engine/dependency-injection/boundaryBridge/build.ts b/src/apps/sync-engine/dependency-injection/boundaryBridge/build.ts index bb065d7e1..7afb7e851 100644 --- a/src/apps/sync-engine/dependency-injection/boundaryBridge/build.ts +++ b/src/apps/sync-engine/dependency-injection/boundaryBridge/build.ts @@ -2,6 +2,7 @@ import { BoundaryBridgeContainer } from './BoundaryBridgeContainer'; import { ContentsContainer } from '../contents/ContentsContainer'; import { FilesContainer } from '../files/FilesContainer'; import { FileCreationOrchestrator } from '../../../../context/virtual-drive/boundaryBridge/application/FileCreationOrchestrator'; +import { FileSyncOrchestrator } from '../../../../context/virtual-drive/boundaryBridge/application/FileSyncOrchestrator'; export function buildBoundaryBridgeContainer( contentsContainer: ContentsContainer, @@ -13,7 +14,13 @@ export function buildBoundaryBridgeContainer( filesContainer.sameFileWasMoved ); + const fileSyncOrchestrator = new FileSyncOrchestrator( + contentsContainer.contentsUploader, + filesContainer.fileSyncronizer + ); + return { fileCreationOrchestrator, + fileSyncOrchestrator, }; } diff --git a/src/apps/sync-engine/dependency-injection/files/FilesContainer.ts b/src/apps/sync-engine/dependency-injection/files/FilesContainer.ts index 5ba634247..12fa6eab3 100644 --- a/src/apps/sync-engine/dependency-injection/files/FilesContainer.ts +++ b/src/apps/sync-engine/dependency-injection/files/FilesContainer.ts @@ -9,12 +9,16 @@ import { FilesPlaceholderCreator } from '../../../../context/virtual-drive/files import { RepositoryPopulator } from '../../../../context/virtual-drive/files/application/RepositoryPopulator'; import { RetrieveAllFiles } from '../../../../context/virtual-drive/files/application/RetrieveAllFiles'; import { SameFileWasMoved } from '../../../../context/virtual-drive/files/application/SameFileWasMoved'; +import { FileSyncronizer } from '../../../../context/virtual-drive/files/application/FileSyncronizer'; +import { FilePlaceholderConverter } from '../../../../context/virtual-drive/files/application/FIlePlaceholderConverter'; +import { FileSyncStatusUpdater } from '../../../../context/virtual-drive/files/application/FileSyncStatusUpdater'; export interface FilesContainer { fileFinderByContentsId: FileFinderByContentsId; fileDeleter: FileDeleter; filePathUpdater: FilePathUpdater; fileCreator: FileCreator; + fileSyncronizer: FileSyncronizer; filePlaceholderCreatorFromContentsId: FilePlaceholderCreatorFromContentsId; createFilePlaceholderOnDeletionFailed: CreateFilePlaceholderOnDeletionFailed; sameFileWasMoved: SameFileWasMoved; @@ -22,4 +26,6 @@ export interface FilesContainer { repositoryPopulator: RepositoryPopulator; filesPlaceholderCreator: FilesPlaceholderCreator; filesPlaceholderUpdater: FilesPlaceholderUpdater; + filePlaceholderConverter: FilePlaceholderConverter; + fileSyncStatusUpdater: FileSyncStatusUpdater; } diff --git a/src/apps/sync-engine/dependency-injection/files/builder.ts b/src/apps/sync-engine/dependency-injection/files/builder.ts index e9cf5c424..63f758eee 100644 --- a/src/apps/sync-engine/dependency-injection/files/builder.ts +++ b/src/apps/sync-engine/dependency-injection/files/builder.ts @@ -24,6 +24,9 @@ import { SDKRemoteFileSystem } from '../../../../context/virtual-drive/files/inf import { NodeWinLocalFileSystem } from '../../../../context/virtual-drive/files/infrastructure/NodeWinLocalFileSystem'; import { LocalFileIdProvider } from '../../../../context/virtual-drive/shared/application/LocalFileIdProvider'; import { DependencyInjectionHttpClientsProvider } from '../common/clients'; +import { FileSyncronizer } from '../../../../context/virtual-drive/files/application/FileSyncronizer'; +import { FilePlaceholderConverter } from '../../../../context/virtual-drive/files/application/FIlePlaceholderConverter'; +import { FileSyncStatusUpdater } from '../../../../context/virtual-drive/files/application/FileSyncStatusUpdater'; export async function buildFilesContainer( folderContainer: FoldersContainer, @@ -115,11 +118,29 @@ export async function buildFilesContainer( eventHistory ); + const filePlaceholderConverter = new FilePlaceholderConverter( + localFileSystem + ); + + const fileSyncStatusUpdater = new FileSyncStatusUpdater(localFileSystem); + + const fileSyncronizer = new FileSyncronizer( + repository, + fileSyncStatusUpdater, + filePlaceholderConverter, + fileCreator, + sharedContainer.absolutePathToRelativeConverter, + folderContainer.folderCreator, + folderContainer.offline.folderCreator, + folderContainer.foldersFatherSyncStatusUpdater + ); + const container: FilesContainer = { fileFinderByContentsId, fileDeleter, filePathUpdater, fileCreator, + fileSyncronizer, filePlaceholderCreatorFromContentsId: filePlaceholderCreatorFromContentsId, createFilePlaceholderOnDeletionFailed: createFilePlaceholderOnDeletionFailed, @@ -128,6 +149,8 @@ export async function buildFilesContainer( repositoryPopulator: repositoryPopulator, filesPlaceholderCreator, filesPlaceholderUpdater, + filePlaceholderConverter, + fileSyncStatusUpdater, }; return { container, subscribers: [] }; diff --git a/src/apps/sync-engine/dependency-injection/folders/FoldersContainer.ts b/src/apps/sync-engine/dependency-injection/folders/FoldersContainer.ts index bcb774e3d..de4e028f0 100644 --- a/src/apps/sync-engine/dependency-injection/folders/FoldersContainer.ts +++ b/src/apps/sync-engine/dependency-injection/folders/FoldersContainer.ts @@ -12,6 +12,9 @@ import { RetrieveAllFolders } from '../../../../context/virtual-drive/folders/ap import { SynchronizeOfflineModifications } from '../../../../context/virtual-drive/folders/application/SynchronizeOfflineModifications'; import { SynchronizeOfflineModificationsOnFolderCreated } from '../../../../context/virtual-drive/folders/application/SynchronizeOfflineModificationsOnFolderCreated'; import { FolderPlaceholderUpdater } from '../../../../context/virtual-drive/folders/application/UpdatePlaceholderFolder'; +import { FolderPlaceholderConverter } from '../../../../context/virtual-drive/folders/application/FolderPlaceholderConverter'; +import { FolderSyncStatusUpdater } from '../../../../context/virtual-drive/folders/application/FolderSyncStatusUpdater'; +import { FoldersFatherSyncStatusUpdater } from '../../../../context/virtual-drive/folders/application/FoldersFatherSyncStatusUpdater'; export interface FoldersContainer { folderCreator: FolderCreator; @@ -30,4 +33,7 @@ export interface FoldersContainer { folderRepositoryInitiator: FolderRepositoryInitiator; folderPlaceholderUpdater: FolderPlaceholderUpdater; foldersPlaceholderCreator: FoldersPlaceholderCreator; + folderPlaceholderConverter: FolderPlaceholderConverter; + folderSyncStatusUpdater: FolderSyncStatusUpdater; + foldersFatherSyncStatusUpdater: FoldersFatherSyncStatusUpdater; } diff --git a/src/apps/sync-engine/dependency-injection/folders/builder.ts b/src/apps/sync-engine/dependency-injection/folders/builder.ts index b40d16044..233dd2b74 100644 --- a/src/apps/sync-engine/dependency-injection/folders/builder.ts +++ b/src/apps/sync-engine/dependency-injection/folders/builder.ts @@ -27,6 +27,9 @@ import { DependencyInjectionEventRepository } from '../common/eventRepository'; import { DependencyInjectionVirtualDrive } from '../common/virtualDrive'; import { SharedContainer } from '../shared/SharedContainer'; import { FoldersContainer } from './FoldersContainer'; +import { FolderPlaceholderConverter } from '../../../../context/virtual-drive/folders/application/FolderPlaceholderConverter'; +import { FolderSyncStatusUpdater } from '../../../../context/virtual-drive/folders/application/FolderSyncStatusUpdater'; +import { FoldersFatherSyncStatusUpdater } from '../../../../context/virtual-drive/folders/application/FoldersFatherSyncStatusUpdater'; export async function buildFoldersContainer( shredContainer: SharedContainer @@ -38,12 +41,21 @@ export async function buildFoldersContainer( const repository = new InMemoryFolderRepository(); - const localFileSystem = new NodeWinLocalFileSystem(virtualDrive); + const localFileSystem = new NodeWinLocalFileSystem( + virtualDrive, + shredContainer.relativePathToAbsoluteConverter + ); const remoteFileSystem = new HttpRemoteFileSystem( clients.drive, clients.newDrive ); + const folderPlaceholderConverter = new FolderPlaceholderConverter( + localFileSystem + ); + + const folderSyncStatusUpdater = new FolderSyncStatusUpdater(localFileSystem); + const folderFinder = new FolderFinder(repository); const allParentFoldersStatusIsExists = new AllParentFoldersStatusIsExists( @@ -61,7 +73,8 @@ export async function buildFoldersContainer( repository, remoteFileSystem, ipcRendererSyncEngine, - eventBus + eventBus, + folderPlaceholderConverter ); const folderMover = new FolderMover( @@ -124,6 +137,11 @@ export async function buildFoldersContainer( shredContainer.relativePathToAbsoluteConverter ); + const foldersFatherSyncStatusUpdater = new FoldersFatherSyncStatusUpdater( + localFileSystem, + repository + ); + return { folderCreator, folderFinder, @@ -141,5 +159,8 @@ export async function buildFoldersContainer( folderRepositoryInitiator, foldersPlaceholderCreator, folderPlaceholderUpdater, + folderPlaceholderConverter, + folderSyncStatusUpdater, + foldersFatherSyncStatusUpdater, }; } diff --git a/src/apps/sync-engine/dependency-injection/shared/SharedContainer.ts b/src/apps/sync-engine/dependency-injection/shared/SharedContainer.ts index 1649eceac..1386789f7 100644 --- a/src/apps/sync-engine/dependency-injection/shared/SharedContainer.ts +++ b/src/apps/sync-engine/dependency-injection/shared/SharedContainer.ts @@ -1,7 +1,11 @@ import { AbsolutePathToRelativeConverter } from '../../../../context/virtual-drive/shared/application/AbsolutePathToRelativeConverter'; +import { PollingMonitorStart } from '../../../../context/virtual-drive/shared/application/PollingMonitorStart'; +import { PollingMonitorStop } from '../../../../context/virtual-drive/shared/application/PollingMonitorStop'; import { RelativePathToAbsoluteConverter } from '../../../../context/virtual-drive/shared/application/RelativePathToAbsoluteConverter'; export interface SharedContainer { absolutePathToRelativeConverter: AbsolutePathToRelativeConverter; relativePathToAbsoluteConverter: RelativePathToAbsoluteConverter; + pollingMonitorStart: PollingMonitorStart; + pollingMonitorStop: PollingMonitorStop; } diff --git a/src/apps/sync-engine/dependency-injection/shared/builder.ts b/src/apps/sync-engine/dependency-injection/shared/builder.ts index b7b086734..089719499 100644 --- a/src/apps/sync-engine/dependency-injection/shared/builder.ts +++ b/src/apps/sync-engine/dependency-injection/shared/builder.ts @@ -1,9 +1,13 @@ import { AbsolutePathToRelativeConverter } from '../../../../context/virtual-drive/shared/application/AbsolutePathToRelativeConverter'; +import { PollingMonitorStart } from '../../../../context/virtual-drive/shared/application/PollingMonitorStart'; +import { PollingMonitorStop } from '../../../../context/virtual-drive/shared/application/PollingMonitorStop'; import { RelativePathToAbsoluteConverter } from '../../../../context/virtual-drive/shared/application/RelativePathToAbsoluteConverter'; +import { PollingMonitor } from '../../../../context/virtual-drive/shared/domain/PollingMonitor'; import { DependencyInjectionLocalRootFolderPath } from '../common/localRootFolderPath'; import { SharedContainer } from './SharedContainer'; export function buildSharedContainer(): SharedContainer { + const MONITORING_PULLING_INTERVAL = 60 * 60 * 1000; const localRootFolderPath = DependencyInjectionLocalRootFolderPath.get(); const absolutePathToRelativeConverter = new AbsolutePathToRelativeConverter( localRootFolderPath @@ -13,8 +17,14 @@ export function buildSharedContainer(): SharedContainer { localRootFolderPath ); + const pollingMonitor = new PollingMonitor(MONITORING_PULLING_INTERVAL); + const pollingMonitorStart = new PollingMonitorStart(pollingMonitor); + const pollingMonitorStop = new PollingMonitorStop(pollingMonitor); + return { absolutePathToRelativeConverter, relativePathToAbsoluteConverter, + pollingMonitorStart, + pollingMonitorStop, }; } diff --git a/src/context/virtual-drive/boundaryBridge/application/FileSyncOrchestrator.ts b/src/context/virtual-drive/boundaryBridge/application/FileSyncOrchestrator.ts new file mode 100644 index 000000000..80e9afc13 --- /dev/null +++ b/src/context/virtual-drive/boundaryBridge/application/FileSyncOrchestrator.ts @@ -0,0 +1,20 @@ +import { RetryContentsUploader } from '../../contents/application/RetryContentsUploader'; +import { FileSyncronizer } from '../../files/application/FileSyncronizer'; + +export class FileSyncOrchestrator { + constructor( + private readonly contentsUploader: RetryContentsUploader, + private readonly fileSyncronizer: FileSyncronizer + ) {} + + run(absolutePaths: string[]): Promise { + return Promise.all( + absolutePaths.map(async (absolutePath) => { + await this.fileSyncronizer.run( + absolutePath, + this.contentsUploader.run.bind(this.contentsUploader) + ); + }) + ); + } +} diff --git a/src/context/virtual-drive/files/application/FIlePlaceholderConverter.ts b/src/context/virtual-drive/files/application/FIlePlaceholderConverter.ts new file mode 100644 index 000000000..763a52482 --- /dev/null +++ b/src/context/virtual-drive/files/application/FIlePlaceholderConverter.ts @@ -0,0 +1,10 @@ +import { File } from '../domain/File'; +import { LocalFileSystem } from '../domain/file-systems/LocalFileSystem'; + +export class FilePlaceholderConverter { + constructor(private readonly localFileSystem: LocalFileSystem) {} + + async run(file: File) { + await this.localFileSystem.convertToPlaceholder(file); + } +} diff --git a/src/context/virtual-drive/files/application/FileContentsUpdater.ts b/src/context/virtual-drive/files/application/FileContentsUpdater.ts new file mode 100644 index 000000000..e0a189bc9 --- /dev/null +++ b/src/context/virtual-drive/files/application/FileContentsUpdater.ts @@ -0,0 +1,13 @@ +// import { FileRepository } from '../domain/FileRepository'; +// import { RemoteFileSystem } from '../domain/file-systems/RemoteFileSystem'; + +// export class FileContentsUpdater { +// constructor( +// private readonly repository: FileRepository, +// private readonly remote: RemoteFileSystem +// ) {} + +// async run(file: File): Promise { +// await this.local.updateSyncStatus(file); +// } +// } diff --git a/src/context/virtual-drive/files/application/FileCreator.ts b/src/context/virtual-drive/files/application/FileCreator.ts index 3128d7828..ffab80019 100644 --- a/src/context/virtual-drive/files/application/FileCreator.ts +++ b/src/context/virtual-drive/files/application/FileCreator.ts @@ -22,15 +22,21 @@ export class FileCreator { private readonly ipc: SyncEngineIpc ) {} - async run(filePath: FilePath, contents: RemoteFileContents): Promise { + async run( + filePath: FilePath, + contents: RemoteFileContents, + existingFileAlreadyEvaluated = false + ): Promise { try { - const existingFile = this.repository.searchByPartial({ - path: PlatformPathConverter.winToPosix(filePath.value), - status: FileStatuses.EXISTS, - }); + if (!existingFileAlreadyEvaluated) { + const existingFile = this.repository.searchByPartial({ + path: PlatformPathConverter.winToPosix(filePath.value), + status: FileStatuses.EXISTS, + }); - if (existingFile) { - await this.fileDeleter.run(existingFile.contentsId); + if (existingFile) { + await this.fileDeleter.run(existingFile.contentsId); + } } const size = new FileSize(contents.size); diff --git a/src/context/virtual-drive/files/application/FileSyncStatusUpdater.ts b/src/context/virtual-drive/files/application/FileSyncStatusUpdater.ts new file mode 100644 index 000000000..a03a284e8 --- /dev/null +++ b/src/context/virtual-drive/files/application/FileSyncStatusUpdater.ts @@ -0,0 +1,10 @@ +import { File } from '../domain/File'; +import { LocalFileSystem } from '../domain/file-systems/LocalFileSystem'; + +export class FileSyncStatusUpdater { + constructor(private readonly localFileSystem: LocalFileSystem) {} + + async run(file: File) { + await this.localFileSystem.updateSyncStatus(file); + } +} diff --git a/src/context/virtual-drive/files/application/FileSyncronizer.ts b/src/context/virtual-drive/files/application/FileSyncronizer.ts new file mode 100644 index 000000000..f38038945 --- /dev/null +++ b/src/context/virtual-drive/files/application/FileSyncronizer.ts @@ -0,0 +1,124 @@ +import { RemoteFileContents } from '../../contents/domain/RemoteFileContents'; +import { PlatformPathConverter } from '../../shared/application/PlatformPathConverter'; +import { FilePath } from '../domain/FilePath'; +import { FileRepository } from '../domain/FileRepository'; +import { FileStatuses } from '../domain/FileStatus'; +import Logger from 'electron-log'; +import { FileCreator } from './FileCreator'; +import { AbsolutePathToRelativeConverter } from '../../shared/application/AbsolutePathToRelativeConverter'; +import { FolderNotFoundError } from '../../folders/domain/errors/FolderNotFoundError'; +import { FolderCreator } from '../../folders/application/FolderCreator'; +import { OfflineFolderCreator } from '../../folders/application/Offline/OfflineFolderCreator'; +import { Folder } from '../../folders/domain/Folder'; +import * as fs from 'fs'; +import { File } from '../domain/File'; +import { FileSyncStatusUpdater } from './FileSyncStatusUpdater'; +import { FilePlaceholderConverter } from './FIlePlaceholderConverter'; +import { FoldersFatherSyncStatusUpdater } from '../../folders/application/FoldersFatherSyncStatusUpdater'; + +export class FileSyncronizer { + constructor( + private readonly repository: FileRepository, + private readonly fileSyncStatusUpdater: FileSyncStatusUpdater, + private readonly filePlaceholderConverter: FilePlaceholderConverter, + private readonly fileCreator: FileCreator, + private readonly absolutePathToRelativeConverter: AbsolutePathToRelativeConverter, + private readonly folderCreator: FolderCreator, + private readonly offlineFolderCreator: OfflineFolderCreator, + private readonly foldersFatherSyncStatusUpdater: FoldersFatherSyncStatusUpdater + ) {} + + async run( + absolutePath: string, + upload: (path: string) => Promise + ) { + const win32RelativePath = + this.absolutePathToRelativeConverter.run(absolutePath); + + const posixRelativePath = + PlatformPathConverter.winToPosix(win32RelativePath); + + const path = new FilePath(posixRelativePath); + + const existingFile = this.repository.searchByPartial({ + path: PlatformPathConverter.winToPosix(path.value), + status: FileStatuses.EXISTS, + }); + + if (existingFile) { + if (this.hasDifferentSize(existingFile, absolutePath)) { + return; + } + await this.convertAndUpdateSyncStatus(existingFile); + } else { + await this.retryCreation(posixRelativePath, path, upload); + } + } + + private retryCreation = async ( + posixRelativePath: string, + filePath: FilePath, + upload: (path: string) => Promise, + attemps = 3 + ) => { + try { + const fileContents = await upload(posixRelativePath); + const createdFile = await this.fileCreator.run(filePath, fileContents); + await this.convertAndUpdateSyncStatus(createdFile); + } catch (error: unknown) { + Logger.error('Error creating file:', error); + if (error instanceof FolderNotFoundError) { + await this.createFolderFather(posixRelativePath); + } + if (attemps > 0) { + await new Promise((resolve) => setTimeout(resolve, 2000)); + await this.retryCreation( + posixRelativePath, + filePath, + upload, + attemps - 1 + ); + return; + } + } + }; + + private async runFolderCreator(posixRelativePath: string): Promise { + const offlineFolder = this.offlineFolderCreator.run(posixRelativePath); + return this.folderCreator.run(offlineFolder); + } + + private async createFolderFather(posixRelativePath: string) { + Logger.info('posixRelativePath', posixRelativePath); + const posixDir = + PlatformPathConverter.getFatherPathPosix(posixRelativePath); + try { + await new Promise((resolve) => setTimeout(resolve, 1000)); + await this.runFolderCreator(posixDir); + } catch (error) { + Logger.error('Error creating folder father creation:', error); + if (error instanceof FolderNotFoundError) { + // father created + await this.createFolderFather(posixDir); + // child created + await this.runFolderCreator(posixDir); + } else { + Logger.error( + 'Error creating folder father creation inside catch:', + error + ); + throw error; + } + } + } + + private hasDifferentSize(file: File, absoulthePath: string) { + const stats = fs.statSync(absoulthePath); + return Math.abs(file.size - stats.size) > 0.001; + } + + private async convertAndUpdateSyncStatus(file: File) { + await this.filePlaceholderConverter.run(file); + await this.fileSyncStatusUpdater.run(file); + } +} diff --git a/src/context/virtual-drive/files/domain/file-systems/LocalFileSystem.ts b/src/context/virtual-drive/files/domain/file-systems/LocalFileSystem.ts index acdddbb1d..a7fb66102 100644 --- a/src/context/virtual-drive/files/domain/file-systems/LocalFileSystem.ts +++ b/src/context/virtual-drive/files/domain/file-systems/LocalFileSystem.ts @@ -4,4 +4,8 @@ export interface LocalFileSystem { createPlaceHolder(file: File): Promise; getLocalFileId(file: File): Promise<`${string}-${string}`>; + + updateSyncStatus(file: File): Promise; + + convertToPlaceholder(file: File): Promise; } diff --git a/src/context/virtual-drive/files/infrastructure/NodeWinLocalFileSystem.ts b/src/context/virtual-drive/files/infrastructure/NodeWinLocalFileSystem.ts index 49e7e5cf1..21682a31f 100644 --- a/src/context/virtual-drive/files/infrastructure/NodeWinLocalFileSystem.ts +++ b/src/context/virtual-drive/files/infrastructure/NodeWinLocalFileSystem.ts @@ -34,4 +34,22 @@ export class NodeWinLocalFileSystem implements LocalFileSystem { file.updatedAt.getTime() ); } + + async updateSyncStatus(file: File): Promise { + const win32AbsolutePath = this.relativePathToAbsoluteConverter.run( + file.path + ); + return this.virtualDrive.updateSyncStatus(win32AbsolutePath, false); + } + + async convertToPlaceholder(file: File): Promise { + const win32AbsolutePath = this.relativePathToAbsoluteConverter.run( + file.path + ); + + return this.virtualDrive.convertToPlaceholder( + win32AbsolutePath, + file.placeholderId + ); + } } diff --git a/src/context/virtual-drive/files/infrastructure/dtos/ReplaceFileDTO.ts b/src/context/virtual-drive/files/infrastructure/dtos/ReplaceFileDTO.ts new file mode 100644 index 000000000..be1cd6457 --- /dev/null +++ b/src/context/virtual-drive/files/infrastructure/dtos/ReplaceFileDTO.ts @@ -0,0 +1,6 @@ +import { File } from '../../domain/File'; + +export interface ReplaceFileDTO { + fileId: File['contentsId']; + size: File['size']; +} diff --git a/src/context/virtual-drive/folders/application/FolderCreator.ts b/src/context/virtual-drive/folders/application/FolderCreator.ts index 1e3a83f36..0b64008dc 100644 --- a/src/context/virtual-drive/folders/application/FolderCreator.ts +++ b/src/context/virtual-drive/folders/application/FolderCreator.ts @@ -4,13 +4,15 @@ import { Folder } from '../domain/Folder'; import { FolderRepository } from '../domain/FolderRepository'; import { OfflineFolder } from '../domain/OfflineFolder'; import { RemoteFileSystem } from '../domain/file-systems/RemoteFileSystem'; +import { FolderPlaceholderConverter } from './FolderPlaceholderConverter'; export class FolderCreator { constructor( private readonly repository: FolderRepository, private readonly remote: RemoteFileSystem, private readonly ipc: SyncEngineIpc, - private readonly eventBus: EventBus + private readonly eventBus: EventBus, + private readonly folderPlaceholderConverter: FolderPlaceholderConverter ) {} async run(offlineFolder: OfflineFolder): Promise { @@ -27,6 +29,8 @@ export class FolderCreator { const events = folder.pullDomainEvents(); this.eventBus.publish(events); + await this.folderPlaceholderConverter.run(folder); + this.ipc.send('FOLDER_CREATED', { name: offlineFolder.name, }); diff --git a/src/context/virtual-drive/folders/application/FolderPlaceholderConverter.ts b/src/context/virtual-drive/folders/application/FolderPlaceholderConverter.ts new file mode 100644 index 000000000..b3d84f30c --- /dev/null +++ b/src/context/virtual-drive/folders/application/FolderPlaceholderConverter.ts @@ -0,0 +1,10 @@ +import { Folder } from '../domain/Folder'; +import { LocalFileSystem } from '../domain/file-systems/LocalFileSystem'; + +export class FolderPlaceholderConverter { + constructor(private readonly localFileSystem: LocalFileSystem) {} + + async run(folder: Folder) { + await this.localFileSystem.convertToPlaceholder(folder); + } +} diff --git a/src/context/virtual-drive/folders/application/FolderSyncStatusUpdater.ts b/src/context/virtual-drive/folders/application/FolderSyncStatusUpdater.ts new file mode 100644 index 000000000..d333caf4c --- /dev/null +++ b/src/context/virtual-drive/folders/application/FolderSyncStatusUpdater.ts @@ -0,0 +1,10 @@ +import { Folder } from '../domain/Folder'; +import { LocalFileSystem } from '../domain/file-systems/LocalFileSystem'; + +export class FolderSyncStatusUpdater { + constructor(private readonly localFileSystem: LocalFileSystem) {} + + async run(folder: Folder) { + await this.localFileSystem.updateSyncStatus(folder); + } +} diff --git a/src/context/virtual-drive/folders/application/FoldersFatherSyncStatusUpdater.ts b/src/context/virtual-drive/folders/application/FoldersFatherSyncStatusUpdater.ts new file mode 100644 index 000000000..6b94883df --- /dev/null +++ b/src/context/virtual-drive/folders/application/FoldersFatherSyncStatusUpdater.ts @@ -0,0 +1,30 @@ +import { File } from '../../files/domain/File'; +import { PlatformPathConverter } from '../../shared/application/PlatformPathConverter'; +import { Folder } from '../domain/Folder'; +import { FolderRepository } from '../domain/FolderRepository'; +import { LocalFileSystem } from '../domain/file-systems/LocalFileSystem'; +import Logger from 'electron-log'; + +export class FoldersFatherSyncStatusUpdater { + constructor( + private readonly localFileSystem: LocalFileSystem, + private readonly repository: FolderRepository + ) {} + + async run(file: File): Promise { + return this.update(file.path); + } + + private async update(path: File['path'] | Folder['path']) { + const posixDir = PlatformPathConverter.getFatherPathPosix(path); + if (posixDir === '/') { + return; + } + const folder = await this.repository.searchByPartial({ path: posixDir }); + if (folder) { + Logger.debug(`Updating sync status for ${folder.path}`); + await this.localFileSystem.updateSyncStatus(folder); + } + this.update(posixDir); + } +} diff --git a/src/context/virtual-drive/folders/domain/file-systems/LocalFileSystem.ts b/src/context/virtual-drive/folders/domain/file-systems/LocalFileSystem.ts index b16434a46..e686e68ca 100644 --- a/src/context/virtual-drive/folders/domain/file-systems/LocalFileSystem.ts +++ b/src/context/virtual-drive/folders/domain/file-systems/LocalFileSystem.ts @@ -2,4 +2,8 @@ import { Folder } from '../Folder'; export interface LocalFileSystem { createPlaceHolder(folder: Folder): Promise; + + updateSyncStatus(folder: Folder): Promise; + + convertToPlaceholder(folder: Folder): Promise; } diff --git a/src/context/virtual-drive/folders/infrastructure/NodeWinLocalFileSystem.ts b/src/context/virtual-drive/folders/infrastructure/NodeWinLocalFileSystem.ts index 1c1a2d5b2..f644ea8fa 100644 --- a/src/context/virtual-drive/folders/infrastructure/NodeWinLocalFileSystem.ts +++ b/src/context/virtual-drive/folders/infrastructure/NodeWinLocalFileSystem.ts @@ -2,9 +2,13 @@ import { VirtualDrive } from 'virtual-drive/dist'; import { Folder } from '../domain/Folder'; import { FolderStatuses } from '../domain/FolderStatus'; import { LocalFileSystem } from '../domain/file-systems/LocalFileSystem'; +import { RelativePathToAbsoluteConverter } from '../../shared/application/RelativePathToAbsoluteConverter'; export class NodeWinLocalFileSystem implements LocalFileSystem { - constructor(private readonly virtualDrive: VirtualDrive) {} + constructor( + private readonly virtualDrive: VirtualDrive, + private readonly relativePathToAbsoluteConverter: RelativePathToAbsoluteConverter + ) {} async createPlaceHolder(folder: Folder): Promise { if (!folder.hasStatus(FolderStatuses.EXISTS)) { @@ -21,4 +25,24 @@ export class NodeWinLocalFileSystem implements LocalFileSystem { folder.updatedAt.getTime() ); } + + async updateSyncStatus(folder: Folder): Promise { + const folderPath = `${folder.path}/`; + const win32AbsolutePath = + this.relativePathToAbsoluteConverter.run(folderPath); + + return this.virtualDrive.updateSyncStatus(win32AbsolutePath, true); + } + + async convertToPlaceholder(folder: Folder): Promise { + const folderPath = `${folder.path}/`; + + const win32AbsolutePath = + this.relativePathToAbsoluteConverter.run(folderPath); + + return this.virtualDrive.convertToPlaceholder( + win32AbsolutePath, + folder.placeholderId + ); + } } diff --git a/src/context/virtual-drive/shared/application/PlatformPathConverter.ts b/src/context/virtual-drive/shared/application/PlatformPathConverter.ts index fb1133e1f..1398bf62e 100644 --- a/src/context/virtual-drive/shared/application/PlatformPathConverter.ts +++ b/src/context/virtual-drive/shared/application/PlatformPathConverter.ts @@ -21,7 +21,7 @@ export class PlatformPathConverter { static getFatherPathPosix(posixPath: string): string { const pathArray = posixPath.split('/'); - const folderToCreate = pathArray.pop(); + pathArray.pop(); const parentPath = pathArray.join('/'); return this.winToPosix(parentPath); } diff --git a/src/context/virtual-drive/shared/application/PollingMonitorStart.ts b/src/context/virtual-drive/shared/application/PollingMonitorStart.ts new file mode 100644 index 000000000..6c0f2918c --- /dev/null +++ b/src/context/virtual-drive/shared/application/PollingMonitorStart.ts @@ -0,0 +1,8 @@ +import { MonitorFn, PollingMonitor } from '../domain/PollingMonitor'; + +export class PollingMonitorStart { + constructor(private readonly polling: PollingMonitor) {} + run(fn: MonitorFn) { + return this.polling.start(fn); + } +} diff --git a/src/context/virtual-drive/shared/application/PollingMonitorStop.ts b/src/context/virtual-drive/shared/application/PollingMonitorStop.ts new file mode 100644 index 000000000..cdaee08c3 --- /dev/null +++ b/src/context/virtual-drive/shared/application/PollingMonitorStop.ts @@ -0,0 +1,8 @@ +import { PollingMonitor } from '../domain/PollingMonitor'; + +export class PollingMonitorStop { + constructor(private readonly polling: PollingMonitor) {} + run() { + return this.polling.stop(); + } +} diff --git a/src/context/virtual-drive/shared/domain/PollingMonitor.ts b/src/context/virtual-drive/shared/domain/PollingMonitor.ts new file mode 100644 index 000000000..ef232677a --- /dev/null +++ b/src/context/virtual-drive/shared/domain/PollingMonitor.ts @@ -0,0 +1,29 @@ +export type MonitorFn = () => Promise; +export class PollingMonitor { + constructor(private readonly delay: number) {} + + private timeout: NodeJS.Timeout | null = null; + + private clearTimeout() { + if (this.timeout) { + clearTimeout(this.timeout); + this.timeout = null; + } + } + + private setTimeout(fn: MonitorFn) { + this.clearTimeout(); + this.timeout = setTimeout(async () => { + await fn(); + this.setTimeout(fn); + }, this.delay); + } + + start(fn: MonitorFn) { + this.setTimeout(fn); + } + + stop() { + this.clearTimeout(); + } +} diff --git a/tests/context/virtual-drive/files/__mocks__/LocalFileSystemMock.ts b/tests/context/virtual-drive/files/__mocks__/LocalFileSystemMock.ts index 6495b0307..3ec74e1a0 100644 --- a/tests/context/virtual-drive/files/__mocks__/LocalFileSystemMock.ts +++ b/tests/context/virtual-drive/files/__mocks__/LocalFileSystemMock.ts @@ -4,6 +4,7 @@ import { File } from '../../../../../src/context/virtual-drive/files/domain/File export class LocalFileSystemMock implements LocalFileSystem { public readonly createPlaceHolderMock = jest.fn(); public readonly getLocalFileIdMock = jest.fn(); + public readonly updateSyncStatusMock = jest.fn(); createPlaceHolder(file: File): Promise { return this.createPlaceHolder(file); @@ -12,4 +13,8 @@ export class LocalFileSystemMock implements LocalFileSystem { getLocalFileId(file: File): Promise<`${string}-${string}`> { return this.getLocalFileIdMock(file); } + + updateSyncStatus(file: File): Promise { + throw this.updateSyncStatusMock(file); + } }