initialize git, basic setup for crm
This commit is contained in:
231
src/controllers/admin.controller.js
Normal file
231
src/controllers/admin.controller.js
Normal file
@@ -0,0 +1,231 @@
|
||||
import { db } from '../config/database.js';
|
||||
import { users } from '../db/schema.js';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { hashPassword, generateTempPassword } from '../utils/password.js';
|
||||
import { logUserCreation, logRoleChange } from '../services/audit.service.js';
|
||||
import { formatErrorResponse, ConflictError, NotFoundError } from '../utils/errors.js';
|
||||
|
||||
/**
|
||||
* Vytvorenie nového usera s temporary password (admin only)
|
||||
* POST /api/admin/users
|
||||
*/
|
||||
export const createUser = async (req, res) => {
|
||||
const { username, tempPassword, role, firstName, lastName } = req.body;
|
||||
const adminId = req.userId;
|
||||
const ipAddress = req.ip || req.connection.remoteAddress;
|
||||
const userAgent = req.headers['user-agent'];
|
||||
|
||||
try {
|
||||
// Skontroluj či username už neexistuje
|
||||
const [existingUser] = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.username, username))
|
||||
.limit(1);
|
||||
|
||||
if (existingUser) {
|
||||
throw new ConflictError('Username už existuje');
|
||||
}
|
||||
|
||||
// Hash temporary password
|
||||
const hashedTempPassword = await hashPassword(tempPassword);
|
||||
|
||||
// Vytvor usera
|
||||
const [newUser] = await db
|
||||
.insert(users)
|
||||
.values({
|
||||
username,
|
||||
tempPassword: hashedTempPassword,
|
||||
role: role || 'member',
|
||||
firstName: firstName || null,
|
||||
lastName: lastName || null,
|
||||
changedPassword: false,
|
||||
})
|
||||
.returning();
|
||||
|
||||
// Log user creation
|
||||
await logUserCreation(adminId, newUser.id, username, role || 'member', ipAddress, userAgent);
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
data: {
|
||||
user: {
|
||||
id: newUser.id,
|
||||
username: newUser.username,
|
||||
role: newUser.role,
|
||||
tempPassword: tempPassword, // Vráti plain text password pre admina
|
||||
},
|
||||
},
|
||||
message: 'Používateľ úspešne vytvorený',
|
||||
});
|
||||
} catch (error) {
|
||||
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
|
||||
res.status(error.statusCode || 500).json(errorResponse);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Zoznam všetkých userov (admin only)
|
||||
* GET /api/admin/users
|
||||
*/
|
||||
export const getAllUsers = async (req, res) => {
|
||||
try {
|
||||
const allUsers = await db
|
||||
.select({
|
||||
id: users.id,
|
||||
username: users.username,
|
||||
email: users.email,
|
||||
firstName: users.firstName,
|
||||
lastName: users.lastName,
|
||||
role: users.role,
|
||||
changedPassword: users.changedPassword,
|
||||
lastLogin: users.lastLogin,
|
||||
createdAt: users.createdAt,
|
||||
})
|
||||
.from(users);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: {
|
||||
users: allUsers,
|
||||
count: allUsers.length,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
|
||||
res.status(error.statusCode || 500).json(errorResponse);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Získanie konkrétneho usera (admin only)
|
||||
* GET /api/admin/users/:userId
|
||||
*/
|
||||
export const getUserById = async (req, res) => {
|
||||
const { userId } = req.params;
|
||||
|
||||
try {
|
||||
const [user] = await db
|
||||
.select({
|
||||
id: users.id,
|
||||
username: users.username,
|
||||
email: users.email,
|
||||
firstName: users.firstName,
|
||||
lastName: users.lastName,
|
||||
role: users.role,
|
||||
changedPassword: users.changedPassword,
|
||||
lastLogin: users.lastLogin,
|
||||
createdAt: users.createdAt,
|
||||
updatedAt: users.updatedAt,
|
||||
})
|
||||
.from(users)
|
||||
.where(eq(users.id, userId))
|
||||
.limit(1);
|
||||
|
||||
if (!user) {
|
||||
throw new NotFoundError('Používateľ nenájdený');
|
||||
}
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: { user },
|
||||
});
|
||||
} catch (error) {
|
||||
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
|
||||
res.status(error.statusCode || 500).json(errorResponse);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Zmena role usera (admin only)
|
||||
* PATCH /api/admin/users/:userId/role
|
||||
*/
|
||||
export const changeUserRole = async (req, res) => {
|
||||
const { userId } = req.params;
|
||||
const { role } = req.body;
|
||||
const adminId = req.userId;
|
||||
const ipAddress = req.ip || req.connection.remoteAddress;
|
||||
const userAgent = req.headers['user-agent'];
|
||||
|
||||
try {
|
||||
// Získaj starú rolu
|
||||
const [user] = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.id, userId))
|
||||
.limit(1);
|
||||
|
||||
if (!user) {
|
||||
throw new NotFoundError('Používateľ nenájdený');
|
||||
}
|
||||
|
||||
const oldRole = user.role;
|
||||
|
||||
// Update role
|
||||
await db
|
||||
.update(users)
|
||||
.set({
|
||||
role,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(eq(users.id, userId));
|
||||
|
||||
// Log role change
|
||||
await logRoleChange(adminId, userId, oldRole, role, ipAddress, userAgent);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: {
|
||||
userId,
|
||||
oldRole,
|
||||
newRole: role,
|
||||
},
|
||||
message: 'Rola používateľa bola zmenená',
|
||||
});
|
||||
} catch (error) {
|
||||
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
|
||||
res.status(error.statusCode || 500).json(errorResponse);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Zmazanie usera (admin only)
|
||||
* DELETE /api/admin/users/:userId
|
||||
*/
|
||||
export const deleteUser = async (req, res) => {
|
||||
const { userId } = req.params;
|
||||
|
||||
try {
|
||||
const [user] = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.id, userId))
|
||||
.limit(1);
|
||||
|
||||
if (!user) {
|
||||
throw new NotFoundError('Používateľ nenájdený');
|
||||
}
|
||||
|
||||
// Zabraň zmazaniu posledného admina
|
||||
if (user.role === 'admin') {
|
||||
const [adminCount] = await db
|
||||
.select({ count: db.$count(users) })
|
||||
.from(users)
|
||||
.where(eq(users.role, 'admin'));
|
||||
|
||||
if (adminCount.count <= 1) {
|
||||
throw new ConflictError('Nemôžete zmazať posledného administrátora');
|
||||
}
|
||||
}
|
||||
|
||||
await db.delete(users).where(eq(users.id, userId));
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: 'Používateľ bol zmazaný',
|
||||
});
|
||||
} catch (error) {
|
||||
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
|
||||
res.status(error.statusCode || 500).json(errorResponse);
|
||||
}
|
||||
};
|
||||
204
src/controllers/auth.controller.js
Normal file
204
src/controllers/auth.controller.js
Normal file
@@ -0,0 +1,204 @@
|
||||
import * as authService from '../services/auth.service.js';
|
||||
import {
|
||||
logLoginAttempt,
|
||||
logPasswordChange,
|
||||
logEmailLink,
|
||||
} from '../services/audit.service.js';
|
||||
import { formatErrorResponse } from '../utils/errors.js';
|
||||
|
||||
/**
|
||||
* KROK 1: Login s temporary password
|
||||
* POST /api/auth/login
|
||||
*/
|
||||
export const login = async (req, res) => {
|
||||
const { username, password } = req.body;
|
||||
const ipAddress = req.ip || req.connection.remoteAddress;
|
||||
const userAgent = req.headers['user-agent'];
|
||||
|
||||
try {
|
||||
const result = await authService.loginWithTempPassword(
|
||||
username,
|
||||
password,
|
||||
ipAddress,
|
||||
userAgent
|
||||
);
|
||||
|
||||
// Log successful login
|
||||
await logLoginAttempt(username, true, ipAddress, userAgent);
|
||||
|
||||
// Nastav cookie s access tokenom (httpOnly, secure)
|
||||
res.cookie('accessToken', result.tokens.accessToken, {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === 'production',
|
||||
sameSite: 'strict',
|
||||
maxAge: 60 * 60 * 1000, // 1 hodina
|
||||
});
|
||||
|
||||
res.cookie('refreshToken', result.tokens.refreshToken, {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === 'production',
|
||||
sameSite: 'strict',
|
||||
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 dní
|
||||
});
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: {
|
||||
user: result.user,
|
||||
tokens: result.tokens,
|
||||
needsPasswordChange: result.needsPasswordChange,
|
||||
needsEmailSetup: result.needsEmailSetup,
|
||||
},
|
||||
message: 'Prihlásenie úspešné',
|
||||
});
|
||||
} catch (error) {
|
||||
// Log failed login
|
||||
await logLoginAttempt(username, false, ipAddress, userAgent, error.message);
|
||||
|
||||
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
|
||||
res.status(error.statusCode || 500).json(errorResponse);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* KROK 2: Nastavenie nového hesla
|
||||
* POST /api/auth/set-password
|
||||
* Requires: authentication
|
||||
*/
|
||||
export const setPassword = async (req, res) => {
|
||||
const { newPassword } = req.body;
|
||||
const userId = req.userId;
|
||||
const ipAddress = req.ip || req.connection.remoteAddress;
|
||||
const userAgent = req.headers['user-agent'];
|
||||
|
||||
try {
|
||||
const result = await authService.setNewPassword(userId, newPassword);
|
||||
|
||||
// Log password change
|
||||
await logPasswordChange(userId, ipAddress, userAgent);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: result,
|
||||
message: 'Heslo úspešne nastavené',
|
||||
});
|
||||
} catch (error) {
|
||||
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
|
||||
res.status(error.statusCode || 500).json(errorResponse);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* KROK 3: Pripojenie emailu s JMAP validáciou
|
||||
* POST /api/auth/link-email
|
||||
* Requires: authentication
|
||||
*/
|
||||
export const linkEmail = async (req, res) => {
|
||||
const { email, emailPassword } = req.body;
|
||||
const userId = req.userId;
|
||||
const ipAddress = req.ip || req.connection.remoteAddress;
|
||||
const userAgent = req.headers['user-agent'];
|
||||
|
||||
try {
|
||||
const result = await authService.linkEmail(userId, email, emailPassword);
|
||||
|
||||
// Log email link
|
||||
await logEmailLink(userId, email, ipAddress, userAgent);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: {
|
||||
email,
|
||||
accountId: result.accountId,
|
||||
},
|
||||
message: 'Email účet úspešne pripojený a overený',
|
||||
});
|
||||
} catch (error) {
|
||||
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
|
||||
res.status(error.statusCode || 500).json(errorResponse);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* KROK 3 (alternatíva): Skip email setup
|
||||
* POST /api/auth/skip-email
|
||||
* Requires: authentication
|
||||
*/
|
||||
export const skipEmail = async (req, res) => {
|
||||
const userId = req.userId;
|
||||
|
||||
try {
|
||||
const result = await authService.skipEmailSetup(userId);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: result,
|
||||
message: 'Email setup preskočený',
|
||||
});
|
||||
} catch (error) {
|
||||
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
|
||||
res.status(error.statusCode || 500).json(errorResponse);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Logout
|
||||
* POST /api/auth/logout
|
||||
* Requires: authentication
|
||||
*/
|
||||
export const logout = async (req, res) => {
|
||||
try {
|
||||
const result = await authService.logout();
|
||||
|
||||
// Vymaž cookies
|
||||
res.clearCookie('accessToken');
|
||||
res.clearCookie('refreshToken');
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Získanie aktuálnej session info
|
||||
* GET /api/auth/session
|
||||
* Requires: authentication
|
||||
*/
|
||||
export const getSession = async (req, res) => {
|
||||
try {
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: {
|
||||
user: req.user,
|
||||
authenticated: true,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
|
||||
res.status(error.statusCode || 500).json(errorResponse);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Profil aktuálneho usera
|
||||
* GET /api/auth/me
|
||||
* Requires: authentication
|
||||
*/
|
||||
export const getMe = async (req, res) => {
|
||||
try {
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: {
|
||||
user: req.user,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
|
||||
res.status(error.statusCode || 500).json(errorResponse);
|
||||
}
|
||||
};
|
||||
147
src/controllers/contact.controller.js
Normal file
147
src/controllers/contact.controller.js
Normal file
@@ -0,0 +1,147 @@
|
||||
import * as contactService from '../services/contact.service.js';
|
||||
import { discoverContactsFromJMAP, getJmapConfig } from '../services/jmap.service.js';
|
||||
import { formatErrorResponse } from '../utils/errors.js';
|
||||
import { getUserById } from '../services/auth.service.js';
|
||||
|
||||
/**
|
||||
* Get all contacts for authenticated user
|
||||
* GET /api/contacts
|
||||
*/
|
||||
export const getContacts = async (req, res) => {
|
||||
try {
|
||||
const userId = req.userId;
|
||||
const contacts = await contactService.getUserContacts(userId);
|
||||
|
||||
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?search=query&limit=50
|
||||
*/
|
||||
export const discoverContacts = async (req, res) => {
|
||||
try {
|
||||
const userId = req.userId;
|
||||
const { search = '', limit = 50 } = req.query;
|
||||
|
||||
// Get user to access JMAP config
|
||||
const user = await getUserById(userId);
|
||||
|
||||
// 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,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const jmapConfig = getJmapConfig(user);
|
||||
|
||||
const potentialContacts = await discoverContactsFromJMAP(
|
||||
jmapConfig,
|
||||
userId,
|
||||
search,
|
||||
parseInt(limit)
|
||||
);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
count: potentialContacts.length,
|
||||
data: potentialContacts,
|
||||
});
|
||||
} catch (error) {
|
||||
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
|
||||
res.status(error.statusCode || 500).json(errorResponse);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a new contact
|
||||
* POST /api/contacts
|
||||
*/
|
||||
export const addContact = async (req, res) => {
|
||||
try {
|
||||
const userId = req.userId;
|
||||
const { email, name = '', notes = '' } = req.body;
|
||||
|
||||
if (!email) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: {
|
||||
message: 'Email je povinný',
|
||||
statusCode: 400,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Get user to access JMAP config
|
||||
const user = await getUserById(userId);
|
||||
const jmapConfig = getJmapConfig(user);
|
||||
|
||||
const contact = await contactService.addContact(userId, jmapConfig, email, name, notes);
|
||||
|
||||
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
|
||||
*/
|
||||
export const removeContact = async (req, res) => {
|
||||
try {
|
||||
const userId = req.userId;
|
||||
const { contactId } = req.params;
|
||||
|
||||
const result = await contactService.removeContact(userId, contactId);
|
||||
|
||||
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
|
||||
*/
|
||||
export const updateContact = async (req, res) => {
|
||||
try {
|
||||
const userId = req.userId;
|
||||
const { contactId } = req.params;
|
||||
const { name, notes } = req.body;
|
||||
|
||||
const updated = await contactService.updateContact(userId, contactId, { 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);
|
||||
}
|
||||
};
|
||||
193
src/controllers/crm-email.controller.js
Normal file
193
src/controllers/crm-email.controller.js
Normal file
@@ -0,0 +1,193 @@
|
||||
import * as crmEmailService from '../services/crm-email.service.js';
|
||||
import { markEmailAsRead, sendEmail, getJmapConfig } 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
|
||||
*/
|
||||
export const getEmails = async (req, res) => {
|
||||
try {
|
||||
const userId = req.userId;
|
||||
const emails = await crmEmailService.getUserEmails(userId);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
count: emails.length,
|
||||
data: emails,
|
||||
});
|
||||
} catch (error) {
|
||||
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
|
||||
res.status(error.statusCode || 500).json(errorResponse);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get emails by thread (conversation)
|
||||
* GET /api/emails/thread/:threadId
|
||||
*/
|
||||
export const getThread = async (req, res) => {
|
||||
try {
|
||||
const userId = req.userId;
|
||||
const { threadId } = req.params;
|
||||
|
||||
const thread = await crmEmailService.getEmailThread(userId, threadId);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
count: thread.length,
|
||||
data: thread,
|
||||
});
|
||||
} catch (error) {
|
||||
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
|
||||
res.status(error.statusCode || 500).json(errorResponse);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Search emails
|
||||
* GET /api/emails/search?q=query
|
||||
*/
|
||||
export const searchEmails = async (req, res) => {
|
||||
try {
|
||||
const userId = req.userId;
|
||||
const { q } = req.query;
|
||||
|
||||
const results = await crmEmailService.searchEmails(userId, q);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
count: results.length,
|
||||
data: results,
|
||||
});
|
||||
} catch (error) {
|
||||
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
|
||||
res.status(error.statusCode || 500).json(errorResponse);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get unread count
|
||||
* GET /api/emails/unread-count
|
||||
*/
|
||||
export const getUnreadCount = async (req, res) => {
|
||||
try {
|
||||
const userId = req.userId;
|
||||
const count = await crmEmailService.getUnreadCount(userId);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: { count },
|
||||
});
|
||||
} catch (error) {
|
||||
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
|
||||
res.status(error.statusCode || 500).json(errorResponse);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Mark email as read/unread
|
||||
* PATCH /api/emails/:jmapId/read
|
||||
*/
|
||||
export const markAsRead = async (req, res) => {
|
||||
try {
|
||||
const userId = req.userId;
|
||||
const { jmapId } = req.params;
|
||||
const { isRead } = req.body;
|
||||
|
||||
// Get user to access JMAP config
|
||||
const user = await getUserById(userId);
|
||||
const jmapConfig = getJmapConfig(user);
|
||||
|
||||
await markEmailAsRead(jmapConfig, userId, jmapId, isRead);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: `Email označený ako ${isRead ? 'prečítaný' : 'neprečítaný'}`,
|
||||
});
|
||||
} catch (error) {
|
||||
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
|
||||
res.status(error.statusCode || 500).json(errorResponse);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Mark entire thread as read
|
||||
* POST /api/emails/thread/:threadId/read
|
||||
*/
|
||||
export const markThreadRead = async (req, res) => {
|
||||
try {
|
||||
const userId = req.userId;
|
||||
const { threadId } = req.params;
|
||||
|
||||
const result = await crmEmailService.markThreadAsRead(userId, threadId);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: 'Konverzácia označená ako prečítaná',
|
||||
count: result.count,
|
||||
});
|
||||
} catch (error) {
|
||||
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
|
||||
res.status(error.statusCode || 500).json(errorResponse);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Send email reply
|
||||
* POST /api/emails/reply
|
||||
*/
|
||||
export const replyToEmail = async (req, res) => {
|
||||
try {
|
||||
const userId = req.userId;
|
||||
const { to, subject, body, inReplyTo = null, threadId = null } = req.body;
|
||||
|
||||
if (!to || !subject || !body) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: {
|
||||
message: 'Chýbajúce povinné polia: to, subject, body',
|
||||
statusCode: 400,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Get user to access JMAP config
|
||||
const user = await getUserById(userId);
|
||||
const jmapConfig = getJmapConfig(user);
|
||||
|
||||
const result = await sendEmail(jmapConfig, userId, to, subject, body, inReplyTo, threadId);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: 'Email odoslaný',
|
||||
data: result,
|
||||
});
|
||||
} catch (error) {
|
||||
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
|
||||
res.status(error.statusCode || 500).json(errorResponse);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get emails for a specific contact
|
||||
* GET /api/emails/contact/:contactId
|
||||
*/
|
||||
export const getContactEmails = async (req, res) => {
|
||||
try {
|
||||
const userId = req.userId;
|
||||
const { contactId } = req.params;
|
||||
|
||||
const emails = await crmEmailService.getContactEmails(userId, contactId);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
count: emails.length,
|
||||
data: emails,
|
||||
});
|
||||
} catch (error) {
|
||||
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
|
||||
res.status(error.statusCode || 500).json(errorResponse);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user