courrier multi account restore compose
This commit is contained in:
parent
3e6953a4a4
commit
6b37f0062d
114
lib/redis.ts
114
lib/redis.ts
@ -101,6 +101,7 @@ interface EmailCredentials {
|
|||||||
smtp_secure?: boolean;
|
smtp_secure?: boolean;
|
||||||
display_name?: string;
|
display_name?: string;
|
||||||
color?: string;
|
color?: string;
|
||||||
|
id?: string; // Add ID field to identify accounts
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ImapSessionData {
|
interface ImapSessionData {
|
||||||
@ -115,52 +116,67 @@ interface ImapSessionData {
|
|||||||
*/
|
*/
|
||||||
export async function cacheEmailCredentials(
|
export async function cacheEmailCredentials(
|
||||||
userId: string,
|
userId: string,
|
||||||
credentials: EmailCredentials
|
credentials: EmailCredentials | EmailCredentials[]
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const redis = getRedisClient();
|
const redis = getRedisClient();
|
||||||
const key = KEYS.CREDENTIALS(userId);
|
const key = KEYS.CREDENTIALS(userId);
|
||||||
|
|
||||||
// Validate credentials before caching
|
|
||||||
if (!credentials.email || !credentials.host || !credentials.password) {
|
|
||||||
console.error(`Cannot cache incomplete credentials for user ${userId}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log(`Caching credentials for user ${userId}`);
|
console.log(`Caching credentials for user ${userId}`);
|
||||||
|
|
||||||
|
// Handle both single account and array of accounts
|
||||||
|
const accountsToCache = Array.isArray(credentials) ? credentials : [credentials];
|
||||||
|
|
||||||
|
const secureAccounts = await Promise.all(accountsToCache.map(async (account) => {
|
||||||
|
// Validate credentials before caching
|
||||||
|
if (!account.email || !account.host || !account.password) {
|
||||||
|
console.error(`Cannot cache incomplete credentials for account ${account.email}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// Create a copy without the password to store
|
// Create a copy without the password to store
|
||||||
const secureCredentials: EmailCredentials = {
|
const secureCredentials: EmailCredentials = {
|
||||||
email: credentials.email,
|
id: account.id,
|
||||||
host: credentials.host,
|
email: account.email,
|
||||||
port: credentials.port,
|
host: account.host,
|
||||||
secure: credentials.secure ?? true,
|
port: account.port,
|
||||||
|
secure: account.secure ?? true,
|
||||||
// Include the extended fields
|
// Include the extended fields
|
||||||
...(credentials.smtp_host && { smtp_host: credentials.smtp_host }),
|
...(account.smtp_host && { smtp_host: account.smtp_host }),
|
||||||
...(credentials.smtp_port && { smtp_port: credentials.smtp_port }),
|
...(account.smtp_port && { smtp_port: account.smtp_port }),
|
||||||
...(credentials.smtp_secure !== undefined && { smtp_secure: credentials.smtp_secure }),
|
...(account.smtp_secure !== undefined && { smtp_secure: account.smtp_secure }),
|
||||||
...(credentials.display_name && { display_name: credentials.display_name }),
|
...(account.display_name && { display_name: account.display_name }),
|
||||||
...(credentials.color && { color: credentials.color })
|
...(account.color && { color: account.color })
|
||||||
};
|
};
|
||||||
|
|
||||||
// Encrypt password
|
// Encrypt password
|
||||||
if (credentials.password) {
|
if (account.password) {
|
||||||
try {
|
try {
|
||||||
const encrypted = encryptData(credentials.password);
|
const encrypted = encryptData(account.password);
|
||||||
console.log(`Successfully encrypted password for user ${userId}`);
|
console.log(`Successfully encrypted password for account ${account.email}`);
|
||||||
secureCredentials.encryptedPassword = encrypted;
|
secureCredentials.encryptedPassword = encrypted;
|
||||||
} catch (encryptError) {
|
} catch (encryptError) {
|
||||||
console.error(`Failed to encrypt password for user ${userId}:`, encryptError);
|
console.error(`Failed to encrypt password for account ${account.email}:`, encryptError);
|
||||||
// Don't proceed with caching if encryption fails
|
return null;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.warn(`No password provided for user ${userId}, skipping credential caching`);
|
console.warn(`No password provided for account ${account.email}, skipping credential caching`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return secureCredentials;
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Filter out any null values from failed encryption
|
||||||
|
const validAccounts = secureAccounts.filter(acc => acc !== null);
|
||||||
|
|
||||||
|
if (validAccounts.length === 0) {
|
||||||
|
console.warn(`No valid accounts to cache for user ${userId}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await redis.set(key, JSON.stringify(secureCredentials), 'EX', TTL.CREDENTIALS);
|
await redis.set(key, JSON.stringify(validAccounts), 'EX', TTL.CREDENTIALS);
|
||||||
console.log(`Credentials cached for user ${userId}`);
|
console.log(`Cached ${validAccounts.length} accounts for user ${userId}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error caching credentials for user ${userId}:`, error);
|
console.error(`Error caching credentials for user ${userId}:`, error);
|
||||||
}
|
}
|
||||||
@ -169,7 +185,7 @@ export async function cacheEmailCredentials(
|
|||||||
/**
|
/**
|
||||||
* Get email credentials from Redis
|
* Get email credentials from Redis
|
||||||
*/
|
*/
|
||||||
export async function getEmailCredentials(userId: string): Promise<EmailCredentials | null> {
|
export async function getEmailCredentials(userId: string): Promise<EmailCredentials[] | null> {
|
||||||
const redis = getRedisClient();
|
const redis = getRedisClient();
|
||||||
const key = KEYS.CREDENTIALS(userId);
|
const key = KEYS.CREDENTIALS(userId);
|
||||||
|
|
||||||
@ -180,37 +196,47 @@ export async function getEmailCredentials(userId: string): Promise<EmailCredenti
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const creds = JSON.parse(credStr) as EmailCredentials;
|
const creds = JSON.parse(credStr) as EmailCredentials[];
|
||||||
|
|
||||||
if (!creds.encryptedPassword) {
|
// Handle both single account (backward compatibility) and array of accounts
|
||||||
console.warn(`No encrypted password found for user ${userId}`);
|
const accounts = Array.isArray(creds) ? creds : [creds];
|
||||||
|
|
||||||
|
const decryptedAccounts = await Promise.all(accounts.map(async (account) => {
|
||||||
|
if (!account.encryptedPassword) {
|
||||||
|
console.warn(`No encrypted password found for account ${account.email}`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Decrypt the password
|
// Decrypt the password
|
||||||
const password = decryptData(creds.encryptedPassword);
|
const password = decryptData(account.encryptedPassword);
|
||||||
|
|
||||||
// Return the full credentials with decrypted password
|
// Return the full credentials with decrypted password
|
||||||
return {
|
return {
|
||||||
email: creds.email,
|
id: account.id,
|
||||||
|
email: account.email,
|
||||||
password,
|
password,
|
||||||
host: creds.host,
|
host: account.host,
|
||||||
port: creds.port,
|
port: account.port,
|
||||||
secure: creds.secure ?? true,
|
secure: account.secure ?? true,
|
||||||
// Include the extended fields if they exist in the cache
|
smtp_host: account.smtp_host,
|
||||||
...(creds.smtp_host && { smtp_host: creds.smtp_host }),
|
smtp_port: account.smtp_port,
|
||||||
...(creds.smtp_port && { smtp_port: creds.smtp_port }),
|
smtp_secure: account.smtp_secure,
|
||||||
...(creds.smtp_secure !== undefined && { smtp_secure: creds.smtp_secure }),
|
display_name: account.display_name,
|
||||||
...(creds.display_name && { display_name: creds.display_name }),
|
color: account.color
|
||||||
...(creds.color && { color: creds.color })
|
|
||||||
};
|
};
|
||||||
} catch (decryptError) {
|
} catch (error) {
|
||||||
console.error(`Failed to decrypt password for user ${userId}:`, decryptError);
|
console.error(`Failed to decrypt password for account ${account.email}:`, error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Filter out any null values from failed decryption
|
||||||
|
const validAccounts = decryptedAccounts.filter(acc => acc !== null);
|
||||||
|
|
||||||
|
return validAccounts.length > 0 ? validAccounts : null;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error retrieving credentials for user ${userId}:`, error);
|
console.error(`Error getting credentials for user ${userId}:`, error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -417,6 +443,6 @@ export async function invalidateUserEmailCache(
|
|||||||
*/
|
*/
|
||||||
export async function getCachedEmailCredentials(
|
export async function getCachedEmailCredentials(
|
||||||
userId: string
|
userId: string
|
||||||
): Promise<EmailCredentials | null> {
|
): Promise<EmailCredentials[] | null> {
|
||||||
return getEmailCredentials(userId);
|
return getEmailCredentials(userId);
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user