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 { users } from '../db/schema.js';
import { eq } from 'drizzle-orm';
import { users, userEmailAccounts, emailAccounts } from '../db/schema.js';
import { eq, inArray } 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';
import { ConflictError, NotFoundError } from '../utils/errors.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
* 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 adminId = req.userId;
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.',
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
return next(error);
}
};
@@ -109,7 +108,7 @@ export const createUser = async (req, res) => {
* Zoznam všetkých userov (admin only)
* GET /api/admin/users
*/
export const getAllUsers = async (req, res) => {
export const getAllUsers = async (req, res, next) => {
try {
const allUsers = await db
.select({
@@ -130,8 +129,7 @@ export const getAllUsers = async (req, res) => {
data: allUsers,
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
return next(error);
}
};
@@ -139,7 +137,7 @@ export const getAllUsers = async (req, res) => {
* Získanie konkrétneho usera (admin only)
* GET /api/admin/users/:userId
*/
export const getUser = async (req, res) => {
export const getUser = async (req, res, next) => {
const { userId } = req.params;
try {
@@ -176,8 +174,7 @@ export const getUser = async (req, res) => {
},
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
return next(error);
}
};
@@ -185,7 +182,7 @@ export const getUser = async (req, res) => {
* Zmena role usera (admin only)
* PATCH /api/admin/users/:userId/role
*/
export const changeUserRole = async (req, res) => {
export const changeUserRole = async (req, res, next) => {
const { userId } = req.params;
const { role } = req.body;
const adminId = req.userId;
@@ -228,8 +225,7 @@ export const changeUserRole = async (req, res) => {
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);
return next(error);
}
};
@@ -237,7 +233,7 @@ export const changeUserRole = async (req, res) => {
* Zmazanie usera (admin only)
* DELETE /api/admin/users/:userId
*/
export const deleteUser = async (req, res) => {
export const deleteUser = async (req, res, next) => {
const { userId } = req.params;
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));
// 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({
success: true,
message: 'Používateľ bol zmazaný',
deletedEmailAccounts,
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
return next(error);
}
};

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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