Improve centralized error handling

This commit is contained in:
richardtekula
2025-12-04 07:39:52 +01:00
parent 109cae1167
commit 35dfa07668
14 changed files with 266 additions and 336 deletions

View File

@@ -1,9 +1,9 @@
import { db } from '../config/database.js'; import { db } from '../config/database.js';
import { users } from '../db/schema.js'; import { users, userEmailAccounts, emailAccounts } from '../db/schema.js';
import { eq } from 'drizzle-orm'; import { eq, inArray } from 'drizzle-orm';
import { hashPassword, generateTempPassword } from '../utils/password.js'; import { hashPassword, generateTempPassword } from '../utils/password.js';
import { logUserCreation, logRoleChange } from '../services/audit.service.js'; import { logUserCreation, logRoleChange } from '../services/audit.service.js';
import { formatErrorResponse, ConflictError, NotFoundError } from '../utils/errors.js'; import { ConflictError, NotFoundError } from '../utils/errors.js';
import * as emailAccountService from '../services/email-account.service.js'; import * as emailAccountService from '../services/email-account.service.js';
/** /**
@@ -11,7 +11,7 @@ import * as emailAccountService from '../services/email-account.service.js';
* Ak je poskytnutý email a emailPassword, automaticky sa fetchne JMAP account ID * Ak je poskytnutý email a emailPassword, automaticky sa fetchne JMAP account ID
* POST /api/admin/users * POST /api/admin/users
*/ */
export const createUser = async (req, res) => { export const createUser = async (req, res, next) => {
const { username, email, emailPassword, firstName, lastName, role } = req.body; const { username, email, emailPassword, firstName, lastName, role } = req.body;
const adminId = req.userId; const adminId = req.userId;
const ipAddress = req.ip || req.connection.remoteAddress; const ipAddress = req.ip || req.connection.remoteAddress;
@@ -100,8 +100,7 @@ export const createUser = async (req, res) => {
: 'Používateľ úspešne vytvorený. Email môže byť nastavený neskôr.', : 'Používateľ úspešne vytvorený. Email môže byť nastavený neskôr.',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -109,7 +108,7 @@ export const createUser = async (req, res) => {
* Zoznam všetkých userov (admin only) * Zoznam všetkých userov (admin only)
* GET /api/admin/users * GET /api/admin/users
*/ */
export const getAllUsers = async (req, res) => { export const getAllUsers = async (req, res, next) => {
try { try {
const allUsers = await db const allUsers = await db
.select({ .select({
@@ -130,8 +129,7 @@ export const getAllUsers = async (req, res) => {
data: allUsers, data: allUsers,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -139,7 +137,7 @@ export const getAllUsers = async (req, res) => {
* Získanie konkrétneho usera (admin only) * Získanie konkrétneho usera (admin only)
* GET /api/admin/users/:userId * GET /api/admin/users/:userId
*/ */
export const getUser = async (req, res) => { export const getUser = async (req, res, next) => {
const { userId } = req.params; const { userId } = req.params;
try { try {
@@ -176,8 +174,7 @@ export const getUser = async (req, res) => {
}, },
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -185,7 +182,7 @@ export const getUser = async (req, res) => {
* Zmena role usera (admin only) * Zmena role usera (admin only)
* PATCH /api/admin/users/:userId/role * PATCH /api/admin/users/:userId/role
*/ */
export const changeUserRole = async (req, res) => { export const changeUserRole = async (req, res, next) => {
const { userId } = req.params; const { userId } = req.params;
const { role } = req.body; const { role } = req.body;
const adminId = req.userId; const adminId = req.userId;
@@ -228,8 +225,7 @@ export const changeUserRole = async (req, res) => {
message: 'Rola používateľa bola zmenená', message: 'Rola používateľa bola zmenená',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -237,7 +233,7 @@ export const changeUserRole = async (req, res) => {
* Zmazanie usera (admin only) * Zmazanie usera (admin only)
* DELETE /api/admin/users/:userId * DELETE /api/admin/users/:userId
*/ */
export const deleteUser = async (req, res) => { export const deleteUser = async (req, res, next) => {
const { userId } = req.params; const { userId } = req.params;
try { try {
@@ -263,14 +259,38 @@ export const deleteUser = async (req, res) => {
} }
} }
// Get user's email account IDs before deletion
const userEmailAccountLinks = await db
.select({ emailAccountId: userEmailAccounts.emailAccountId })
.from(userEmailAccounts)
.where(eq(userEmailAccounts.userId, userId));
const emailAccountIds = userEmailAccountLinks.map(link => link.emailAccountId);
// Delete user (cascades userEmailAccounts links)
await db.delete(users).where(eq(users.id, userId)); await db.delete(users).where(eq(users.id, userId));
// Delete orphaned email accounts (no users linked)
// This will cascade delete contacts and emails
let deletedEmailAccounts = 0;
for (const emailAccountId of emailAccountIds) {
const [remainingLinks] = await db
.select({ count: db.$count(userEmailAccounts) })
.from(userEmailAccounts)
.where(eq(userEmailAccounts.emailAccountId, emailAccountId));
if (remainingLinks.count === 0) {
await db.delete(emailAccounts).where(eq(emailAccounts.id, emailAccountId));
deletedEmailAccounts++;
}
}
res.status(200).json({ res.status(200).json({
success: true, success: true,
message: 'Používateľ bol zmazaný', message: 'Používateľ bol zmazaný',
deletedEmailAccounts,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };

View File

@@ -4,13 +4,12 @@ import {
logPasswordChange, logPasswordChange,
logEmailLink, logEmailLink,
} from '../services/audit.service.js'; } from '../services/audit.service.js';
import { formatErrorResponse } from '../utils/errors.js';
/** /**
* KROK 1: Login s temporary password * KROK 1: Login s temporary password
* POST /api/auth/login * POST /api/auth/login
*/ */
export const login = async (req, res) => { export const login = async (req, res, next) => {
const { username, password } = req.body; const { username, password } = req.body;
const ipAddress = req.ip || req.connection.remoteAddress; const ipAddress = req.ip || req.connection.remoteAddress;
const userAgent = req.headers['user-agent']; const userAgent = req.headers['user-agent'];
@@ -55,8 +54,7 @@ export const login = async (req, res) => {
// Log failed login // Log failed login
await logLoginAttempt(username, false, ipAddress, userAgent, error.message); await logLoginAttempt(username, false, ipAddress, userAgent, error.message);
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -65,7 +63,7 @@ export const login = async (req, res) => {
* POST /api/auth/set-password * POST /api/auth/set-password
* Requires: authentication * Requires: authentication
*/ */
export const setPassword = async (req, res) => { export const setPassword = async (req, res, next) => {
const { newPassword } = req.body; const { newPassword } = req.body;
const userId = req.userId; const userId = req.userId;
const ipAddress = req.ip || req.connection.remoteAddress; const ipAddress = req.ip || req.connection.remoteAddress;
@@ -83,8 +81,7 @@ export const setPassword = async (req, res) => {
message: 'Heslo úspešne nastavené', message: 'Heslo úspešne nastavené',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -93,7 +90,7 @@ export const setPassword = async (req, res) => {
* POST /api/auth/link-email * POST /api/auth/link-email
* Requires: authentication * Requires: authentication
*/ */
export const linkEmail = async (req, res) => { export const linkEmail = async (req, res, next) => {
const { email, emailPassword } = req.body; const { email, emailPassword } = req.body;
const userId = req.userId; const userId = req.userId;
const ipAddress = req.ip || req.connection.remoteAddress; const ipAddress = req.ip || req.connection.remoteAddress;
@@ -114,8 +111,7 @@ export const linkEmail = async (req, res) => {
message: 'Email účet úspešne pripojený a overený', message: 'Email účet úspešne pripojený a overený',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -124,7 +120,7 @@ export const linkEmail = async (req, res) => {
* POST /api/auth/skip-email * POST /api/auth/skip-email
* Requires: authentication * Requires: authentication
*/ */
export const skipEmail = async (req, res) => { export const skipEmail = async (req, res, next) => {
const userId = req.userId; const userId = req.userId;
try { try {
@@ -136,8 +132,7 @@ export const skipEmail = async (req, res) => {
message: 'Email setup preskočený', message: 'Email setup preskočený',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -146,7 +141,7 @@ export const skipEmail = async (req, res) => {
* POST /api/auth/logout * POST /api/auth/logout
* Requires: authentication * Requires: authentication
*/ */
export const logout = async (req, res) => { export const logout = async (req, res, next) => {
try { try {
const result = await authService.logout(); const result = await authService.logout();
@@ -159,8 +154,7 @@ export const logout = async (req, res) => {
message: result.message, message: result.message,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -169,7 +163,7 @@ export const logout = async (req, res) => {
* GET /api/auth/session * GET /api/auth/session
* Requires: authentication * Requires: authentication
*/ */
export const getSession = async (req, res) => { export const getSession = async (req, res, next) => {
try { try {
res.status(200).json({ res.status(200).json({
success: true, success: true,
@@ -179,8 +173,7 @@ export const getSession = async (req, res) => {
}, },
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -189,7 +182,7 @@ export const getSession = async (req, res) => {
* GET /api/auth/me * GET /api/auth/me
* Requires: authentication * Requires: authentication
*/ */
export const getMe = async (req, res) => { export const getMe = async (req, res, next) => {
try { try {
res.status(200).json({ res.status(200).json({
success: true, success: true,
@@ -198,7 +191,6 @@ export const getMe = async (req, res) => {
}, },
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };

View File

@@ -2,13 +2,12 @@ import * as companyService from '../services/company.service.js';
import * as noteService from '../services/note.service.js'; import * as noteService from '../services/note.service.js';
import * as companyReminderService from '../services/company-reminder.service.js'; import * as companyReminderService from '../services/company-reminder.service.js';
import * as companyEmailService from '../services/company-email.service.js'; import * as companyEmailService from '../services/company-email.service.js';
import { formatErrorResponse } from '../utils/errors.js';
/** /**
* Get all companies * Get all companies
* GET /api/companies?search=query * GET /api/companies?search=query
*/ */
export const getAllCompanies = async (req, res) => { export const getAllCompanies = async (req, res, next) => {
try { try {
const { search } = req.query; const { search } = req.query;
@@ -20,8 +19,7 @@ export const getAllCompanies = async (req, res) => {
data: companies, data: companies,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -29,7 +27,7 @@ export const getAllCompanies = async (req, res) => {
* Get company by ID * Get company by ID
* GET /api/companies/:companyId * GET /api/companies/:companyId
*/ */
export const getCompanyById = async (req, res) => { export const getCompanyById = async (req, res, next) => {
try { try {
const { companyId } = req.params; const { companyId } = req.params;
@@ -40,8 +38,7 @@ export const getCompanyById = async (req, res) => {
data: company, data: company,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -49,7 +46,7 @@ export const getCompanyById = async (req, res) => {
* Get company email threads aggregated across user's email accounts * Get company email threads aggregated across user's email accounts
* GET /api/companies/:companyId/email-threads * GET /api/companies/:companyId/email-threads
*/ */
export const getCompanyEmailThreads = async (req, res) => { export const getCompanyEmailThreads = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const { companyId } = req.params; const { companyId } = req.params;
@@ -64,8 +61,7 @@ export const getCompanyEmailThreads = async (req, res) => {
data: result, data: result,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -73,7 +69,7 @@ export const getCompanyEmailThreads = async (req, res) => {
* Get unread email counts grouped by company for current user * Get unread email counts grouped by company for current user
* GET /api/companies/email-unread * GET /api/companies/email-unread
*/ */
export const getCompanyUnreadCounts = async (req, res) => { export const getCompanyUnreadCounts = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const counts = await companyEmailService.getCompanyUnreadCounts(userId); const counts = await companyEmailService.getCompanyUnreadCounts(userId);
@@ -83,8 +79,7 @@ export const getCompanyUnreadCounts = async (req, res) => {
data: counts, data: counts,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -92,7 +87,7 @@ export const getCompanyUnreadCounts = async (req, res) => {
* Get company with relations (projects, todos, notes) * Get company with relations (projects, todos, notes)
* GET /api/companies/:companyId/details * GET /api/companies/:companyId/details
*/ */
export const getCompanyWithRelations = async (req, res) => { export const getCompanyWithRelations = async (req, res, next) => {
try { try {
const { companyId } = req.params; const { companyId } = req.params;
@@ -103,8 +98,7 @@ export const getCompanyWithRelations = async (req, res) => {
data: company, data: company,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -113,7 +107,7 @@ export const getCompanyWithRelations = async (req, res) => {
* POST /api/companies * POST /api/companies
* Body: { name, description, address, city, country, phone, email, website } * Body: { name, description, address, city, country, phone, email, website }
*/ */
export const createCompany = async (req, res) => { export const createCompany = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const data = req.body; const data = req.body;
@@ -126,8 +120,7 @@ export const createCompany = async (req, res) => {
message: 'Firma bola vytvorená', message: 'Firma bola vytvorená',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -136,7 +129,7 @@ export const createCompany = async (req, res) => {
* PATCH /api/companies/:companyId * PATCH /api/companies/:companyId
* Body: { name, description, address, city, country, phone, email, website } * Body: { name, description, address, city, country, phone, email, website }
*/ */
export const updateCompany = async (req, res) => { export const updateCompany = async (req, res, next) => {
try { try {
const { companyId } = req.params; const { companyId } = req.params;
const data = req.body; const data = req.body;
@@ -149,8 +142,7 @@ export const updateCompany = async (req, res) => {
message: 'Firma bola aktualizovaná', message: 'Firma bola aktualizovaná',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -158,7 +150,7 @@ export const updateCompany = async (req, res) => {
* Delete company * Delete company
* DELETE /api/companies/:companyId * DELETE /api/companies/:companyId
*/ */
export const deleteCompany = async (req, res) => { export const deleteCompany = async (req, res, next) => {
try { try {
const { companyId } = req.params; const { companyId } = req.params;
@@ -169,8 +161,7 @@ export const deleteCompany = async (req, res) => {
message: result.message, message: result.message,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -178,7 +169,7 @@ export const deleteCompany = async (req, res) => {
* Get company notes * Get company notes
* GET /api/companies/:companyId/notes * GET /api/companies/:companyId/notes
*/ */
export const getCompanyNotes = async (req, res) => { export const getCompanyNotes = async (req, res, next) => {
try { try {
const { companyId } = req.params; const { companyId } = req.params;
@@ -190,8 +181,7 @@ export const getCompanyNotes = async (req, res) => {
data: notes, data: notes,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -199,7 +189,7 @@ export const getCompanyNotes = async (req, res) => {
* Add company note * Add company note
* POST /api/companies/:companyId/notes * POST /api/companies/:companyId/notes
*/ */
export const addCompanyNote = async (req, res) => { export const addCompanyNote = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const { companyId } = req.params; const { companyId } = req.params;
@@ -216,8 +206,7 @@ export const addCompanyNote = async (req, res) => {
message: 'Poznámka bola pridaná', message: 'Poznámka bola pridaná',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -225,7 +214,7 @@ export const addCompanyNote = async (req, res) => {
* Update company note * Update company note
* PATCH /api/companies/:companyId/notes/:noteId * PATCH /api/companies/:companyId/notes/:noteId
*/ */
export const updateCompanyNote = async (req, res) => { export const updateCompanyNote = async (req, res, next) => {
try { try {
const { noteId } = req.params; const { noteId } = req.params;
const { content } = req.body; const { content } = req.body;
@@ -240,8 +229,7 @@ export const updateCompanyNote = async (req, res) => {
message: 'Poznámka bola aktualizovaná', message: 'Poznámka bola aktualizovaná',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -249,7 +237,7 @@ export const updateCompanyNote = async (req, res) => {
* Delete company note * Delete company note
* DELETE /api/companies/:companyId/notes/:noteId * DELETE /api/companies/:companyId/notes/:noteId
*/ */
export const deleteCompanyNote = async (req, res) => { export const deleteCompanyNote = async (req, res, next) => {
try { try {
const { noteId } = req.params; const { noteId } = req.params;
@@ -260,8 +248,7 @@ export const deleteCompanyNote = async (req, res) => {
message: result.message, message: result.message,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -269,7 +256,7 @@ export const deleteCompanyNote = async (req, res) => {
* Company reminders * Company reminders
* CRUD for /api/companies/:companyId/reminders * CRUD for /api/companies/:companyId/reminders
*/ */
export const getCompanyReminders = async (req, res) => { export const getCompanyReminders = async (req, res, next) => {
try { try {
const { companyId } = req.params; const { companyId } = req.params;
@@ -281,12 +268,11 @@ export const getCompanyReminders = async (req, res) => {
data: reminders, data: reminders,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
export const createCompanyReminder = async (req, res) => { export const createCompanyReminder = async (req, res, next) => {
try { try {
const { companyId } = req.params; const { companyId } = req.params;
const { description, dueDate, isChecked } = req.body; const { description, dueDate, isChecked } = req.body;
@@ -299,12 +285,11 @@ export const createCompanyReminder = async (req, res) => {
message: 'Reminder bol pridaný', message: 'Reminder bol pridaný',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
export const updateCompanyReminder = async (req, res) => { export const updateCompanyReminder = async (req, res, next) => {
try { try {
const { companyId, reminderId } = req.params; const { companyId, reminderId } = req.params;
const { description, dueDate, isChecked } = req.body; const { description, dueDate, isChecked } = req.body;
@@ -317,12 +302,11 @@ export const updateCompanyReminder = async (req, res) => {
message: 'Reminder bol aktualizovaný', message: 'Reminder bol aktualizovaný',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
export const deleteCompanyReminder = async (req, res) => { export const deleteCompanyReminder = async (req, res, next) => {
try { try {
const { companyId, reminderId } = req.params; const { companyId, reminderId } = req.params;
@@ -333,12 +317,11 @@ export const deleteCompanyReminder = async (req, res) => {
message: result.message, message: result.message,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
export const getReminderSummary = async (_req, res) => { export const getReminderSummary = async (_req, res, next) => {
try { try {
const summary = await companyReminderService.getReminderSummary(); const summary = await companyReminderService.getReminderSummary();
res.status(200).json({ res.status(200).json({
@@ -346,12 +329,11 @@ export const getReminderSummary = async (_req, res) => {
data: summary, data: summary,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
export const getReminderCountsByCompany = async (_req, res) => { export const getReminderCountsByCompany = async (_req, res, next) => {
try { try {
const counts = await companyReminderService.getReminderCountsByCompany(); const counts = await companyReminderService.getReminderCountsByCompany();
res.status(200).json({ res.status(200).json({
@@ -359,12 +341,11 @@ export const getReminderCountsByCompany = async (_req, res) => {
data: counts, data: counts,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
export const getUpcomingReminders = async (_req, res) => { export const getUpcomingReminders = async (_req, res, next) => {
try { try {
const reminders = await companyReminderService.getUpcomingReminders(); const reminders = await companyReminderService.getUpcomingReminders();
res.status(200).json({ res.status(200).json({
@@ -373,7 +354,6 @@ export const getUpcomingReminders = async (_req, res) => {
data: reminders, data: reminders,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };

View File

@@ -1,6 +1,5 @@
import * as contactService from '../services/contact.service.js'; import * as contactService from '../services/contact.service.js';
import { discoverContactsFromJMAP, getJmapConfigFromAccount } from '../services/jmap.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 * as emailAccountService from '../services/email-account.service.js';
import { logger } from '../utils/logger.js'; import { logger } from '../utils/logger.js';
@@ -8,7 +7,7 @@ import { logger } from '../utils/logger.js';
* Get all contacts for an email account * Get all contacts for an email account
* GET /api/contacts?accountId=xxx (required) * GET /api/contacts?accountId=xxx (required)
*/ */
export const getContacts = async (req, res) => { export const getContacts = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const { accountId } = req.query; const { accountId } = req.query;
@@ -35,8 +34,7 @@ export const getContacts = async (req, res) => {
data: contacts, data: contacts,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -44,7 +42,7 @@ export const getContacts = async (req, res) => {
* Discover potential contacts from JMAP (email senders) * Discover potential contacts from JMAP (email senders)
* GET /api/contacts/discover?accountId=xxx&search=query&limit=50 * GET /api/contacts/discover?accountId=xxx&search=query&limit=50
*/ */
export const discoverContacts = async (req, res) => { export const discoverContacts = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const { accountId, search = '', limit = 50 } = req.query; const { accountId, search = '', limit = 50 } = req.query;
@@ -96,8 +94,7 @@ export const discoverContacts = async (req, res) => {
}); });
} catch (error) { } catch (error) {
logger.error('ERROR in discoverContacts', { error: error.message, stack: error.stack }); logger.error('ERROR in discoverContacts', { error: error.message, stack: error.stack });
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -106,7 +103,7 @@ export const discoverContacts = async (req, res) => {
* POST /api/contacts * POST /api/contacts
* Body: { email, name, notes, accountId } * Body: { email, name, notes, accountId }
*/ */
export const addContact = async (req, res) => { export const addContact = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
logger.debug('Full req.body', { body: req.body }); logger.debug('Full req.body', { body: req.body });
@@ -162,8 +159,7 @@ export const addContact = async (req, res) => {
message: 'Kontakt pridaný a emaily synchronizované', message: 'Kontakt pridaný a emaily synchronizované',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -171,7 +167,7 @@ export const addContact = async (req, res) => {
* Remove a contact * Remove a contact
* DELETE /api/contacts/:contactId?accountId=xxx * DELETE /api/contacts/:contactId?accountId=xxx
*/ */
export const removeContact = async (req, res) => { export const removeContact = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const { contactId } = req.params; const { contactId } = req.params;
@@ -197,8 +193,7 @@ export const removeContact = async (req, res) => {
message: result.message, message: result.message,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -206,7 +201,7 @@ export const removeContact = async (req, res) => {
* Update a contact * Update a contact
* PATCH /api/contacts/:contactId?accountId=xxx * PATCH /api/contacts/:contactId?accountId=xxx
*/ */
export const updateContact = async (req, res) => { export const updateContact = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const { contactId } = req.params; const { contactId } = req.params;
@@ -234,8 +229,7 @@ export const updateContact = async (req, res) => {
message: 'Kontakt aktualizovaný', message: 'Kontakt aktualizovaný',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -244,7 +238,7 @@ export const updateContact = async (req, res) => {
* POST /api/contacts/:contactId/link-company?accountId=xxx * POST /api/contacts/:contactId/link-company?accountId=xxx
* Body: { companyId } * Body: { companyId }
*/ */
export const linkCompanyToContact = async (req, res) => { export const linkCompanyToContact = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const { contactId } = req.params; const { contactId } = req.params;
@@ -282,8 +276,7 @@ export const linkCompanyToContact = async (req, res) => {
message: 'Firma bola linknutá ku kontaktu', message: 'Firma bola linknutá ku kontaktu',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -291,7 +284,7 @@ export const linkCompanyToContact = async (req, res) => {
* Unlink company from contact * Unlink company from contact
* POST /api/contacts/:contactId/unlink-company?accountId=xxx * POST /api/contacts/:contactId/unlink-company?accountId=xxx
*/ */
export const unlinkCompanyFromContact = async (req, res) => { export const unlinkCompanyFromContact = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const { contactId } = req.params; const { contactId } = req.params;
@@ -318,8 +311,7 @@ export const unlinkCompanyFromContact = async (req, res) => {
message: 'Firma bola odlinknutá od kontaktu', message: 'Firma bola odlinknutá od kontaktu',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -328,7 +320,7 @@ export const unlinkCompanyFromContact = async (req, res) => {
* POST /api/contacts/:contactId/create-company?accountId=xxx * POST /api/contacts/:contactId/create-company?accountId=xxx
* Body: { name, email, phone, address, city, country, website, description } (all optional, uses contact data as defaults) * Body: { name, email, phone, address, city, country, website, description } (all optional, uses contact data as defaults)
*/ */
export const createCompanyFromContact = async (req, res) => { export const createCompanyFromContact = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const { contactId } = req.params; const { contactId } = req.params;
@@ -356,7 +348,6 @@ export const createCompanyFromContact = async (req, res) => {
message: 'Firma bola vytvorená z kontaktu', message: 'Firma bola vytvorená z kontaktu',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };

View File

@@ -2,7 +2,6 @@ import * as crmEmailService from '../services/crm-email.service.js';
import * as contactService from '../services/contact.service.js'; import * as contactService from '../services/contact.service.js';
import * as emailAccountService from '../services/email-account.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 { 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'; import { getUserById } from '../services/auth.service.js';
import { logger } from '../utils/logger.js'; import { logger } from '../utils/logger.js';
@@ -10,7 +9,7 @@ import { logger } from '../utils/logger.js';
* Get all emails for authenticated user * Get all emails for authenticated user
* GET /api/emails?accountId=xxx (REQUIRED) * GET /api/emails?accountId=xxx (REQUIRED)
*/ */
export const getEmails = async (req, res) => { export const getEmails = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const { accountId } = req.query; const { accountId } = req.query;
@@ -36,8 +35,7 @@ export const getEmails = async (req, res) => {
data: emails, data: emails,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -45,7 +43,7 @@ export const getEmails = async (req, res) => {
* Get emails by thread (conversation) * Get emails by thread (conversation)
* GET /api/emails/thread/:threadId?accountId=xxx (accountId required) * GET /api/emails/thread/:threadId?accountId=xxx (accountId required)
*/ */
export const getThread = async (req, res) => { export const getThread = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const { threadId } = req.params; const { threadId } = req.params;
@@ -72,8 +70,7 @@ export const getThread = async (req, res) => {
data: thread, data: thread,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -81,7 +78,7 @@ export const getThread = async (req, res) => {
* Search emails * Search emails
* GET /api/emails/search?q=query&accountId=xxx (accountId required) * GET /api/emails/search?q=query&accountId=xxx (accountId required)
*/ */
export const searchEmails = async (req, res) => { export const searchEmails = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const { q, accountId } = req.query; const { q, accountId } = req.query;
@@ -107,8 +104,7 @@ export const searchEmails = async (req, res) => {
data: results, data: results,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -117,7 +113,7 @@ export const searchEmails = async (req, res) => {
* GET /api/emails/unread-count * GET /api/emails/unread-count
* Returns total unread count and per-account counts * Returns total unread count and per-account counts
*/ */
export const getUnreadCount = async (req, res) => { export const getUnreadCount = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
@@ -139,8 +135,7 @@ export const getUnreadCount = async (req, res) => {
}); });
} catch (error) { } catch (error) {
logger.error('ERROR in getUnreadCount', { error: error.message }); logger.error('ERROR in getUnreadCount', { error: error.message });
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -149,7 +144,7 @@ export const getUnreadCount = async (req, res) => {
* POST /api/emails/sync * POST /api/emails/sync
* Body: { accountId } (optional - defaults to primary account) * Body: { accountId } (optional - defaults to primary account)
*/ */
export const syncEmails = async (req, res) => { export const syncEmails = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const { accountId } = req.body; const { accountId } = req.body;
@@ -213,8 +208,7 @@ export const syncEmails = async (req, res) => {
}, },
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -222,7 +216,7 @@ export const syncEmails = async (req, res) => {
* Mark email as read/unread * Mark email as read/unread
* PATCH /api/emails/:jmapId/read?accountId=xxx * PATCH /api/emails/:jmapId/read?accountId=xxx
*/ */
export const markAsRead = async (req, res) => { export const markAsRead = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const { jmapId } = req.params; const { jmapId } = req.params;
@@ -249,8 +243,7 @@ export const markAsRead = async (req, res) => {
message: `Email označený ako ${isRead ? 'prečítaný' : 'neprečítaný'}`, message: `Email označený ako ${isRead ? 'prečítaný' : 'neprečítaný'}`,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -258,7 +251,7 @@ export const markAsRead = async (req, res) => {
* Mark all emails from contact as read * Mark all emails from contact as read
* POST /api/emails/contact/:contactId/read?accountId=xxx * POST /api/emails/contact/:contactId/read?accountId=xxx
*/ */
export const markContactEmailsRead = async (req, res) => { export const markContactEmailsRead = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const { contactId } = req.params; const { contactId } = req.params;
@@ -308,8 +301,7 @@ export const markContactEmailsRead = async (req, res) => {
}); });
} catch (error) { } catch (error) {
logger.error('ERROR in markContactEmailsRead', { error: error.message }); logger.error('ERROR in markContactEmailsRead', { error: error.message });
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -317,7 +309,7 @@ export const markContactEmailsRead = async (req, res) => {
* Mark entire thread as read * Mark entire thread as read
* POST /api/emails/thread/:threadId/read?accountId=xxx * POST /api/emails/thread/:threadId/read?accountId=xxx
*/ */
export const markThreadRead = async (req, res) => { export const markThreadRead = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const { threadId } = req.params; const { threadId } = req.params;
@@ -361,8 +353,7 @@ export const markThreadRead = async (req, res) => {
count, count,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -371,7 +362,7 @@ export const markThreadRead = async (req, res) => {
* POST /api/emails/reply * POST /api/emails/reply
* Body: { to, subject, body, inReplyTo, threadId, accountId } * Body: { to, subject, body, inReplyTo, threadId, accountId }
*/ */
export const replyToEmail = async (req, res) => { export const replyToEmail = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const { to, subject, body, inReplyTo = null, threadId = null, accountId } = req.body; const { to, subject, body, inReplyTo = null, threadId = null, accountId } = req.body;
@@ -414,8 +405,7 @@ export const replyToEmail = async (req, res) => {
data: result, data: result,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -423,7 +413,7 @@ export const replyToEmail = async (req, res) => {
* Get emails for a specific contact * Get emails for a specific contact
* GET /api/emails/contact/:contactId?accountId=xxx * GET /api/emails/contact/:contactId?accountId=xxx
*/ */
export const getContactEmails = async (req, res) => { export const getContactEmails = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const { contactId } = req.params; const { contactId } = req.params;
@@ -450,8 +440,7 @@ export const getContactEmails = async (req, res) => {
data: emails, data: emails,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -460,7 +449,7 @@ export const getContactEmails = async (req, res) => {
* GET /api/emails/search-jmap?query=text&limit=50&offset=0&accountId=xxx * GET /api/emails/search-jmap?query=text&limit=50&offset=0&accountId=xxx
* Searches in: from, to, subject, and email body * Searches in: from, to, subject, and email body
*/ */
export const searchEmailsJMAP = async (req, res) => { export const searchEmailsJMAP = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const { query = '', limit = 50, offset = 0, accountId } = req.query; const { query = '', limit = 50, offset = 0, accountId } = req.query;
@@ -505,7 +494,6 @@ export const searchEmailsJMAP = async (req, res) => {
}); });
} catch (error) { } catch (error) {
logger.error('ERROR in searchEmailsJMAP', { error: error.message }); logger.error('ERROR in searchEmailsJMAP', { error: error.message });
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };

View File

@@ -2,13 +2,12 @@ import * as emailAccountService from '../services/email-account.service.js';
import { import {
logEmailLink, logEmailLink,
} from '../services/audit.service.js'; } from '../services/audit.service.js';
import { formatErrorResponse } from '../utils/errors.js';
/** /**
* Get all email accounts for logged-in user * Get all email accounts for logged-in user
* GET /api/email-accounts * GET /api/email-accounts
*/ */
export const getEmailAccounts = async (req, res) => { export const getEmailAccounts = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const accounts = await emailAccountService.getUserEmailAccounts(userId); const accounts = await emailAccountService.getUserEmailAccounts(userId);
@@ -18,8 +17,7 @@ export const getEmailAccounts = async (req, res) => {
data: accounts, data: accounts,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -27,7 +25,7 @@ export const getEmailAccounts = async (req, res) => {
* Get a specific email account * Get a specific email account
* GET /api/email-accounts/:id * GET /api/email-accounts/:id
*/ */
export const getEmailAccount = async (req, res) => { export const getEmailAccount = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const { id } = req.params; const { id } = req.params;
@@ -39,8 +37,7 @@ export const getEmailAccount = async (req, res) => {
data: account, data: account,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -48,7 +45,7 @@ export const getEmailAccount = async (req, res) => {
* Create a new email account * Create a new email account
* POST /api/email-accounts * POST /api/email-accounts
*/ */
export const createEmailAccount = async (req, res) => { export const createEmailAccount = async (req, res, next) => {
const { email, emailPassword } = req.body; const { email, emailPassword } = req.body;
const userId = req.userId; const userId = req.userId;
const ipAddress = req.ip || req.connection.remoteAddress; const ipAddress = req.ip || req.connection.remoteAddress;
@@ -70,8 +67,7 @@ export const createEmailAccount = async (req, res) => {
message: 'Email účet úspešne pripojený', message: 'Email účet úspešne pripojený',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -79,7 +75,7 @@ export const createEmailAccount = async (req, res) => {
* Update email account password * Update email account password
* PATCH /api/email-accounts/:id/password * PATCH /api/email-accounts/:id/password
*/ */
export const updateEmailAccountPassword = async (req, res) => { export const updateEmailAccountPassword = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const { id } = req.params; const { id } = req.params;
@@ -97,8 +93,7 @@ export const updateEmailAccountPassword = async (req, res) => {
message: 'Heslo k emailovému účtu bolo aktualizované', message: 'Heslo k emailovému účtu bolo aktualizované',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -106,7 +101,7 @@ export const updateEmailAccountPassword = async (req, res) => {
* Toggle email account active status * Toggle email account active status
* PATCH /api/email-accounts/:id/status * PATCH /api/email-accounts/:id/status
*/ */
export const toggleEmailAccountStatus = async (req, res) => { export const toggleEmailAccountStatus = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const { id } = req.params; const { id } = req.params;
@@ -124,8 +119,7 @@ export const toggleEmailAccountStatus = async (req, res) => {
message: `Email účet ${isActive ? 'aktivovaný' : 'deaktivovaný'}`, message: `Email účet ${isActive ? 'aktivovaný' : 'deaktivovaný'}`,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -133,7 +127,7 @@ export const toggleEmailAccountStatus = async (req, res) => {
* Set email account as primary * Set email account as primary
* POST /api/email-accounts/:id/set-primary * POST /api/email-accounts/:id/set-primary
*/ */
export const setPrimaryEmailAccount = async (req, res) => { export const setPrimaryEmailAccount = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const { id } = req.params; const { id } = req.params;
@@ -146,8 +140,7 @@ export const setPrimaryEmailAccount = async (req, res) => {
message: 'Primárny email účet bol nastavený', message: 'Primárny email účet bol nastavený',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -155,7 +148,7 @@ export const setPrimaryEmailAccount = async (req, res) => {
* Delete email account * Delete email account
* DELETE /api/email-accounts/:id * DELETE /api/email-accounts/:id
*/ */
export const deleteEmailAccount = async (req, res) => { export const deleteEmailAccount = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const { id } = req.params; const { id } = req.params;
@@ -168,7 +161,6 @@ export const deleteEmailAccount = async (req, res) => {
message: result.message, message: result.message,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };

View File

@@ -1,11 +1,10 @@
import * as noteService from '../services/note.service.js'; import * as noteService from '../services/note.service.js';
import { formatErrorResponse } from '../utils/errors.js';
/** /**
* Get all notes * Get all notes
* GET /api/notes?search=query&companyId=xxx&projectId=xxx&todoId=xxx&contactId=xxx * GET /api/notes?search=query&companyId=xxx&projectId=xxx&todoId=xxx&contactId=xxx
*/ */
export const getAllNotes = async (req, res) => { export const getAllNotes = async (req, res, next) => {
try { try {
const { search, companyId, projectId, todoId, contactId } = req.query; const { search, companyId, projectId, todoId, contactId } = req.query;
@@ -25,8 +24,7 @@ export const getAllNotes = async (req, res) => {
data: notes, data: notes,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -34,7 +32,7 @@ export const getAllNotes = async (req, res) => {
* Get note by ID * Get note by ID
* GET /api/notes/:noteId * GET /api/notes/:noteId
*/ */
export const getNoteById = async (req, res) => { export const getNoteById = async (req, res, next) => {
try { try {
const { noteId } = req.params; const { noteId } = req.params;
@@ -45,8 +43,7 @@ export const getNoteById = async (req, res) => {
data: note, data: note,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -55,7 +52,7 @@ export const getNoteById = async (req, res) => {
* POST /api/notes * POST /api/notes
* Body: { title, content, companyId, projectId, todoId, contactId } * Body: { title, content, companyId, projectId, todoId, contactId }
*/ */
export const createNote = async (req, res) => { export const createNote = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const data = req.body; const data = req.body;
@@ -68,8 +65,7 @@ export const createNote = async (req, res) => {
message: 'Poznámka bola vytvorená', message: 'Poznámka bola vytvorená',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -78,7 +74,7 @@ export const createNote = async (req, res) => {
* PATCH /api/notes/:noteId * PATCH /api/notes/:noteId
* Body: { title, content, companyId, projectId, todoId, contactId } * Body: { title, content, companyId, projectId, todoId, contactId }
*/ */
export const updateNote = async (req, res) => { export const updateNote = async (req, res, next) => {
try { try {
const { noteId } = req.params; const { noteId } = req.params;
const data = req.body; const data = req.body;
@@ -91,8 +87,7 @@ export const updateNote = async (req, res) => {
message: 'Poznámka bola aktualizovaná', message: 'Poznámka bola aktualizovaná',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -100,7 +95,7 @@ export const updateNote = async (req, res) => {
* Delete note * Delete note
* DELETE /api/notes/:noteId * DELETE /api/notes/:noteId
*/ */
export const deleteNote = async (req, res) => { export const deleteNote = async (req, res, next) => {
try { try {
const { noteId } = req.params; const { noteId } = req.params;
@@ -111,8 +106,7 @@ export const deleteNote = async (req, res) => {
message: result.message, message: result.message,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -120,7 +114,7 @@ export const deleteNote = async (req, res) => {
* Get upcoming reminders for current user * Get upcoming reminders for current user
* GET /api/notes/my-reminders * GET /api/notes/my-reminders
*/ */
export const getMyReminders = async (req, res) => { export const getMyReminders = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
@@ -132,8 +126,7 @@ export const getMyReminders = async (req, res) => {
data: reminders, data: reminders,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -141,7 +134,7 @@ export const getMyReminders = async (req, res) => {
* Mark reminder as sent * Mark reminder as sent
* POST /api/notes/:noteId/mark-reminder-sent * POST /api/notes/:noteId/mark-reminder-sent
*/ */
export const markReminderSent = async (req, res) => { export const markReminderSent = async (req, res, next) => {
try { try {
const { noteId } = req.params; const { noteId } = req.params;
@@ -153,7 +146,6 @@ export const markReminderSent = async (req, res) => {
message: 'Reminder označený ako odoslaný', message: 'Reminder označený ako odoslaný',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };

View File

@@ -1,12 +1,11 @@
import * as projectService from '../services/project.service.js'; import * as projectService from '../services/project.service.js';
import * as noteService from '../services/note.service.js'; import * as noteService from '../services/note.service.js';
import { formatErrorResponse } from '../utils/errors.js';
/** /**
* Get all projects * Get all projects
* GET /api/projects?search=query&companyId=xxx * GET /api/projects?search=query&companyId=xxx
*/ */
export const getAllProjects = async (req, res) => { export const getAllProjects = async (req, res, next) => {
try { try {
const { search, companyId } = req.query; const { search, companyId } = req.query;
@@ -18,8 +17,7 @@ export const getAllProjects = async (req, res) => {
data: projects, data: projects,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -27,7 +25,7 @@ export const getAllProjects = async (req, res) => {
* Get project by ID * Get project by ID
* GET /api/projects/:projectId * GET /api/projects/:projectId
*/ */
export const getProjectById = async (req, res) => { export const getProjectById = async (req, res, next) => {
try { try {
const { projectId } = req.params; const { projectId } = req.params;
@@ -38,8 +36,7 @@ export const getProjectById = async (req, res) => {
data: project, data: project,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -47,7 +44,7 @@ export const getProjectById = async (req, res) => {
* Get project with relations (company, todos, notes, timesheets) * Get project with relations (company, todos, notes, timesheets)
* GET /api/projects/:projectId/details * GET /api/projects/:projectId/details
*/ */
export const getProjectWithRelations = async (req, res) => { export const getProjectWithRelations = async (req, res, next) => {
try { try {
const { projectId } = req.params; const { projectId } = req.params;
@@ -58,8 +55,7 @@ export const getProjectWithRelations = async (req, res) => {
data: project, data: project,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -68,7 +64,7 @@ export const getProjectWithRelations = async (req, res) => {
* POST /api/projects * POST /api/projects
* Body: { name, description, companyId, status, startDate, endDate } * Body: { name, description, companyId, status, startDate, endDate }
*/ */
export const createProject = async (req, res) => { export const createProject = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const data = req.body; const data = req.body;
@@ -81,8 +77,7 @@ export const createProject = async (req, res) => {
message: 'Projekt bol vytvorený', message: 'Projekt bol vytvorený',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -91,7 +86,7 @@ export const createProject = async (req, res) => {
* PATCH /api/projects/:projectId * PATCH /api/projects/:projectId
* Body: { name, description, companyId, status, startDate, endDate } * Body: { name, description, companyId, status, startDate, endDate }
*/ */
export const updateProject = async (req, res) => { export const updateProject = async (req, res, next) => {
try { try {
const { projectId } = req.params; const { projectId } = req.params;
const data = req.body; const data = req.body;
@@ -104,8 +99,7 @@ export const updateProject = async (req, res) => {
message: 'Projekt bol aktualizovaný', message: 'Projekt bol aktualizovaný',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -113,7 +107,7 @@ export const updateProject = async (req, res) => {
* Delete project * Delete project
* DELETE /api/projects/:projectId * DELETE /api/projects/:projectId
*/ */
export const deleteProject = async (req, res) => { export const deleteProject = async (req, res, next) => {
try { try {
const { projectId } = req.params; const { projectId } = req.params;
@@ -124,8 +118,7 @@ export const deleteProject = async (req, res) => {
message: result.message, message: result.message,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -133,7 +126,7 @@ export const deleteProject = async (req, res) => {
* Get project notes * Get project notes
* GET /api/projects/:projectId/notes * GET /api/projects/:projectId/notes
*/ */
export const getProjectNotes = async (req, res) => { export const getProjectNotes = async (req, res, next) => {
try { try {
const { projectId } = req.params; const { projectId } = req.params;
@@ -145,8 +138,7 @@ export const getProjectNotes = async (req, res) => {
data: notes, data: notes,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -154,7 +146,7 @@ export const getProjectNotes = async (req, res) => {
* Add project note * Add project note
* POST /api/projects/:projectId/notes * POST /api/projects/:projectId/notes
*/ */
export const addProjectNote = async (req, res) => { export const addProjectNote = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const { projectId } = req.params; const { projectId } = req.params;
@@ -172,8 +164,7 @@ export const addProjectNote = async (req, res) => {
message: 'Poznámka bola pridaná', message: 'Poznámka bola pridaná',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -181,7 +172,7 @@ export const addProjectNote = async (req, res) => {
* Update project note * Update project note
* PATCH /api/projects/:projectId/notes/:noteId * PATCH /api/projects/:projectId/notes/:noteId
*/ */
export const updateProjectNote = async (req, res) => { export const updateProjectNote = async (req, res, next) => {
try { try {
const { noteId } = req.params; const { noteId } = req.params;
const { content, reminderAt } = req.body; const { content, reminderAt } = req.body;
@@ -197,8 +188,7 @@ export const updateProjectNote = async (req, res) => {
message: 'Poznámka bola aktualizovaná', message: 'Poznámka bola aktualizovaná',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -206,7 +196,7 @@ export const updateProjectNote = async (req, res) => {
* Delete project note * Delete project note
* DELETE /api/projects/:projectId/notes/:noteId * DELETE /api/projects/:projectId/notes/:noteId
*/ */
export const deleteProjectNote = async (req, res) => { export const deleteProjectNote = async (req, res, next) => {
try { try {
const { noteId } = req.params; const { noteId } = req.params;
@@ -217,8 +207,7 @@ export const deleteProjectNote = async (req, res) => {
message: result.message, message: result.message,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -226,7 +215,7 @@ export const deleteProjectNote = async (req, res) => {
* Get project users (team members) * Get project users (team members)
* GET /api/projects/:projectId/users * GET /api/projects/:projectId/users
*/ */
export const getProjectUsers = async (req, res) => { export const getProjectUsers = async (req, res, next) => {
try { try {
const { projectId } = req.params; const { projectId } = req.params;
@@ -238,8 +227,7 @@ export const getProjectUsers = async (req, res) => {
data: users, data: users,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -248,7 +236,7 @@ export const getProjectUsers = async (req, res) => {
* POST /api/projects/:projectId/users * POST /api/projects/:projectId/users
* Body: { userId, role } * Body: { userId, role }
*/ */
export const assignUserToProject = async (req, res) => { export const assignUserToProject = async (req, res, next) => {
try { try {
const currentUserId = req.userId; const currentUserId = req.userId;
const { projectId } = req.params; const { projectId } = req.params;
@@ -267,8 +255,7 @@ export const assignUserToProject = async (req, res) => {
message: 'Používateľ bol priradený k projektu', message: 'Používateľ bol priradený k projektu',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -276,7 +263,7 @@ export const assignUserToProject = async (req, res) => {
* Remove user from project * Remove user from project
* DELETE /api/projects/:projectId/users/:userId * DELETE /api/projects/:projectId/users/:userId
*/ */
export const removeUserFromProject = async (req, res) => { export const removeUserFromProject = async (req, res, next) => {
try { try {
const { projectId, userId } = req.params; const { projectId, userId } = req.params;
@@ -287,8 +274,7 @@ export const removeUserFromProject = async (req, res) => {
message: result.message, message: result.message,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -297,7 +283,7 @@ export const removeUserFromProject = async (req, res) => {
* PATCH /api/projects/:projectId/users/:userId * PATCH /api/projects/:projectId/users/:userId
* Body: { role } * Body: { role }
*/ */
export const updateUserRoleOnProject = async (req, res) => { export const updateUserRoleOnProject = async (req, res, next) => {
try { try {
const { projectId, userId } = req.params; const { projectId, userId } = req.params;
const { role } = req.body; const { role } = req.body;
@@ -310,7 +296,6 @@ export const updateUserRoleOnProject = async (req, res) => {
message: 'Rola používateľa bola aktualizovaná', message: 'Rola používateľa bola aktualizovaná',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };

View File

@@ -1,11 +1,10 @@
import * as timeTrackingService from '../services/time-tracking.service.js'; import * as timeTrackingService from '../services/time-tracking.service.js';
import { formatErrorResponse } from '../utils/errors.js';
/** /**
* Start a new time entry * Start a new time entry
* POST /api/time-tracking/start * POST /api/time-tracking/start
*/ */
export const startTimeEntry = async (req, res) => { export const startTimeEntry = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const { projectId, todoId, companyId, description } = req.body; const { projectId, todoId, companyId, description } = req.body;
@@ -23,8 +22,7 @@ export const startTimeEntry = async (req, res) => {
message: 'Časovač bol spustený', message: 'Časovač bol spustený',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -32,7 +30,7 @@ export const startTimeEntry = async (req, res) => {
* Stop a running time entry * Stop a running time entry
* POST /api/time-tracking/:entryId/stop * POST /api/time-tracking/:entryId/stop
*/ */
export const stopTimeEntry = async (req, res) => { export const stopTimeEntry = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const { entryId } = req.params; const { entryId } = req.params;
@@ -51,8 +49,7 @@ export const stopTimeEntry = async (req, res) => {
message: 'Časovač bol zastavený', message: 'Časovač bol zastavený',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -60,7 +57,7 @@ export const stopTimeEntry = async (req, res) => {
* Get running time entry for current user * Get running time entry for current user
* GET /api/time-tracking/running * GET /api/time-tracking/running
*/ */
export const getRunningTimeEntry = async (req, res) => { export const getRunningTimeEntry = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
@@ -71,8 +68,24 @@ export const getRunningTimeEntry = async (req, res) => {
data: entry, data: entry,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse); }
};
/**
* Get all running time entries (for dashboard)
* GET /api/time-tracking/running-all
*/
export const getAllRunningTimeEntries = async (req, res, next) => {
try {
const entries = await timeTrackingService.getAllRunningTimeEntries();
res.status(200).json({
success: true,
data: entries,
});
} catch (error) {
return next(error);
} }
}; };
@@ -80,7 +93,7 @@ export const getRunningTimeEntry = async (req, res) => {
* Get all time entries for current user with filters * Get all time entries for current user with filters
* GET /api/time-tracking?projectId=xxx&todoId=xxx&companyId=xxx&startDate=xxx&endDate=xxx * GET /api/time-tracking?projectId=xxx&todoId=xxx&companyId=xxx&startDate=xxx&endDate=xxx
*/ */
export const getAllTimeEntries = async (req, res) => { export const getAllTimeEntries = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const { projectId, todoId, companyId, startDate, endDate } = req.query; const { projectId, todoId, companyId, startDate, endDate } = req.query;
@@ -101,8 +114,7 @@ export const getAllTimeEntries = async (req, res) => {
data: entries, data: entries,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -110,7 +122,7 @@ export const getAllTimeEntries = async (req, res) => {
* Get time entries for a specific month * Get time entries for a specific month
* GET /api/time-tracking/month/:year/:month * GET /api/time-tracking/month/:year/:month
*/ */
export const getMonthlyTimeEntries = async (req, res) => { export const getMonthlyTimeEntries = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const userRole = req.user.role; const userRole = req.user.role;
@@ -129,8 +141,7 @@ export const getMonthlyTimeEntries = async (req, res) => {
data: entries, data: entries,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -138,7 +149,7 @@ export const getMonthlyTimeEntries = async (req, res) => {
* Generate timesheet file for a month * Generate timesheet file for a month
* POST /api/time-tracking/month/:year/:month/generate * POST /api/time-tracking/month/:year/:month/generate
*/ */
export const generateMonthlyTimesheet = async (req, res) => { export const generateMonthlyTimesheet = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const userRole = req.user.role; const userRole = req.user.role;
@@ -157,8 +168,7 @@ export const generateMonthlyTimesheet = async (req, res) => {
message: 'Timesheet bol vygenerovaný', message: 'Timesheet bol vygenerovaný',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -166,7 +176,7 @@ export const generateMonthlyTimesheet = async (req, res) => {
* Get time entry by ID * Get time entry by ID
* GET /api/time-tracking/:entryId * GET /api/time-tracking/:entryId
*/ */
export const getTimeEntryById = async (req, res) => { export const getTimeEntryById = async (req, res, next) => {
try { try {
const { entryId } = req.params; const { entryId } = req.params;
@@ -177,8 +187,7 @@ export const getTimeEntryById = async (req, res) => {
data: entry, data: entry,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -186,7 +195,7 @@ export const getTimeEntryById = async (req, res) => {
* Get time entry with related data * Get time entry with related data
* GET /api/time-tracking/:entryId/details * GET /api/time-tracking/:entryId/details
*/ */
export const getTimeEntryWithRelations = async (req, res) => { export const getTimeEntryWithRelations = async (req, res, next) => {
try { try {
const { entryId } = req.params; const { entryId } = req.params;
@@ -197,8 +206,7 @@ export const getTimeEntryWithRelations = async (req, res) => {
data: entry, data: entry,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -206,7 +214,7 @@ export const getTimeEntryWithRelations = async (req, res) => {
* Update time entry * Update time entry
* PATCH /api/time-tracking/:entryId * PATCH /api/time-tracking/:entryId
*/ */
export const updateTimeEntry = async (req, res) => { export const updateTimeEntry = async (req, res, next) => {
try { try {
const { entryId } = req.params; const { entryId } = req.params;
const { startTime, endTime, projectId, todoId, companyId, description } = req.body; const { startTime, endTime, projectId, todoId, companyId, description } = req.body;
@@ -229,8 +237,7 @@ export const updateTimeEntry = async (req, res) => {
message: 'Záznam bol aktualizovaný', message: 'Záznam bol aktualizovaný',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -238,7 +245,7 @@ export const updateTimeEntry = async (req, res) => {
* Delete time entry * Delete time entry
* DELETE /api/time-tracking/:entryId * DELETE /api/time-tracking/:entryId
*/ */
export const deleteTimeEntry = async (req, res) => { export const deleteTimeEntry = async (req, res, next) => {
try { try {
const { entryId } = req.params; const { entryId } = req.params;
@@ -249,8 +256,7 @@ export const deleteTimeEntry = async (req, res) => {
res.status(200).json(result); res.status(200).json(result);
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -258,7 +264,7 @@ export const deleteTimeEntry = async (req, res) => {
* Get monthly statistics * Get monthly statistics
* GET /api/time-tracking/stats/monthly/:year/:month * GET /api/time-tracking/stats/monthly/:year/:month
*/ */
export const getMonthlyStats = async (req, res) => { export const getMonthlyStats = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const userRole = req.user.role; const userRole = req.user.role;
@@ -276,7 +282,6 @@ export const getMonthlyStats = async (req, res) => {
data: stats, data: stats,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };

View File

@@ -1,11 +1,11 @@
import * as timesheetService from '../services/timesheet.service.js'; import * as timesheetService from '../services/timesheet.service.js';
import { formatErrorResponse } from '../utils/errors.js'; import { ForbiddenError } from '../utils/errors.js';
/** /**
* Upload timesheet * Upload timesheet
* POST /api/timesheets/upload * POST /api/timesheets/upload
*/ */
export const uploadTimesheet = async (req, res) => { export const uploadTimesheet = async (req, res, next) => {
try { try {
const { year, month, userId: requestUserId } = req.body; const { year, month, userId: requestUserId } = req.body;
@@ -15,11 +15,7 @@ export const uploadTimesheet = async (req, res) => {
let targetUserId = req.userId; let targetUserId = req.userId;
if (requestUserId) { if (requestUserId) {
if (req.user.role !== 'admin') { if (req.user.role !== 'admin') {
const errorResponse = formatErrorResponse( throw new ForbiddenError('Iba admin môže nahrávať timesheets za iných používateľov');
new Error('Iba admin môže nahrávať timesheets za iných používateľov'),
process.env.NODE_ENV === 'development'
);
return res.status(403).json(errorResponse);
} }
targetUserId = requestUserId; targetUserId = requestUserId;
} }
@@ -37,8 +33,7 @@ export const uploadTimesheet = async (req, res) => {
message: 'Timesheet bol úspešne nahraný', message: 'Timesheet bol úspešne nahraný',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -46,7 +41,7 @@ export const uploadTimesheet = async (req, res) => {
* Get user's timesheets (with optional filters) * Get user's timesheets (with optional filters)
* GET /api/timesheets/my * GET /api/timesheets/my
*/ */
export const getMyTimesheets = async (req, res) => { export const getMyTimesheets = async (req, res, next) => {
try { try {
const { year, month } = req.query; const { year, month } = req.query;
@@ -60,8 +55,7 @@ export const getMyTimesheets = async (req, res) => {
}, },
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -69,7 +63,7 @@ export const getMyTimesheets = async (req, res) => {
* Get all users' timesheets (admin only) - grouped by user * Get all users' timesheets (admin only) - grouped by user
* GET /api/timesheets/all * GET /api/timesheets/all
*/ */
export const getAllTimesheets = async (req, res) => { export const getAllTimesheets = async (req, res, next) => {
try { try {
const { userId, year, month } = req.query; const { userId, year, month } = req.query;
@@ -83,8 +77,7 @@ export const getAllTimesheets = async (req, res) => {
}, },
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -92,7 +85,7 @@ export const getAllTimesheets = async (req, res) => {
* Download timesheet file * Download timesheet file
* GET /api/timesheets/:timesheetId/download * GET /api/timesheets/:timesheetId/download
*/ */
export const downloadTimesheet = async (req, res) => { export const downloadTimesheet = async (req, res, next) => {
try { try {
const { timesheetId } = req.params; const { timesheetId } = req.params;
const { filePath, fileName } = await timesheetService.getDownloadInfo(timesheetId, { const { filePath, fileName } = await timesheetService.getDownloadInfo(timesheetId, {
@@ -102,8 +95,7 @@ export const downloadTimesheet = async (req, res) => {
res.download(filePath, fileName); res.download(filePath, fileName);
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -111,7 +103,7 @@ export const downloadTimesheet = async (req, res) => {
* Delete timesheet * Delete timesheet
* DELETE /api/timesheets/:timesheetId * DELETE /api/timesheets/:timesheetId
*/ */
export const deleteTimesheet = async (req, res) => { export const deleteTimesheet = async (req, res, next) => {
try { try {
const { timesheetId } = req.params; const { timesheetId } = req.params;
@@ -125,7 +117,6 @@ export const deleteTimesheet = async (req, res) => {
message: 'Timesheet bol zmazaný', message: 'Timesheet bol zmazaný',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };

View File

@@ -1,11 +1,10 @@
import * as todoService from '../services/todo.service.js'; import * as todoService from '../services/todo.service.js';
import { formatErrorResponse } from '../utils/errors.js';
/** /**
* Get all todos * Get all todos
* GET /api/todos?search=query&projectId=xxx&companyId=xxx&assignedTo=xxx&status=xxx * GET /api/todos?search=query&projectId=xxx&companyId=xxx&assignedTo=xxx&status=xxx
*/ */
export const getAllTodos = async (req, res) => { export const getAllTodos = async (req, res, next) => {
try { try {
const { search, projectId, companyId, assignedTo, status, completed, priority } = req.query; const { search, projectId, companyId, assignedTo, status, completed, priority } = req.query;
@@ -32,8 +31,7 @@ export const getAllTodos = async (req, res) => {
data: todos, data: todos,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -41,7 +39,7 @@ export const getAllTodos = async (req, res) => {
* Get my todos (assigned to current user) * Get my todos (assigned to current user)
* GET /api/todos/my?status=xxx * GET /api/todos/my?status=xxx
*/ */
export const getMyTodos = async (req, res) => { export const getMyTodos = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const { status } = req.query; const { status } = req.query;
@@ -59,8 +57,7 @@ export const getMyTodos = async (req, res) => {
data: todos, data: todos,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -68,7 +65,7 @@ export const getMyTodos = async (req, res) => {
* Get todo by ID * Get todo by ID
* GET /api/todos/:todoId * GET /api/todos/:todoId
*/ */
export const getTodoById = async (req, res) => { export const getTodoById = async (req, res, next) => {
try { try {
const { todoId } = req.params; const { todoId } = req.params;
@@ -79,8 +76,7 @@ export const getTodoById = async (req, res) => {
data: todo, data: todo,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -88,7 +84,7 @@ export const getTodoById = async (req, res) => {
* Get todo with relations (project, company, assigned user, notes) * Get todo with relations (project, company, assigned user, notes)
* GET /api/todos/:todoId/details * GET /api/todos/:todoId/details
*/ */
export const getTodoWithRelations = async (req, res) => { export const getTodoWithRelations = async (req, res, next) => {
try { try {
const { todoId } = req.params; const { todoId } = req.params;
@@ -99,8 +95,7 @@ export const getTodoWithRelations = async (req, res) => {
data: todo, data: todo,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -109,7 +104,7 @@ export const getTodoWithRelations = async (req, res) => {
* POST /api/todos * POST /api/todos
* Body: { title, description, projectId, companyId, assignedUserIds, status, priority, dueDate } * Body: { title, description, projectId, companyId, assignedUserIds, status, priority, dueDate }
*/ */
export const createTodo = async (req, res) => { export const createTodo = async (req, res, next) => {
try { try {
const userId = req.userId; const userId = req.userId;
const data = req.body; const data = req.body;
@@ -123,8 +118,7 @@ export const createTodo = async (req, res) => {
message: 'Todo bolo vytvorené', message: 'Todo bolo vytvorené',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -133,7 +127,7 @@ export const createTodo = async (req, res) => {
* PATCH /api/todos/:todoId * PATCH /api/todos/:todoId
* Body: { title, description, projectId, companyId, assignedUserIds, status, priority, dueDate } * Body: { title, description, projectId, companyId, assignedUserIds, status, priority, dueDate }
*/ */
export const updateTodo = async (req, res) => { export const updateTodo = async (req, res, next) => {
try { try {
const { todoId } = req.params; const { todoId } = req.params;
const data = req.body; const data = req.body;
@@ -147,8 +141,7 @@ export const updateTodo = async (req, res) => {
message: 'Todo bolo aktualizované', message: 'Todo bolo aktualizované',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -156,7 +149,7 @@ export const updateTodo = async (req, res) => {
* Delete todo * Delete todo
* DELETE /api/todos/:todoId * DELETE /api/todos/:todoId
*/ */
export const deleteTodo = async (req, res) => { export const deleteTodo = async (req, res, next) => {
try { try {
const { todoId } = req.params; const { todoId } = req.params;
@@ -167,8 +160,7 @@ export const deleteTodo = async (req, res) => {
message: result.message, message: result.message,
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };
@@ -176,7 +168,7 @@ export const deleteTodo = async (req, res) => {
* Toggle todo completion status * Toggle todo completion status
* PATCH /api/todos/:todoId/toggle * PATCH /api/todos/:todoId/toggle
*/ */
export const toggleTodo = async (req, res) => { export const toggleTodo = async (req, res, next) => {
try { try {
const { todoId } = req.params; const { todoId } = req.params;
@@ -194,7 +186,6 @@ export const toggleTodo = async (req, res) => {
message: 'Todo status aktualizovaný', message: 'Todo status aktualizovaný',
}); });
} catch (error) { } catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); return next(error);
res.status(error.statusCode || 500).json(errorResponse);
} }
}; };

View File

@@ -48,12 +48,6 @@ export const authenticate = async (req, res, next) => {
}); });
} }
return res.status(401).json({ return next(error);
success: false,
error: {
message: 'Neplatný alebo expirovaný token',
statusCode: 401,
},
});
} }
}; };

View File

@@ -2,11 +2,19 @@ import { formatErrorResponse } from '../../utils/errors.js';
import { logger } from '../../utils/logger.js'; import { logger } from '../../utils/logger.js';
export function errorHandler(err, req, res, next) { export function errorHandler(err, req, res, next) {
if (res.headersSent) {
return next(err);
}
// Log error // Log error
logger.error('Unhandled error', err); logger.error('Unhandled error', err);
// Get status code // Get status code
const statusCode = err.statusCode || res.statusCode !== 200 ? res.statusCode : 500; const statusCode = typeof err.statusCode === 'number'
? err.statusCode
: res.statusCode >= 400
? res.statusCode
: 500;
// Format error response // Format error response
const errorResponse = formatErrorResponse(err, process.env.NODE_ENV === 'development'); const errorResponse = formatErrorResponse(err, process.env.NODE_ENV === 'development');

View File

@@ -1,4 +1,5 @@
import jwt from 'jsonwebtoken'; import jwt from 'jsonwebtoken';
import { AuthenticationError } from './errors.js';
/** /**
* Generuje access JWT token * Generuje access JWT token
@@ -33,10 +34,10 @@ export const verifyAccessToken = (token) => {
return jwt.verify(token, process.env.JWT_SECRET); return jwt.verify(token, process.env.JWT_SECRET);
} catch (error) { } catch (error) {
if (error.name === 'TokenExpiredError') { if (error.name === 'TokenExpiredError') {
throw new Error('Token expiroval'); throw new AuthenticationError('Token expiroval');
} }
if (error.name === 'JsonWebTokenError') { if (error.name === 'JsonWebTokenError') {
throw new Error('Neplatný token'); throw new AuthenticationError('Neplatný token');
} }
throw error; throw error;
} }
@@ -53,10 +54,10 @@ export const verifyRefreshToken = (token) => {
return jwt.verify(token, process.env.JWT_REFRESH_SECRET); return jwt.verify(token, process.env.JWT_REFRESH_SECRET);
} catch (error) { } catch (error) {
if (error.name === 'TokenExpiredError') { if (error.name === 'TokenExpiredError') {
throw new Error('Refresh token expiroval'); throw new AuthenticationError('Refresh token expiroval');
} }
if (error.name === 'JsonWebTokenError') { if (error.name === 'JsonWebTokenError') {
throw new Error('Neplatný refresh token'); throw new AuthenticationError('Neplatný refresh token');
} }
throw error; throw error;
} }