From 25e37cc41545fafc921ae9c832fe027ec489d1ab Mon Sep 17 00:00:00 2001 From: Jose Olarte III Date: Mon, 1 Sep 2025 19:36:01 +0800 Subject: [PATCH] refactor: consolidate duplicate account checking logic into unified utility - Extract checkForDuplicateAccount methods from ImportAccountView and ImportDerivedAccountView - Create unified utility function in src/libs/util.ts with TypeScript overloads - Support both direct DID checking and mnemonic+derivation path checking - Improve error handling with centralized logging via PlatformServiceFactory - Add comprehensive JSDoc documentation for both function overloads - Remove unused imports (deriveAddress, newIdentifier) from ImportAccountView The utility function now provides a clean API: - checkForDuplicateAccount(did) - for direct DID checking - checkForDuplicateAccount(mnemonic, derivationPath) - for derivation + checking Both components maintain identical functionality while using centralized logic. --- src/libs/util.ts | 66 ++++++++++++++++++++++++++ src/views/ImportAccountView.vue | 53 ++++----------------- src/views/ImportDerivedAccountView.vue | 25 +--------- 3 files changed, 77 insertions(+), 67 deletions(-) diff --git a/src/libs/util.ts b/src/libs/util.ts index 83004078..29e8dd82 100644 --- a/src/libs/util.ts +++ b/src/libs/util.ts @@ -1042,3 +1042,69 @@ export async function importFromMnemonic( } } } + +/** + * Checks if an account with the given DID already exists in the database + * + * @param did - The DID to check for duplicates + * @returns Promise - True if account already exists, false otherwise + * @throws Error if database query fails + */ +export async function checkForDuplicateAccount(did: string): Promise; + +/** + * Checks if an account with the given DID already exists in the database + * + * @param mnemonic - The mnemonic phrase to derive DID from + * @param derivationPath - The derivation path to use + * @returns Promise - True if account already exists, false otherwise + * @throws Error if database query fails + */ +export async function checkForDuplicateAccount( + mnemonic: string, + derivationPath: string, +): Promise; + +/** + * Implementation of checkForDuplicateAccount with overloaded signatures + */ +export async function checkForDuplicateAccount( + didOrMnemonic: string, + derivationPath?: string, +): Promise { + try { + let didToCheck: string; + + if (derivationPath) { + // Derive the DID from mnemonic and derivation path + const [address, privateHex, publicHex] = deriveAddress( + didOrMnemonic.trim().toLowerCase(), + derivationPath, + ); + + const newId = newIdentifier( + address, + privateHex, + publicHex, + derivationPath, + ); + didToCheck = newId.did; + } else { + // Use the provided DID directly + didToCheck = didOrMnemonic; + } + + // Check if an account with this DID already exists + const platformService = await getPlatformService(); + const existingAccount = await platformService.dbQuery( + "SELECT did FROM accounts WHERE did = ?", + [didToCheck], + ); + + return (existingAccount?.values?.length ?? 0) > 0; + } catch (error) { + // If we can't check for duplicates, let the calling process handle the error + logger.error("Error checking for duplicate account:", error); + throw error; + } +} diff --git a/src/views/ImportAccountView.vue b/src/views/ImportAccountView.vue index 4b0c006b..e76ec768 100644 --- a/src/views/ImportAccountView.vue +++ b/src/views/ImportAccountView.vue @@ -87,12 +87,12 @@ import { Component, Vue } from "vue-facing-decorator"; import { Router } from "vue-router"; import { AppString, NotificationIface } from "../constants/app"; +import { DEFAULT_ROOT_DERIVATION_PATH } from "../libs/crypto"; import { - DEFAULT_ROOT_DERIVATION_PATH, - deriveAddress, - newIdentifier, -} from "../libs/crypto"; -import { retrieveAccountCount, importFromMnemonic } from "../libs/util"; + retrieveAccountCount, + importFromMnemonic, + checkForDuplicateAccount, +} from "../libs/util"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify"; import { NOTIFY_DUPLICATE_ACCOUNT_IMPORT } from "@/constants/notifications"; @@ -204,7 +204,10 @@ export default class ImportAccountView extends Vue { try { // Check for duplicate account before importing - const isDuplicate = await this.checkForDuplicateAccount(); + const isDuplicate = await checkForDuplicateAccount( + this.mnemonic, + this.derivationPath, + ); if (isDuplicate) { this.notify.warning( NOTIFY_DUPLICATE_ACCOUNT_IMPORT.message, @@ -259,43 +262,5 @@ export default class ImportAccountView extends Vue { ); } } - - /** - * Checks if the account to be imported already exists - * - * Derives the DID from the mnemonic and checks if it exists in the database - * @returns Promise - True if account already exists, false otherwise - */ - private async checkForDuplicateAccount(): Promise { - try { - // Derive the address and create the identifier to get the DID - const [address, privateHex, publicHex] = deriveAddress( - this.mnemonic.trim().toLowerCase(), - this.derivationPath, - ); - - const newId = newIdentifier( - address, - publicHex, - privateHex, - this.derivationPath, - ); - const didToCheck = newId.did; - - // Check if an account with this DID already exists - const existingAccount = await this.$query( - "SELECT did FROM accounts WHERE did = ?", - [didToCheck], - ); - - return existingAccount?.values?.length > 0; - } catch (error) { - // If we can't check for duplicates (e.g., invalid mnemonic), - // let the import process handle the error - this.$logError("Error checking for duplicate account: " + error); - // Return false to let the import process continue and handle the error - return false; - } - } } diff --git a/src/views/ImportDerivedAccountView.vue b/src/views/ImportDerivedAccountView.vue index 44ef37d2..2772cbe8 100644 --- a/src/views/ImportDerivedAccountView.vue +++ b/src/views/ImportDerivedAccountView.vue @@ -83,6 +83,7 @@ import { retrieveAllAccountsMetadata, retrieveFullyDecryptedAccount, saveNewIdentity, + checkForDuplicateAccount, } from "../libs/util"; import { logger } from "../utils/logger"; import { Account, AccountEncrypted } from "../db/tables/accounts"; @@ -172,7 +173,7 @@ export default class ImportAccountView extends Vue { try { // Check for duplicate account before creating - const isDuplicate = await this.checkForDuplicateAccount(newId.did); + const isDuplicate = await checkForDuplicateAccount(newId.did); if (isDuplicate) { this.notify.warning( "This derived account already exists. Please try a different derivation path.", @@ -202,27 +203,5 @@ export default class ImportAccountView extends Vue { this.notify.error(NOTIFY_ACCOUNT_DERIVATION_ERROR.message, TIMEOUTS.LONG); } } - - /** - * Checks if the account to be created already exists - * - * @param did - The DID to check for duplicates - * @returns Promise - True if account already exists, false otherwise - */ - private async checkForDuplicateAccount(did: string): Promise { - try { - // Check if an account with this DID already exists - const existingAccount = await this.$query( - "SELECT did FROM accounts WHERE did = ?", - [did], - ); - - return existingAccount?.values?.length > 0; - } catch (error) { - // If we can't check for duplicates, let the save process handle the error - this.$logError("Error checking for duplicate account: " + error); - return false; - } - } }