Skip to content

Conversation

@larryrider
Copy link
Contributor

@larryrider larryrider commented Jan 20, 2026

  • Adds B2B workspaces

    • Switch from workspaces using: internxt workspaces use
    • List available workspaces using: internxt workspaces list
    • Switch to personal drive space using: internxt workspaces unset or internxt workspaces use --personal
  • Improved and simplified services

  • Improved and simplified WebDAV handlers and server

  • Refactored some util classes for better testing process

  • Fixed tests and changed from vi.mock to vi.spyon for better consistency

larryrider and others added 25 commits January 14, 2026 17:29
…vFolderService for proper asynchronous handling
@larryrider larryrider self-assigned this Jan 20, 2026
@larryrider larryrider added the enhancement New feature or request label Jan 20, 2026
@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
47.1% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@larryrider larryrider marked this pull request as ready for review January 21, 2026 17:40
@larryrider larryrider requested a review from AlexisMora January 21, 2026 17:40
Comment on lines +40 to +44
SdkManager.init({ token: userCredentials.token });
await ConfigService.instance.saveUser({ ...userCredentials, workspace: undefined });
CLIUtils.success(this.log.bind(this), 'Personal drive space selected successfully.');
return { success: true, message: 'Personal drive space selected successfully.' };
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could encapsulate this logic as you are basically using the same as the use-personal command and reuse it on both places

if (workspaceTokenDetails.expiration.refreshRequired) {
SdkManager.init({ token: loginCreds.token, workspaceToken: loginCreds.workspace.workspaceCredentials.token });
const workspaceCredentials = await WorkspaceService.instance.getWorkspaceCredentials(workspaceUuid);
// TODO refresh also workspace data
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could reference here a jira ticket so that this todo does not gets forgotten if you dont want to do this change is this pr

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also having a look at workspaceData, how much this object is going to change? is it necessary? just out of curiosity

Comment on lines +196 to +197
const privateKeyInBase64 = user.keys?.ecc?.privateKey ?? '';
const privateKyberKeyInBase64 = user.keys?.kyber?.privateKey ?? '';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see what this function is trying to do: Decrypt if possible, default to fallback otherwise.
But this approach have some hidden costs:

the catch block without the proper error handling could make for silent errors, and also raises me this question: does it even make sense to throw an exception that we arent going to handle?

Mutable state increases cognitive load unnecessarly

by fallbacking privateKyberKeyInBase64 and privateKeyInBase64, we are running decryption that we know it will fail.

If we structure this to fail fast and handle each path explicitly. It becomes easier to test and read.
What do you think about this, for example?

private readonly decryptMnemonic = async (
  encryptionKey: string, 
  user: LoginCredentials['user']
): Promise<string> => {
  const privateKeyInBase64 = user.keys?.ecc?.privateKey;
  const privateKyberKeyInBase64 = user.keys?.kyber?.privateKey;
  
  if (!privateKeyInBase64 || !privateKyberKeyInBase64) {
    return user.mnemonic;
  }

  try {
    return await KeysService.instance.hybridDecryptMessageWithPrivateKey({
      encryptedMessageInBase64: encryptionKey,
      privateKeyInBase64,
      privateKyberKeyInBase64,
    });
  } catch (error) {
    logger.warn('Decryption failed, using fallback mnemonic', { error });
    return user.mnemonic;
  }
};

This way getting rid of the let, it makes the code much more linear

public createFile = async (payload: StorageTypes.FileEntryByUuid): Promise<DriveFileItem> => {
const storageClient = SdkManager.instance.getStorage();
const driveFile = await storageClient.createFileEntryByUuid(payload);
let driveFile: StorageTypes.DriveFileData;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is another example:
you could split this function into 2:

public createFile = async (payload: StorageTypes.FileEntryByUuid): Promise<DriveFileItem> => {
  const driveFile = await this.createDriveFileEntry(payload);
  
  return {
    itemType: 'file',
    name: payload.plainName,
    id: driveFile.id,
    uuid: driveFile.uuid,
    size: driveFile.size,
    bucket: driveFile.bucket,
    createdAt: new Date(driveFile.createdAt),
    updatedAt: new Date(driveFile.updatedAt),
    fileId: driveFile.fileId,
    type: driveFile.type,
    status: driveFile.status as DriveFileItem['status'],
    folderId: driveFile.folderId,
    folderUuid: driveFile.folderUuid,
    creationTime: new Date(driveFile.creationTime ?? driveFile.createdAt),
    modificationTime: new Date(driveFile.modificationTime ?? driveFile.updatedAt),
  };
};

private async createDriveFileEntry(
  payload: StorageTypes.FileEntryByUuid
): Promise<StorageTypes.DriveFileData> {
  const currentWorkspace = await AuthService.instance.getCurrentWorkspace();
  
  if (currentWorkspace) {
    const workspaceClient = SdkManager.instance.getWorkspaces();
    return workspaceClient.createFileEntry(
      {
        name: payload.plainName,
        plainName: payload.plainName,
        bucket: payload.bucket,
        fileId: payload.fileId ?? '',
        encryptVersion: StorageTypes.EncryptionVersion.Aes03,
        folderUuid: payload.folderUuid,
        size: payload.size,
        type: payload.type ?? '',
        modificationTime: payload.modificationTime ?? new Date().toISOString(),
        date: payload.date ?? new Date().toISOString(),
      },
      currentWorkspace.workspaceCredentials.id,
    );
  }
  
  const storageClient = SdkManager.instance.getStorage();
  return storageClient.createFileEntryByUuid(payload);
}

This way, each function has one job only and we dont have uninitialized variables

kyberSecret = decapsulate.sharedSecret;
}

const decryptedMessage = await this.decryptMessageWithPrivateKey({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could make this method return a string instead of forcing the type using 'as', this can be delicate and have an easy solution

decryptionKeys: privateKey,
});

return decryptedMessage;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here you can use .toString() and then we can change the return type of the function to string, if necessary, right? since the return value of this function is only being used as string

static readonly instance = new LocalFilesystemService();

async scanLocalDirectory(path: string): Promise<ScanResult> {
public scanLocalDirectory = async (path: string): Promise<ScanResult> => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you change it to arrow function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed it to maintain consistency with other methods and classes that use arrow functions too, and also to automatically preserve the this context in case the method is passed as a callback or destructured. However, if you think it's not necessary in this case or if we should have a preference in the project for 'traditional' methods, we can always refactor all the methods, but for me is better this way

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants