From 08912772ee0f8a007bd293d11a42a7920d8f4534 Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Mon, 15 Dec 2025 12:16:12 +0100 Subject: [PATCH] Add storage utility for fallback purposes, refactor EmailDomainField public email domain usage (#1404) --- .../background/VaultMessageHandler.ts | 21 +++----- .../popup/components/EmailPreview.tsx | 7 ++- .../components/Forms/EmailDomainField.tsx | 40 +++++--------- .../entrypoints/popup/context/DbContext.tsx | 12 ++--- .../src/utils/SqliteClient.ts | 10 ++-- .../src/utils/StorageUtility.ts | 53 +++++++++++++++++++ 6 files changed, 89 insertions(+), 54 deletions(-) create mode 100644 apps/browser-extension/src/utils/StorageUtility.ts diff --git a/apps/browser-extension/src/entrypoints/background/VaultMessageHandler.ts b/apps/browser-extension/src/entrypoints/background/VaultMessageHandler.ts index 8cee51420..649e325d6 100644 --- a/apps/browser-extension/src/entrypoints/background/VaultMessageHandler.ts +++ b/apps/browser-extension/src/entrypoints/background/VaultMessageHandler.ts @@ -5,6 +5,7 @@ import type { EncryptionKeyDerivationParams } from '@/utils/dist/core/models/met import type { Vault, VaultResponse, VaultPostResponse } from '@/utils/dist/core/models/webapi'; import { EncryptionUtility } from '@/utils/EncryptionUtility'; import { SqliteClient } from '@/utils/SqliteClient'; +import { getItemWithFallback } from '@/utils/StorageUtility'; import { VaultVersionIncompatibleError } from '@/utils/types/errors/VaultVersionIncompatibleError'; import { BoolResponse as messageBoolResponse } from '@/utils/types/messaging/BoolResponse'; import { CredentialsResponse as messageCredentialsResponse } from '@/utils/types/messaging/CredentialsResponse'; @@ -201,9 +202,10 @@ export async function handleGetVault( // Read from local: storage for persistent vault access const encryptedVault = await storage.getItem('local:encryptedVault') as string; - const publicEmailDomains = await storage.getItem('local:publicEmailDomains') as string[]; - const privateEmailDomains = await storage.getItem('local:privateEmailDomains') as string[]; - const hiddenPrivateEmailDomains = await storage.getItem('local:hiddenPrivateEmailDomains') as string[] ?? []; + // Use fallback for keys migrated from session: to local: in v0.26.0 + const publicEmailDomains = await getItemWithFallback('local:publicEmailDomains'); + const privateEmailDomains = await getItemWithFallback('local:privateEmailDomains'); + const hiddenPrivateEmailDomains = await getItemWithFallback('local:hiddenPrivateEmailDomains') ?? []; const serverRevision = await storage.getItem('local:serverRevision') as number | null; if (!encryptedVault) { @@ -433,7 +435,7 @@ export async function getEmailAddressesForVault( const credentials = sqliteClient.getAllCredentials(); // Get metadata from local: storage - const privateEmailDomains = await storage.getItem('local:privateEmailDomains') as string[]; + const privateEmailDomains = await getItemWithFallback('local:privateEmailDomains') ?? []; const emailAddresses = credentials .filter(cred => cred.Alias?.Email != null) @@ -526,15 +528,8 @@ export async function handleGetEncryptionKey( */ export async function handleGetEncryptionKeyDerivationParams( ) : Promise { - // Try local: storage first (current location since offline support) - let params = await storage.getItem('local:encryptionKeyDerivationParams') as EncryptionKeyDerivationParams | null; - - // Fall back to session: storage for backwards compatibility - if (!params) { - params = await storage.getItem('session:encryptionKeyDerivationParams') as EncryptionKeyDerivationParams | null; - } - - return params; + // Get metadata from storage + return await getItemWithFallback('local:encryptionKeyDerivationParams'); } /** diff --git a/apps/browser-extension/src/entrypoints/popup/components/EmailPreview.tsx b/apps/browser-extension/src/entrypoints/popup/components/EmailPreview.tsx index faaa18ba6..52f46ff32 100644 --- a/apps/browser-extension/src/entrypoints/popup/components/EmailPreview.tsx +++ b/apps/browser-extension/src/entrypoints/popup/components/EmailPreview.tsx @@ -8,8 +8,7 @@ import { useWebApi } from '@/entrypoints/popup/context/WebApiContext'; import { AppInfo } from '@/utils/AppInfo'; import type { ApiErrorResponse, MailboxEmail } from '@/utils/dist/core/models/webapi'; import { EncryptionUtility } from '@/utils/EncryptionUtility'; - -import { storage } from '#imports'; +import { getItemWithFallback } from '@/utils/StorageUtility'; type EmailPreviewProps = { email: string; @@ -56,7 +55,7 @@ export const EmailPreview: React.FC = ({ email }) => { */ const isPublicDomain = async (emailAddress: string): Promise => { // Get metadata from storage - const publicEmailDomains = await storage.getItem('local:publicEmailDomains') as string[] ?? []; + const publicEmailDomains = await getItemWithFallback('local:publicEmailDomains') ?? []; return publicEmailDomains.some(domain => emailAddress.toLowerCase().endsWith(`@${domain.toLowerCase()}`)); }; @@ -65,7 +64,7 @@ export const EmailPreview: React.FC = ({ email }) => { */ const isPrivateDomain = async (emailAddress: string): Promise => { // Get metadata from storage - const privateEmailDomains = await storage.getItem('local:privateEmailDomains') as string[] ?? []; + const privateEmailDomains = await getItemWithFallback('local:privateEmailDomains') ?? []; return privateEmailDomains.some(domain => emailAddress.toLowerCase().endsWith(`@${domain.toLowerCase()}`)); }; diff --git a/apps/browser-extension/src/entrypoints/popup/components/Forms/EmailDomainField.tsx b/apps/browser-extension/src/entrypoints/popup/components/Forms/EmailDomainField.tsx index d7c187279..8da549038 100644 --- a/apps/browser-extension/src/entrypoints/popup/components/Forms/EmailDomainField.tsx +++ b/apps/browser-extension/src/entrypoints/popup/components/Forms/EmailDomainField.tsx @@ -26,20 +26,6 @@ type EmailDomainFieldProps = { onGenerateAlias?: () => void; } -// Hardcoded public email domains (same as in AliasVault.Client) -const PUBLIC_EMAIL_DOMAINS = [ - 'spamok.com', - 'solarflarecorp.com', - 'spamok.nl', - '3060.nl', - 'landmail.nl', - 'asdasd.nl', - 'spamok.de', - 'spamok.com.ua', - 'spamok.es', - 'spamok.fr', -]; - /** * Email domain field component with domain chooser functionality. * Allows users to select from private/public domains or enter custom email addresses. @@ -61,17 +47,19 @@ const EmailDomainField: React.FC = ({ const [localPart, setLocalPart] = useState(''); const [selectedDomain, setSelectedDomain] = useState(''); const [isPopupVisible, setIsPopupVisible] = useState(false); + const [publicEmailDomains, setPublicEmailDomains] = useState([]); const [privateEmailDomains, setPrivateEmailDomains] = useState([]); const [hiddenPrivateEmailDomains, setHiddenPrivateEmailDomains] = useState([]); const popupRef = useRef(null); - // Get private email domains from vault metadata + // Get email domains from vault metadata useEffect(() => { /** - * Load private email domains from vault metadata, excluding hidden ones. + * Load email domains from vault metadata. */ const loadDomains = async (): Promise => { const metadata = await dbContext.getVaultMetadata(); + setPublicEmailDomains(metadata?.publicEmailDomains ?? []); setPrivateEmailDomains(metadata?.privateEmailDomains ?? []); setHiddenPrivateEmailDomains(metadata?.hiddenPrivateEmailDomains ?? []); }; @@ -93,8 +81,8 @@ const EmailDomainField: React.FC = ({ if (!selectedDomain) { if (showPrivateDomains && privateEmailDomains[0]) { setSelectedDomain(privateEmailDomains[0]); - } else if (PUBLIC_EMAIL_DOMAINS[0]) { - setSelectedDomain(PUBLIC_EMAIL_DOMAINS[0]); + } else if (publicEmailDomains[0]) { + setSelectedDomain(publicEmailDomains[0]); } } return; @@ -106,7 +94,7 @@ const EmailDomainField: React.FC = ({ setSelectedDomain(domain); // Check if it's a known domain (public, private, or hidden private) - const isKnownDomain = PUBLIC_EMAIL_DOMAINS.includes(domain) || + const isKnownDomain = publicEmailDomains.includes(domain) || privateEmailDomains.includes(domain) || hiddenPrivateEmailDomains.includes(domain); // Switch to domain chooser mode if domain is recognized @@ -119,8 +107,8 @@ const EmailDomainField: React.FC = ({ if (!selectedDomain && !value.includes('@')) { if (showPrivateDomains && privateEmailDomains[0]) { setSelectedDomain(privateEmailDomains[0]); - } else if (PUBLIC_EMAIL_DOMAINS[0]) { - setSelectedDomain(PUBLIC_EMAIL_DOMAINS[0]); + } else if (publicEmailDomains[0]) { + setSelectedDomain(publicEmailDomains[0]); } } } @@ -142,7 +130,7 @@ const EmailDomainField: React.FC = ({ } // Check if the domain is now recognized after private domains loaded - const isKnownDomain = PUBLIC_EMAIL_DOMAINS.includes(domain) || + const isKnownDomain = publicEmailDomains.includes(domain) || privateEmailDomains.includes(domain) || hiddenPrivateEmailDomains.includes(domain); @@ -150,7 +138,7 @@ const EmailDomainField: React.FC = ({ if (isKnownDomain && isCustomDomain) { setIsCustomDomain(false); } - }, [privateEmailDomains, hiddenPrivateEmailDomains, value, isCustomDomain]); + }, [publicEmailDomains, privateEmailDomains, hiddenPrivateEmailDomains, value, isCustomDomain]); // Handle local part changes const handleLocalPartChange = useCallback((e: React.ChangeEvent) => { @@ -217,7 +205,7 @@ const EmailDomainField: React.FC = ({ // Switching to domain chooser mode const defaultDomain = showPrivateDomains && privateEmailDomains[0] ? privateEmailDomains[0] - : PUBLIC_EMAIL_DOMAINS[0]; + : publicEmailDomains[0]; setSelectedDomain(defaultDomain); // Only add domain if we have a local part @@ -228,7 +216,7 @@ const EmailDomainField: React.FC = ({ onChange(`${value}@${defaultDomain}`); } } - }, [isCustomDomain, value, localPart, showPrivateDomains, privateEmailDomains, onChange, defaultToFreeText]); + }, [isCustomDomain, value, localPart, showPrivateDomains, publicEmailDomains, privateEmailDomains, onChange, defaultToFreeText]); // Handle clicks outside the popup useEffect(() => { @@ -386,7 +374,7 @@ const EmailDomainField: React.FC = ({ {t('credentials.publicEmailDescription')}

- {PUBLIC_EMAIL_DOMAINS.map((domain) => ( + {publicEmailDomains.map((domain) => (