235 lines
6.4 KiB
JavaScript
235 lines
6.4 KiB
JavaScript
import { db } from '../config/database.js';
|
|
import { emails, contacts } from '../db/schema.js';
|
|
import { eq, and, or, desc, like, sql } from 'drizzle-orm';
|
|
import { NotFoundError } from '../utils/errors.js';
|
|
|
|
/**
|
|
* Get all emails for a user (only from added contacts)
|
|
* If emailAccountId is provided, filter by that account
|
|
*/
|
|
export const getUserEmails = async (userId, emailAccountId = null) => {
|
|
const conditions = [eq(emails.userId, userId)];
|
|
|
|
if (emailAccountId) {
|
|
conditions.push(eq(emails.emailAccountId, emailAccountId));
|
|
}
|
|
|
|
const userEmails = await db
|
|
.select({
|
|
id: emails.id,
|
|
jmapId: emails.jmapId,
|
|
messageId: emails.messageId,
|
|
threadId: emails.threadId,
|
|
inReplyTo: emails.inReplyTo,
|
|
from: emails.from,
|
|
to: emails.to,
|
|
subject: emails.subject,
|
|
body: emails.body,
|
|
isRead: emails.isRead,
|
|
date: emails.date,
|
|
createdAt: emails.createdAt,
|
|
emailAccountId: emails.emailAccountId,
|
|
contact: {
|
|
id: contacts.id,
|
|
email: contacts.email,
|
|
name: contacts.name,
|
|
},
|
|
})
|
|
.from(emails)
|
|
.leftJoin(contacts, eq(emails.contactId, contacts.id))
|
|
.where(and(...conditions))
|
|
.orderBy(desc(emails.date));
|
|
|
|
return userEmails;
|
|
};
|
|
|
|
/**
|
|
* Get emails by thread ID
|
|
*/
|
|
export const getEmailThread = async (userId, threadId) => {
|
|
const thread = await db
|
|
.select()
|
|
.from(emails)
|
|
.where(and(eq(emails.userId, userId), eq(emails.threadId, threadId)))
|
|
.orderBy(emails.date);
|
|
|
|
if (thread.length === 0) {
|
|
throw new NotFoundError('Thread nenájdený');
|
|
}
|
|
|
|
return thread;
|
|
};
|
|
|
|
/**
|
|
* Search emails (from, to, subject)
|
|
* If emailAccountId is provided, filter by that account
|
|
*/
|
|
export const searchEmails = async (userId, query, emailAccountId = null) => {
|
|
if (!query || query.trim().length < 2) {
|
|
throw new Error('Search term must be at least 2 characters');
|
|
}
|
|
|
|
const searchPattern = `%${query}%`;
|
|
const conditions = [
|
|
eq(emails.userId, userId),
|
|
or(
|
|
like(emails.from, searchPattern),
|
|
like(emails.to, searchPattern),
|
|
like(emails.subject, searchPattern)
|
|
),
|
|
];
|
|
|
|
if (emailAccountId) {
|
|
conditions.push(eq(emails.emailAccountId, emailAccountId));
|
|
}
|
|
|
|
const results = await db
|
|
.select()
|
|
.from(emails)
|
|
.where(and(...conditions))
|
|
.orderBy(desc(emails.date))
|
|
.limit(50);
|
|
|
|
return results;
|
|
};
|
|
|
|
/**
|
|
* Get unread email count
|
|
* Returns total count and counts per email account
|
|
*/
|
|
export const getUnreadCount = async (userId) => {
|
|
// Get total unread count
|
|
const totalResult = await db
|
|
.select({ count: sql`count(*)::int` })
|
|
.from(emails)
|
|
.where(and(eq(emails.userId, userId), eq(emails.isRead, false)));
|
|
|
|
const totalUnread = totalResult[0]?.count || 0;
|
|
|
|
// Get unread count per email account
|
|
const accountCounts = await db
|
|
.select({
|
|
emailAccountId: emails.emailAccountId,
|
|
count: sql`count(*)::int`,
|
|
})
|
|
.from(emails)
|
|
.where(and(eq(emails.userId, userId), eq(emails.isRead, false)))
|
|
.groupBy(emails.emailAccountId);
|
|
|
|
return {
|
|
totalUnread,
|
|
accounts: accountCounts.map((ac) => ({
|
|
emailAccountId: ac.emailAccountId,
|
|
unreadCount: ac.count,
|
|
})),
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Mark thread as read
|
|
*/
|
|
export const markThreadAsRead = async (userId, threadId) => {
|
|
console.log('🟦 markThreadAsRead called:', { userId, threadId });
|
|
|
|
const result = await db
|
|
.update(emails)
|
|
.set({ isRead: true, updatedAt: new Date() })
|
|
.where(and(eq(emails.userId, userId), eq(emails.threadId, threadId), eq(emails.isRead, false)))
|
|
.returning();
|
|
|
|
console.log('✅ markThreadAsRead result:', { count: result.length, threadId });
|
|
|
|
return { success: true, count: result.length };
|
|
};
|
|
|
|
/**
|
|
* Mark all emails from a contact as read
|
|
*/
|
|
export const markContactEmailsAsRead = async (userId, contactId) => {
|
|
console.log('🟦 markContactEmailsAsRead called:', { userId, contactId });
|
|
|
|
// Get the contact info first
|
|
const [contact] = await db
|
|
.select()
|
|
.from(contacts)
|
|
.where(eq(contacts.id, contactId))
|
|
.limit(1);
|
|
|
|
console.log('👤 Contact info:', {
|
|
id: contact?.id,
|
|
email: contact?.email,
|
|
name: contact?.name,
|
|
});
|
|
|
|
// First, check what emails exist for this contact (including already read ones)
|
|
const allContactEmails = await db
|
|
.select({
|
|
id: emails.id,
|
|
contactId: emails.contactId,
|
|
isRead: emails.isRead,
|
|
from: emails.from,
|
|
subject: emails.subject,
|
|
})
|
|
.from(emails)
|
|
.where(and(eq(emails.userId, userId), eq(emails.contactId, contactId)));
|
|
|
|
console.log('📧 All emails for this contact (by contactId):', {
|
|
total: allContactEmails.length,
|
|
unread: allContactEmails.filter(e => !e.isRead).length,
|
|
read: allContactEmails.filter(e => e.isRead).length,
|
|
sampleEmails: allContactEmails.slice(0, 3),
|
|
});
|
|
|
|
// Check if there are emails from this sender but with NULL or different contactId
|
|
if (contact) {
|
|
const emailsFromSender = await db
|
|
.select({
|
|
id: emails.id,
|
|
contactId: emails.contactId,
|
|
isRead: emails.isRead,
|
|
from: emails.from,
|
|
subject: emails.subject,
|
|
})
|
|
.from(emails)
|
|
.where(and(
|
|
eq(emails.userId, userId),
|
|
or(
|
|
eq(emails.from, contact.email),
|
|
like(emails.from, `%<${contact.email}>%`)
|
|
)
|
|
))
|
|
.limit(10);
|
|
|
|
console.log('📨 Emails from this sender email (any contactId):', {
|
|
total: emailsFromSender.length,
|
|
withContactId: emailsFromSender.filter(e => e.contactId === contactId).length,
|
|
withNullContactId: emailsFromSender.filter(e => e.contactId === null).length,
|
|
withDifferentContactId: emailsFromSender.filter(e => e.contactId && e.contactId !== contactId).length,
|
|
sampleEmails: emailsFromSender.slice(0, 3),
|
|
});
|
|
}
|
|
|
|
const result = await db
|
|
.update(emails)
|
|
.set({ isRead: true, updatedAt: new Date() })
|
|
.where(and(eq(emails.userId, userId), eq(emails.contactId, contactId), eq(emails.isRead, false)))
|
|
.returning();
|
|
|
|
console.log('✅ markContactEmailsAsRead result:', { count: result.length, contactId });
|
|
|
|
return { success: true, count: result.length };
|
|
};
|
|
|
|
/**
|
|
* Get emails for a specific contact
|
|
*/
|
|
export const getContactEmails = async (userId, contactId) => {
|
|
const contactEmails = await db
|
|
.select()
|
|
.from(emails)
|
|
.where(and(eq(emails.userId, userId), eq(emails.contactId, contactId)))
|
|
.orderBy(desc(emails.date));
|
|
|
|
return contactEmails;
|
|
};
|