mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-05-24 16:32:20 -04:00
Add storage utility for fallback purposes, refactor EmailDomainField public email domain usage (#1404)
This commit is contained in:
@@ -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<string[]>('local:publicEmailDomains');
|
||||
const privateEmailDomains = await getItemWithFallback<string[]>('local:privateEmailDomains');
|
||||
const hiddenPrivateEmailDomains = await getItemWithFallback<string[]>('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<string[]>('local:privateEmailDomains') ?? [];
|
||||
|
||||
const emailAddresses = credentials
|
||||
.filter(cred => cred.Alias?.Email != null)
|
||||
@@ -526,15 +528,8 @@ export async function handleGetEncryptionKey(
|
||||
*/
|
||||
export async function handleGetEncryptionKeyDerivationParams(
|
||||
) : Promise<EncryptionKeyDerivationParams | null> {
|
||||
// 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<EncryptionKeyDerivationParams>('local:encryptionKeyDerivationParams');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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<EmailPreviewProps> = ({ email }) => {
|
||||
*/
|
||||
const isPublicDomain = async (emailAddress: string): Promise<boolean> => {
|
||||
// Get metadata from storage
|
||||
const publicEmailDomains = await storage.getItem('local:publicEmailDomains') as string[] ?? [];
|
||||
const publicEmailDomains = await getItemWithFallback<string[]>('local:publicEmailDomains') ?? [];
|
||||
return publicEmailDomains.some(domain => emailAddress.toLowerCase().endsWith(`@${domain.toLowerCase()}`));
|
||||
};
|
||||
|
||||
@@ -65,7 +64,7 @@ export const EmailPreview: React.FC<EmailPreviewProps> = ({ email }) => {
|
||||
*/
|
||||
const isPrivateDomain = async (emailAddress: string): Promise<boolean> => {
|
||||
// Get metadata from storage
|
||||
const privateEmailDomains = await storage.getItem('local:privateEmailDomains') as string[] ?? [];
|
||||
const privateEmailDomains = await getItemWithFallback<string[]>('local:privateEmailDomains') ?? [];
|
||||
return privateEmailDomains.some(domain => emailAddress.toLowerCase().endsWith(`@${domain.toLowerCase()}`));
|
||||
};
|
||||
|
||||
|
||||
@@ -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<EmailDomainFieldProps> = ({
|
||||
const [localPart, setLocalPart] = useState('');
|
||||
const [selectedDomain, setSelectedDomain] = useState('');
|
||||
const [isPopupVisible, setIsPopupVisible] = useState(false);
|
||||
const [publicEmailDomains, setPublicEmailDomains] = useState<string[]>([]);
|
||||
const [privateEmailDomains, setPrivateEmailDomains] = useState<string[]>([]);
|
||||
const [hiddenPrivateEmailDomains, setHiddenPrivateEmailDomains] = useState<string[]>([]);
|
||||
const popupRef = useRef<HTMLDivElement>(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<void> => {
|
||||
const metadata = await dbContext.getVaultMetadata();
|
||||
setPublicEmailDomains(metadata?.publicEmailDomains ?? []);
|
||||
setPrivateEmailDomains(metadata?.privateEmailDomains ?? []);
|
||||
setHiddenPrivateEmailDomains(metadata?.hiddenPrivateEmailDomains ?? []);
|
||||
};
|
||||
@@ -93,8 +81,8 @@ const EmailDomainField: React.FC<EmailDomainFieldProps> = ({
|
||||
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<EmailDomainFieldProps> = ({
|
||||
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<EmailDomainFieldProps> = ({
|
||||
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<EmailDomainFieldProps> = ({
|
||||
}
|
||||
|
||||
// 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<EmailDomainFieldProps> = ({
|
||||
if (isKnownDomain && isCustomDomain) {
|
||||
setIsCustomDomain(false);
|
||||
}
|
||||
}, [privateEmailDomains, hiddenPrivateEmailDomains, value, isCustomDomain]);
|
||||
}, [publicEmailDomains, privateEmailDomains, hiddenPrivateEmailDomains, value, isCustomDomain]);
|
||||
|
||||
// Handle local part changes
|
||||
const handleLocalPartChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
@@ -217,7 +205,7 @@ const EmailDomainField: React.FC<EmailDomainFieldProps> = ({
|
||||
// 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<EmailDomainFieldProps> = ({
|
||||
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<EmailDomainFieldProps> = ({
|
||||
{t('credentials.publicEmailDescription')}
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{PUBLIC_EMAIL_DOMAINS.map((domain) => (
|
||||
{publicEmailDomains.map((domain) => (
|
||||
<button
|
||||
key={domain}
|
||||
type="button"
|
||||
|
||||
@@ -3,6 +3,7 @@ import { sendMessage } from 'webext-bridge/popup';
|
||||
|
||||
import type { EncryptionKeyDerivationParams } from '@/utils/dist/core/models/metadata';
|
||||
import SqliteClient from '@/utils/SqliteClient';
|
||||
import { getItemWithFallback } from '@/utils/StorageUtility';
|
||||
import type { VaultResponse as messageVaultResponse } from '@/utils/types/messaging/VaultResponse';
|
||||
|
||||
import { storage } from '#imports';
|
||||
@@ -175,12 +176,11 @@ export const DbProvider: React.FC<{ children: React.ReactNode }> = ({ children }
|
||||
*/
|
||||
const getVaultMetadata = useCallback(async () : Promise<VaultMetadata | null> => {
|
||||
try {
|
||||
const [publicEmailDomains, privateEmailDomains, hiddenPrivateEmailDomains, revision] = await Promise.all([
|
||||
storage.getItem('local:publicEmailDomains') as Promise<string[] | null>,
|
||||
storage.getItem('local:privateEmailDomains') as Promise<string[] | null>,
|
||||
storage.getItem('local:hiddenPrivateEmailDomains') as Promise<string[] | null>,
|
||||
storage.getItem('local:serverRevision') as Promise<number | null>
|
||||
]);
|
||||
// Use fallback for keys migrated from session: to local: in v0.26.0
|
||||
const publicEmailDomains = await getItemWithFallback<string[]>('local:publicEmailDomains');
|
||||
const privateEmailDomains = await getItemWithFallback<string[]>('local:privateEmailDomains');
|
||||
const hiddenPrivateEmailDomains = await getItemWithFallback<string[]>('local:hiddenPrivateEmailDomains');
|
||||
const revision = await storage.getItem('local:serverRevision') as number | null;
|
||||
|
||||
if (!publicEmailDomains && !privateEmailDomains) {
|
||||
return null;
|
||||
|
||||
@@ -6,12 +6,11 @@ import type { Attachment } from '@/utils/dist/core/models/vault';
|
||||
import { FieldKey, FieldTypes, getSystemField, MAX_FIELD_HISTORY_RECORDS } from '@/utils/dist/core/models/vault';
|
||||
import type { VaultVersion } from '@/utils/dist/core/vault';
|
||||
import { VaultSqlGenerator, checkVersionCompatibility, extractVersionFromMigrationId } from '@/utils/dist/core/vault';
|
||||
import { getItemWithFallback } from '@/utils/StorageUtility';
|
||||
import { VaultVersionIncompatibleError } from '@/utils/types/errors/VaultVersionIncompatibleError';
|
||||
|
||||
import { t } from '@/i18n/StandaloneI18n';
|
||||
|
||||
import { storage } from '#imports';
|
||||
|
||||
/**
|
||||
* Placeholder base64 image for credentials without a logo.
|
||||
*/
|
||||
@@ -756,9 +755,10 @@ export class SqliteClient {
|
||||
* @returns The default email domain or null if no valid domain is found
|
||||
*/
|
||||
public async getDefaultEmailDomain(): Promise<string | null> {
|
||||
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<string[]>('local:publicEmailDomains') ?? [];
|
||||
const privateEmailDomains = await getItemWithFallback<string[]>('local:privateEmailDomains') ?? [];
|
||||
const hiddenPrivateEmailDomains = await getItemWithFallback<string[]>('local:hiddenPrivateEmailDomains') ?? [];
|
||||
|
||||
const defaultEmailDomain = this.getSetting('DefaultEmailDomain');
|
||||
|
||||
|
||||
53
apps/browser-extension/src/utils/StorageUtility.ts
Normal file
53
apps/browser-extension/src/utils/StorageUtility.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { storage } from 'wxt/utils/storage';
|
||||
|
||||
type StorageKey = `local:${string}` | `session:${string}`;
|
||||
|
||||
/**
|
||||
* Storage keys that were migrated from session: to local: storage in v0.26.0 for offline mode support.
|
||||
* This mapping enables backward compatibility for users upgrading from older versions where data
|
||||
* was stored in session: storage. The fallback can be removed in v0.27.0+.
|
||||
*
|
||||
* Format: local key -> session fallback key
|
||||
*/
|
||||
const MIGRATED_STORAGE_KEYS: Record<string, StorageKey> = {
|
||||
'local:publicEmailDomains': 'session:publicEmailDomains',
|
||||
'local:privateEmailDomains': 'session:privateEmailDomains',
|
||||
'local:hiddenPrivateEmailDomains': 'session:hiddenPrivateEmailDomains',
|
||||
'local:encryptionKeyDerivationParams': 'session:encryptionKeyDerivationParams',
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a storage item with fallback to the legacy session: storage location.
|
||||
* This is used for keys that were migrated from session: to local: storage in v0.26.0.
|
||||
*
|
||||
* @param key The local: storage key to retrieve
|
||||
* @returns The value from local: storage, or from session: storage as fallback, or null if not found
|
||||
*
|
||||
* @example
|
||||
* // Instead of:
|
||||
* const domains = await storage.getItem('local:publicEmailDomains');
|
||||
*
|
||||
* // Use:
|
||||
* const domains = await getItemWithFallback('local:publicEmailDomains');
|
||||
*
|
||||
* @note This fallback can be removed in v0.27.0+ after users have had time to upgrade
|
||||
*/
|
||||
export async function getItemWithFallback<T>(key: StorageKey): Promise<T | null> {
|
||||
// Try the current (local:) key first
|
||||
let value = await storage.getItem(key) as T | null;
|
||||
|
||||
// If not found and this is a migrated key, try the fallback
|
||||
if (value === null && key in MIGRATED_STORAGE_KEYS) {
|
||||
const fallbackKey = MIGRATED_STORAGE_KEYS[key];
|
||||
value = await storage.getItem(fallbackKey) as T | null;
|
||||
|
||||
// If found in fallback, migrate to new location for future use
|
||||
if (value !== null) {
|
||||
await storage.setItem(key, value);
|
||||
// Remove the fallback key
|
||||
await storage.removeItem(fallbackKey);
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
Reference in New Issue
Block a user