option for more emails,fix jmap service,add table email accounts

This commit is contained in:
richardtekula
2025-11-19 13:15:45 +01:00
parent 97f437c1c4
commit 1e7c1eab90
18 changed files with 1991 additions and 1299 deletions

View File

@@ -1,17 +1,20 @@
import * as crmEmailService from '../services/crm-email.service.js';
import * as contactService from '../services/contact.service.js';
import { markEmailAsRead, sendEmail, getJmapConfig, syncEmailsFromSender, searchEmailsJMAP as searchEmailsJMAPService } from '../services/jmap.service.js';
import * as emailAccountService from '../services/email-account.service.js';
import { markEmailAsRead, sendEmail, getJmapConfig, getJmapConfigFromAccount, syncEmailsFromSender, searchEmailsJMAP as searchEmailsJMAPService } from '../services/jmap.service.js';
import { formatErrorResponse } from '../utils/errors.js';
import { getUserById } from '../services/auth.service.js';
/**
* Get all emails for authenticated user
* GET /api/emails
* GET /api/emails?accountId=xxx (optional)
*/
export const getEmails = async (req, res) => {
try {
const userId = req.userId;
const emails = await crmEmailService.getUserEmails(userId);
const { accountId } = req.query;
const emails = await crmEmailService.getUserEmails(userId, accountId || null);
res.status(200).json({
success: true,
@@ -48,14 +51,14 @@ export const getThread = async (req, res) => {
/**
* Search emails
* GET /api/emails/search?q=query
* GET /api/emails/search?q=query&accountId=xxx (accountId optional)
*/
export const searchEmails = async (req, res) => {
try {
const userId = req.userId;
const { q } = req.query;
const { q, accountId } = req.query;
const results = await crmEmailService.searchEmails(userId, q);
const results = await crmEmailService.searchEmails(userId, q, accountId || null);
res.status(200).json({
success: true,
@@ -71,33 +74,24 @@ export const searchEmails = async (req, res) => {
/**
* Get unread count
* GET /api/emails/unread-count
* Returns total unread count and per-account counts
*/
export const getUnreadCount = async (req, res) => {
try {
const userId = req.userId;
const count = await crmEmailService.getUnreadCount(userId);
const accounts = [];
if (req.user?.email) {
accounts.push({
id: req.user.jmapAccountId || req.user.email,
email: req.user.email,
label: req.user.email,
unread: count,
});
}
const unreadData = await crmEmailService.getUnreadCount(userId);
res.status(200).json({
success: true,
data: {
count,
totalUnread: count,
accounts,
count: unreadData.totalUnread,
totalUnread: unreadData.totalUnread,
accounts: unreadData.accounts,
lastUpdatedAt: new Date().toISOString(),
},
});
} catch (error) {
console.error('❌ ERROR in getUnreadCount:', error);
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
@@ -106,23 +100,33 @@ export const getUnreadCount = async (req, res) => {
/**
* Sync latest emails for all contacts from JMAP
* POST /api/emails/sync
* Body: { accountId } (optional - defaults to primary account)
*/
export const syncEmails = async (req, res) => {
try {
const userId = req.userId;
const user = await getUserById(userId);
const { accountId } = req.body;
if (!user.email || !user.emailPassword || !user.jmapAccountId) {
return res.status(400).json({
success: false,
error: {
message: 'Najprv musíš pripojiť email účet v Profile',
statusCode: 400,
},
});
// Get email account (or primary if not specified)
let emailAccount;
if (accountId) {
emailAccount = await emailAccountService.getEmailAccountWithCredentials(accountId, userId);
} else {
const primaryAccount = await emailAccountService.getPrimaryEmailAccount(userId);
if (!primaryAccount) {
return res.status(400).json({
success: false,
error: {
message: 'Najprv musíš pripojiť email účet v Profile',
statusCode: 400,
},
});
}
emailAccount = await emailAccountService.getEmailAccountWithCredentials(primaryAccount.id, userId);
}
const contacts = await contactService.getUserContacts(userId);
// Get contacts for this email account
const contacts = await contactService.getUserContacts(userId, emailAccount.id);
if (!contacts.length) {
return res.status(200).json({
@@ -132,7 +136,7 @@ export const syncEmails = async (req, res) => {
});
}
const jmapConfig = getJmapConfig(user);
const jmapConfig = getJmapConfigFromAccount(emailAccount);
let totalSynced = 0;
let totalNew = 0;
@@ -141,6 +145,7 @@ export const syncEmails = async (req, res) => {
const { total, saved } = await syncEmailsFromSender(
jmapConfig,
userId,
emailAccount.id,
contact.id,
contact.email,
{ limit: 50 }
@@ -240,11 +245,12 @@ export const markThreadRead = async (req, res) => {
/**
* Send email reply
* POST /api/emails/reply
* Body: { to, subject, body, inReplyTo, threadId, accountId }
*/
export const replyToEmail = async (req, res) => {
try {
const userId = req.userId;
const { to, subject, body, inReplyTo = null, threadId = null } = req.body;
const { to, subject, body, inReplyTo = null, threadId = null, accountId } = req.body;
if (!to || !subject || !body) {
return res.status(400).json({
@@ -256,11 +262,27 @@ export const replyToEmail = async (req, res) => {
});
}
// Get user to access JMAP config
const user = await getUserById(userId);
const jmapConfig = getJmapConfig(user);
// Get email account (or primary if not specified)
let emailAccount;
if (accountId) {
emailAccount = await emailAccountService.getEmailAccountWithCredentials(accountId, userId);
} else {
const primaryAccount = await emailAccountService.getPrimaryEmailAccount(userId);
if (!primaryAccount) {
return res.status(400).json({
success: false,
error: {
message: 'Najprv musíš pripojiť email účet v Profile',
statusCode: 400,
},
});
}
emailAccount = await emailAccountService.getEmailAccountWithCredentials(primaryAccount.id, userId);
}
const result = await sendEmail(jmapConfig, userId, to, subject, body, inReplyTo, threadId);
const jmapConfig = getJmapConfigFromAccount(emailAccount);
const result = await sendEmail(jmapConfig, userId, emailAccount.id, to, subject, body, inReplyTo, threadId);
res.status(200).json({
success: true,
@@ -297,29 +319,38 @@ export const getContactEmails = async (req, res) => {
/**
* Search emails using JMAP full-text search
* GET /api/emails/search-jmap?query=text&limit=50&offset=0
* GET /api/emails/search-jmap?query=text&limit=50&offset=0&accountId=xxx
* Searches in: from, to, subject, and email body
*/
export const searchEmailsJMAP = async (req, res) => {
try {
const userId = req.userId;
const { query = '', limit = 50, offset = 0 } = req.query;
const { query = '', limit = 50, offset = 0, accountId } = req.query;
// Get user to access JMAP config
const user = await getUserById(userId);
console.log('🔍 searchEmailsJMAP called:', { userId, query, limit, offset, accountId });
// Check if user has JMAP email configured
if (!user.email || !user.emailPassword || !user.jmapAccountId) {
return res.status(400).json({
success: false,
error: {
message: 'Najprv musíš pripojiť email účet v Profile',
statusCode: 400,
},
});
// Get email account (or primary if not specified)
let emailAccount;
if (accountId) {
console.log('📧 Using provided accountId:', accountId);
emailAccount = await emailAccountService.getEmailAccountWithCredentials(accountId, userId);
} else {
console.log('📧 No accountId provided, using primary account');
const primaryAccount = await emailAccountService.getPrimaryEmailAccount(userId);
if (!primaryAccount) {
return res.status(400).json({
success: false,
error: {
message: 'Najprv musíš pripojiť email účet v Profile',
statusCode: 400,
},
});
}
emailAccount = await emailAccountService.getEmailAccountWithCredentials(primaryAccount.id, userId);
console.log('📧 Using primary account:', primaryAccount.id);
}
const jmapConfig = getJmapConfig(user);
const jmapConfig = getJmapConfigFromAccount(emailAccount);
const results = await searchEmailsJMAPService(
jmapConfig,
@@ -335,6 +366,7 @@ export const searchEmailsJMAP = async (req, res) => {
data: results,
});
} catch (error) {
console.error('❌ ERROR in searchEmailsJMAP:', error);
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}