Files
crm-server/src/controllers/contact.controller.js
2025-11-21 13:56:02 +01:00

363 lines
11 KiB
JavaScript

import * as contactService from '../services/contact.service.js';
import { discoverContactsFromJMAP, getJmapConfigFromAccount } from '../services/jmap.service.js';
import { formatErrorResponse } from '../utils/errors.js';
import * as emailAccountService from '../services/email-account.service.js';
import { logger } from '../utils/logger.js';
/**
* Get all contacts for an email account
* GET /api/contacts?accountId=xxx (required)
*/
export const getContacts = async (req, res) => {
try {
const userId = req.userId;
const { accountId } = req.query;
if (!accountId) {
return res.status(400).json({
success: false,
error: {
message: 'accountId je povinný parameter',
statusCode: 400,
},
});
}
// Verify user has access to this email account
await emailAccountService.getEmailAccountById(accountId, userId);
// Get contacts for email account
const contacts = await contactService.getContactsForEmailAccount(accountId);
res.status(200).json({
success: true,
count: contacts.length,
data: contacts,
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Discover potential contacts from JMAP (email senders)
* GET /api/contacts/discover?accountId=xxx&search=query&limit=50
*/
export const discoverContacts = async (req, res) => {
try {
const userId = req.userId;
const { accountId, search = '', limit = 50 } = req.query;
logger.debug('discoverContacts called', { userId, accountId, search, limit });
// Get email account (or primary if not specified)
let emailAccount;
if (accountId) {
logger.debug('Getting email account by ID', { accountId });
emailAccount = await emailAccountService.getEmailAccountWithCredentials(accountId, userId);
logger.debug('Email account retrieved', { id: emailAccount.id, email: emailAccount.email });
} else {
logger.debug('No accountId provided, getting primary account', { userId });
const primaryAccount = await emailAccountService.getPrimaryEmailAccount(userId);
logger.debug('Primary account', primaryAccount ? { id: primaryAccount.id, email: primaryAccount.email } : { found: false });
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);
logger.debug('Email account retrieved from primary', { id: emailAccount.id, email: emailAccount.email });
}
const jmapConfig = getJmapConfigFromAccount(emailAccount);
logger.debug('JMAP Config created', {
server: jmapConfig.server,
username: jmapConfig.username,
accountId: jmapConfig.accountId,
hasPassword: !!jmapConfig.password
});
const potentialContacts = await discoverContactsFromJMAP(
jmapConfig,
emailAccount.id, // emailAccountId
search,
parseInt(limit)
);
res.status(200).json({
success: true,
count: potentialContacts.length,
data: potentialContacts,
});
} catch (error) {
logger.error('ERROR in discoverContacts', { error: error.message, stack: error.stack });
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Add a new contact
* POST /api/contacts
* Body: { email, name, notes, accountId }
*/
export const addContact = async (req, res) => {
try {
const userId = req.userId;
logger.debug('Full req.body', { body: req.body });
const { email, name = '', notes = '', accountId } = req.body;
logger.debug('addContact called', { userId, email, name, accountId });
if (!email) {
return res.status(400).json({
success: false,
error: {
message: 'Email je povinný',
statusCode: 400,
},
});
}
// Get email account (or primary if not specified)
let emailAccount;
if (accountId) {
logger.debug('Using provided accountId', { accountId });
emailAccount = await emailAccountService.getEmailAccountWithCredentials(accountId, userId);
} else {
logger.debug('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);
logger.debug('Using primary account', { accountId: primaryAccount.id });
}
const jmapConfig = getJmapConfigFromAccount(emailAccount);
const contact = await contactService.addContact(
emailAccount.id,
jmapConfig,
email,
name,
notes,
userId // addedByUserId
);
res.status(201).json({
success: true,
data: contact,
message: 'Kontakt pridaný a emaily synchronizované',
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Remove a contact
* DELETE /api/contacts/:contactId?accountId=xxx
*/
export const removeContact = async (req, res) => {
try {
const userId = req.userId;
const { contactId } = req.params;
const { accountId } = req.query;
if (!accountId) {
return res.status(400).json({
success: false,
error: {
message: 'accountId je povinný parameter',
statusCode: 400,
},
});
}
// Verify user has access to this email account
await emailAccountService.getEmailAccountById(accountId, userId);
const result = await contactService.removeContact(contactId, accountId);
res.status(200).json({
success: true,
message: result.message,
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Update a contact
* PATCH /api/contacts/:contactId?accountId=xxx
*/
export const updateContact = async (req, res) => {
try {
const userId = req.userId;
const { contactId } = req.params;
const { accountId } = req.query;
const { name, notes } = req.body;
if (!accountId) {
return res.status(400).json({
success: false,
error: {
message: 'accountId je povinný parameter',
statusCode: 400,
},
});
}
// Verify user has access to this email account
await emailAccountService.getEmailAccountById(accountId, userId);
const updated = await contactService.updateContact(contactId, accountId, { name, notes });
res.status(200).json({
success: true,
data: updated,
message: 'Kontakt aktualizovaný',
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Link company to contact
* POST /api/contacts/:contactId/link-company?accountId=xxx
* Body: { companyId }
*/
export const linkCompanyToContact = async (req, res) => {
try {
const userId = req.userId;
const { contactId } = req.params;
const { accountId } = req.query;
const { companyId } = req.body;
if (!accountId) {
return res.status(400).json({
success: false,
error: {
message: 'accountId je povinný parameter',
statusCode: 400,
},
});
}
if (!companyId) {
return res.status(400).json({
success: false,
error: {
message: 'companyId je povinný',
statusCode: 400,
},
});
}
// Verify user has access to this email account
await emailAccountService.getEmailAccountById(accountId, userId);
const updated = await contactService.linkCompanyToContact(contactId, accountId, companyId);
res.status(200).json({
success: true,
data: updated,
message: 'Firma bola linknutá ku kontaktu',
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Unlink company from contact
* POST /api/contacts/:contactId/unlink-company?accountId=xxx
*/
export const unlinkCompanyFromContact = async (req, res) => {
try {
const userId = req.userId;
const { contactId } = req.params;
const { accountId } = req.query;
if (!accountId) {
return res.status(400).json({
success: false,
error: {
message: 'accountId je povinný parameter',
statusCode: 400,
},
});
}
// Verify user has access to this email account
await emailAccountService.getEmailAccountById(accountId, userId);
const updated = await contactService.unlinkCompanyFromContact(contactId, accountId);
res.status(200).json({
success: true,
data: updated,
message: 'Firma bola odlinknutá od kontaktu',
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Create company from contact
* POST /api/contacts/:contactId/create-company?accountId=xxx
* Body: { name, email, phone, address, city, country, website, description } (all optional, uses contact data as defaults)
*/
export const createCompanyFromContact = async (req, res) => {
try {
const userId = req.userId;
const { contactId } = req.params;
const { accountId } = req.query;
const companyData = req.body;
if (!accountId) {
return res.status(400).json({
success: false,
error: {
message: 'accountId je povinný parameter',
statusCode: 400,
},
});
}
// Verify user has access to this email account
await emailAccountService.getEmailAccountById(accountId, userId);
const result = await contactService.createCompanyFromContact(contactId, accountId, userId, companyData);
res.status(201).json({
success: true,
data: result,
message: 'Firma bola vytvorená z kontaktu',
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};