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
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import { registerFolderServices } from './registerFolderServices';
import { registerVirtualDriveSharedServices } from './registerVirtualDriveSharedServices';
import { registerTreeServices } from './registerTreeServices';
import { LazyVirtualDriveMetadataSynchronizationService } from '../../../../backend/features/virtual-drive/services/lazy/LazyVirtualDriveMetadataSynchronizationService';
import { FileRepository } from '../../../../context/virtual-drive/files/domain/FileRepository';
import { FolderRepository } from '../../../../context/virtual-drive/folders/domain/FolderRepository';

export class VirtualDriveDependencyContainerFactory {
static async build(builder: ContainerBuilder): Promise<void> {
Expand All @@ -12,6 +15,13 @@

await registerFolderServices(builder);

registerFilesServices(builder);
await registerFilesServices(builder);

Check failure on line 18 in src/apps/drive/dependency-injection/virtual-drive/VirtualDriveDependencyContainerFactory.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` of a non-Promise (non-"Thenable") value.

See more on https://sonarcloud.io/project/issues?id=internxt_drive-desktop-linux&issues=AaEiX_-kl9jCT5nsfFJC&open=AaEiX_-kl9jCT5nsfFJC&pullRequest=470

builder.register(LazyVirtualDriveMetadataSynchronizationService).useFactory((container) => {
return new LazyVirtualDriveMetadataSynchronizationService({
folderRepository: container.get(FolderRepository),
fileRepository: container.get(FileRepository),
});
});
}
}
9 changes: 4 additions & 5 deletions src/backend/features/auth/get-user-or-throw.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { getUser } from './get-user';


export function getUserOrThrow() {
const { data: user, error } = getUser();
if (error) throw error;
const { data: user, error } = getUser();
if (error) throw error;

return user;
}
return user;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { FileRepository } from '../../../../../context/virtual-drive/files/domain/FileRepository';
import { FolderRepository } from '../../../../../context/virtual-drive/folders/domain/FolderRepository';
import { LazyVirtualDriveMetadataSynchronizer } from './lazy-virtual-drive-metadata-synchronizer';

type Props = {
folderRepository: FolderRepository;
fileRepository: FileRepository;
};

export class LazyVirtualDriveMetadataSynchronizationService {
constructor({ folderRepository, fileRepository }: Props) {
this.folderRepository = folderRepository;
this.fileRepository = fileRepository;
}

private readonly folderRepository: FolderRepository;
private readonly fileRepository: FileRepository;

readDirectory({ path }: { path: string }) {
return LazyVirtualDriveMetadataSynchronizer.readDirectory({
path,
folderRepository: this.folderRepository,
fileRepository: this.fileRepository,
});
}

ensurePathMetadataSynchronized({ path }: { path: string }) {
return LazyVirtualDriveMetadataSynchronizer.ensurePathMetadataSynchronized({
path,
folderRepository: this.folderRepository,
fileRepository: this.fileRepository,
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,15 @@ describe('directory-state-sqlite-repository', () => {
]);
});

it('should invalidate a directory state', async () => {
await DirectoryStateRepository.invalidate({ dataSource, ...stateProps });

call(queryMock).toStrictEqual([
'DELETE FROM drive_directory_state WHERE folder_id = ? AND status_scope = ?',
[42, 'EXISTS'],
]);
});

it('should clear all directory states', async () => {
await DirectoryStateRepository.clear({ dataSource });

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { AppDataSource } from '../../../../../apps/main/database/data-source';
import { DIRECTORY_STATE_TTL_MS } from './constants';
import type {
DirectoryStateFreshnessProps,
DirectoryStateInvalidationProps,
DirectoryStateOperationProps,
DirectoryStateRepositoryProps,
} from './types';
Expand Down Expand Up @@ -45,6 +46,13 @@ async function markError({ dataSource = AppDataSource, folderId, statusScope }:
);
}

async function invalidate({ dataSource = AppDataSource, folderId, statusScope }: DirectoryStateInvalidationProps) {
await dataSource.query('DELETE FROM drive_directory_state WHERE folder_id = ? AND status_scope = ?', [
folderId,
statusScope,
]);
}

async function clear({ dataSource = AppDataSource }: DirectoryStateRepositoryProps = {}) {
await dataSource.query('DELETE FROM drive_directory_state');
}
Expand All @@ -53,5 +61,6 @@ export const DirectoryStateRepository = {
isFresh,
markLoaded,
markError,
invalidate,
clear,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { mockDeep } from 'vitest-mock-extended';
import { FolderRepository } from '../../../../../context/virtual-drive/folders/domain/FolderRepository';
import { FileRepository } from '../../../../../context/virtual-drive/files/domain/FileRepository';
import { FolderMother } from '../../../../../context/virtual-drive/folders/domain/__test-helpers__/FolderMother';
import { LazyVirtualDriveMetadataSynchronizer } from './lazy-virtual-drive-metadata-synchronizer';

const { isFreshMock, synchronizeDirectoryMetadataMock } = vi.hoisted(() => ({
isFreshMock: vi.fn(),
synchronizeDirectoryMetadataMock: vi.fn(),
}));

vi.mock('./directory-state-sqlite-repository', () => ({
DirectoryStateRepository: { isFresh: isFreshMock },
}));

vi.mock('./synchronize-directory-metadata', () => ({
synchronizeDirectoryMetadata: synchronizeDirectoryMetadataMock,
}));

describe('lazy-virtual-drive-metadata-synchronizer', () => {
const rootFolder = FolderMother.root();
const folderRepository = mockDeep<FolderRepository>();
const fileRepository = mockDeep<FileRepository>();

beforeEach(() => {
folderRepository.matchingPartial.mockImplementation((partial) => (partial.path === '/' ? [rootFolder] : []));
fileRepository.matchingPartial.mockReturnValue([]);
isFreshMock.mockResolvedValue(true);
synchronizeDirectoryMetadataMock.mockResolvedValue(undefined);
});

it('should list locally cached children when the directory state is fresh', async () => {
const childFolder = FolderMother.fromPartial({ id: 99, parentId: rootFolder.id, path: '/documents' });
folderRepository.matchingPartial.mockImplementation((partial) => {
if (partial.path === '/') return [rootFolder];
if (partial.parentId === rootFolder.id) return [childFolder];
return [];
});

const entries = await LazyVirtualDriveMetadataSynchronizer.readDirectory({
path: '/',
folderRepository,
fileRepository,
});

expect(entries).toStrictEqual({ folders: [childFolder.name], files: [] });
expect(synchronizeDirectoryMetadataMock).not.toHaveBeenCalled();
});

it('should synchronize a stale directory before returning its children', async () => {
isFreshMock.mockResolvedValue(false);

await LazyVirtualDriveMetadataSynchronizer.readDirectory({ path: '/', folderRepository, fileRepository });

expect(synchronizeDirectoryMetadataMock).toHaveBeenCalledWith({
folder: rootFolder,
folderRepository,
fileRepository,
});
});

it('should synchronize the parent directory metadata before resolving a missing path', async () => {
isFreshMock.mockResolvedValue(false);

await LazyVirtualDriveMetadataSynchronizer.ensurePathMetadataSynchronized({
path: '/missing-file.txt',
folderRepository,
fileRepository,
});

expect(synchronizeDirectoryMetadataMock).toHaveBeenCalledWith({
folder: rootFolder,
folderRepository,
fileRepository,
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import path from 'node:path';
import { FuseCodes } from '../../../../../apps/drive/fuse/callbacks/FuseCodes';
import { FuseError } from '../../../../../apps/drive/fuse/callbacks/FuseErrors';
import { FileRepository } from '../../../../../context/virtual-drive/files/domain/FileRepository';
import { Folder } from '../../../../../context/virtual-drive/folders/domain/Folder';
import { FolderRepository } from '../../../../../context/virtual-drive/folders/domain/FolderRepository';
import { FileStatuses } from '../../../../../context/virtual-drive/files/domain/FileStatus';
import { FolderStatuses } from '../../../../../context/virtual-drive/folders/domain/FolderStatus';
import { DirectoryStateRepository } from './directory-state-sqlite-repository';
import { synchronizeDirectoryMetadata } from './synchronize-directory-metadata';

type Props = {
path: string;
folderRepository: FolderRepository;
fileRepository: FileRepository;
};

type DirectoryEntries = {
folders: Array<string>;
files: Array<string>;
};

const inFlightRefreshes = new Map<string, Promise<void>>();

async function readDirectory({
path: requestedPath,
folderRepository,
fileRepository,
}: Props): Promise<DirectoryEntries> {
const folder = await resolveFolder({ path: requestedPath, folderRepository, fileRepository });

await synchronizeChildrenIfStale({ folder, folderRepository, fileRepository });

return {
folders: folderRepository
.matchingPartial({ parentId: folder.id, status: FolderStatuses.EXISTS })
.map((child) => child.name),
files: fileRepository
.matchingPartial({ folderId: folder.id, status: FileStatuses.EXISTS })
.map((child) => child.nameWithExtension),
};
}

async function ensurePathMetadataSynchronized({ path: requestedPath, folderRepository, fileRepository }: Props) {
const normalizedPath = normalizePath(requestedPath);
const parentPath = normalizedPath === '/' ? '/' : path.posix.dirname(normalizedPath);
const parentFolder = await resolveFolder({ path: parentPath, folderRepository, fileRepository });

await synchronizeChildrenIfStale({ folder: parentFolder, folderRepository, fileRepository });
}

async function resolveFolder({ path: requestedPath, folderRepository, fileRepository }: Props): Promise<Folder> {
const rootFolder = folderRepository.matchingPartial({ path: '/' })[0];

if (!rootFolder) {
throw new FuseError(FuseCodes.EIO, '[FUSE - Metadata sync] Root folder not initialized');
}

let currentFolder = rootFolder;
let currentPath = '';

for (const segment of normalizePath(requestedPath).split('/').filter(Boolean)) {
currentPath = path.posix.join(currentPath, segment);
const absolutePath = `/${currentPath}`;
const existingFolder = folderRepository.matchingPartial({ path: absolutePath })[0];

if (existingFolder) {
currentFolder = existingFolder;
continue;
}

await synchronizeChildrenIfStale({ folder: currentFolder, folderRepository, fileRepository });

Check warning on line 72 in src/backend/features/virtual-drive/services/lazy/lazy-virtual-drive-metadata-synchronizer.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=internxt_drive-desktop-linux&issues=AaEiX_-Vl9jCT5nsfFJA&open=AaEiX_-Vl9jCT5nsfFJA&pullRequest=470

const synchronizedFolder = folderRepository.matchingPartial({ path: absolutePath })[0];

if (!synchronizedFolder) {
throw new FuseError(FuseCodes.ENOENT, `[FUSE - Metadata sync] Folder not found: ${absolutePath}`);
}

currentFolder = synchronizedFolder;
}

return currentFolder;
}

async function synchronizeChildrenIfStale({
folder,
folderRepository,
fileRepository,
}: {
folder: Folder;
folderRepository: FolderRepository;
fileRepository: FileRepository;
}) {
const statusScope = 'EXISTS' as const;

if (await DirectoryStateRepository.isFresh({ folderId: folder.id, statusScope })) {
return;
}

const key = `${folder.id}:${statusScope}`;
const inFlightRefresh = inFlightRefreshes.get(key);

if (inFlightRefresh) {
return inFlightRefresh;
}

const refresh = synchronizeDirectoryMetadata({ folder, folderRepository, fileRepository }).finally(() => {
inFlightRefreshes.delete(key);
});

inFlightRefreshes.set(key, refresh);

return refresh;
}

function normalizePath(requestedPath: string) {
const normalizedPath = path.posix.normalize(requestedPath || '/');

return normalizedPath === '.' ? '/' : normalizedPath.startsWith('/') ? normalizedPath : `/${normalizedPath}`;

Check warning on line 120 in src/backend/features/virtual-drive/services/lazy/lazy-virtual-drive-metadata-synchronizer.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

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

export const LazyVirtualDriveMetadataSynchronizer = {
readDirectory,
ensurePathMetadataSynchronized,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { ServerFileStatus } from '../../../../../context/shared/domain/ServerFile';
import { ServerFolderStatus } from '../../../../../context/shared/domain/ServerFolder';
import { toRemoteFile, toRemoteFolder } from './remote-synced-mappers';

describe('remote-synced-mappers', () => {
it('should map remote folders as existing', () => {
const folder = toRemoteFolder({
type: 'folder',
id: 1,
parentId: 0,
bucket: 'bucket',
userId: 2,
createdAt: '2026-01-01T00:00:00.000Z',
updatedAt: '2026-01-02T00:00:00.000Z',
uuid: 'folder-uuid',
plainName: 'Documents',
name: 'Documents',
});

expect(folder).toMatchObject({
id: 1,
parentId: 0,
plainName: 'Documents',
status: ServerFolderStatus.EXISTS,
});
});

it('should normalize nullable identifiers and size for remote files', () => {
const file = toRemoteFile({
id: 1,
uuid: 'file-uuid',
fileId: null,
type: 'txt',
size: '42',
bucket: 'bucket',
folderId: 2,
folderUuid: 'folder-uuid',
userId: 3,
modificationTime: '2026-01-01T00:00:00.000Z',
createdAt: '2026-01-01T00:00:00.000Z',
updatedAt: '2026-01-02T00:00:00.000Z',
plainName: 'notes',
name: 'notes.txt',
});

expect(file).toMatchObject({
fileId: '',
size: 42,
plainName: 'notes',
status: ServerFileStatus.EXISTS,
});
});
});
Loading
Loading