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) => (