feat: Add comprehensive audit logging system

- Add audit logging for contacts (link company, create company from contact)
- Add audit logging for notes (create, update, delete)
- Add audit logging for companies (update, user assign/remove, reminder CRUD)
- Add audit logging for projects (update, user assign/remove)
- Add audit logging for todos (update, uncomplete)
- Add audit logging for time entries (update, delete)
- Add audit logging for timesheets (upload, delete)
- Add audit logging for user deletion
- Add pagination and filters to audit logs API (userId, action, resource, dateFrom, dateTo)
- Add endpoints for distinct actions and resources

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
richardtekula
2025-12-17 07:19:40 +01:00
parent 548a8effdb
commit 0585e51b25
13 changed files with 615 additions and 22 deletions

View File

@@ -2,7 +2,16 @@ 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 { logCompanyCreated, logCompanyDeleted } from '../services/audit.service.js';
import {
logCompanyCreated,
logCompanyDeleted,
logCompanyUpdated,
logCompanyUserAssigned,
logCompanyUserRemoved,
logCompanyReminderCreated,
logCompanyReminderUpdated,
logCompanyReminderDeleted,
} from '../services/audit.service.js';
/**
* Get all companies
@@ -138,11 +147,25 @@ export const createCompany = async (req, res, next) => {
*/
export const updateCompany = async (req, res, next) => {
try {
const userId = req.userId;
const { companyId } = req.params;
const data = req.body;
// Get old company for audit
const oldCompany = await companyService.getCompanyById(companyId);
const company = await companyService.updateCompany(companyId, data);
// Log audit event
await logCompanyUpdated(
userId,
companyId,
{ name: oldCompany.name },
{ name: company.name },
req.ip,
req.headers['user-agent']
);
res.status(200).json({
success: true,
data: company,
@@ -291,11 +314,15 @@ export const getCompanyReminders = async (req, res, next) => {
export const createCompanyReminder = async (req, res, next) => {
try {
const userId = req.userId;
const { companyId } = req.params;
const { description, dueDate, isChecked } = req.body;
const reminder = await companyReminderService.createReminder(companyId, { description, dueDate, isChecked });
// Log audit event
await logCompanyReminderCreated(userId, reminder.id, companyId, dueDate, req.ip, req.headers['user-agent']);
res.status(201).json({
success: true,
data: reminder,
@@ -308,11 +335,18 @@ export const createCompanyReminder = async (req, res, next) => {
export const updateCompanyReminder = async (req, res, next) => {
try {
const userId = req.userId;
const { companyId, reminderId } = req.params;
const { description, dueDate, isChecked } = req.body;
// Get old reminder for audit
const oldReminder = await companyReminderService.getReminderById(reminderId);
const reminder = await companyReminderService.updateReminder(companyId, reminderId, { description, dueDate, isChecked });
// Log audit event
await logCompanyReminderUpdated(userId, reminderId, companyId, oldReminder?.dueDate, dueDate, req.ip, req.headers['user-agent']);
res.status(200).json({
success: true,
data: reminder,
@@ -325,10 +359,17 @@ export const updateCompanyReminder = async (req, res, next) => {
export const deleteCompanyReminder = async (req, res, next) => {
try {
const userId = req.userId;
const { companyId, reminderId } = req.params;
// Get reminder for audit before deletion
const reminder = await companyReminderService.getReminderById(reminderId);
const result = await companyReminderService.deleteReminder(companyId, reminderId);
// Log audit event
await logCompanyReminderDeleted(userId, reminderId, companyId, reminder?.dueDate, req.ip, req.headers['user-agent']);
res.status(200).json({
success: true,
message: result.message,
@@ -412,8 +453,21 @@ export const assignUserToCompany = async (req, res, next) => {
const { companyId } = req.params;
const { userId, role } = req.body;
// Get company name for audit
const company = await companyService.getCompanyById(companyId);
const assignment = await companyService.assignUserToCompany(companyId, userId, currentUserId, role);
// Log audit event
await logCompanyUserAssigned(
currentUserId,
companyId,
userId,
company.name,
req.ip,
req.headers['user-agent']
);
res.status(201).json({
success: true,
data: assignment,
@@ -430,10 +484,24 @@ export const assignUserToCompany = async (req, res, next) => {
*/
export const removeUserFromCompany = async (req, res, next) => {
try {
const currentUserId = req.userId;
const { companyId, userId } = req.params;
// Get company name for audit
const company = await companyService.getCompanyById(companyId);
const result = await companyService.removeUserFromCompany(companyId, userId);
// Log audit event
await logCompanyUserRemoved(
currentUserId,
companyId,
userId,
company.name,
req.ip,
req.headers['user-agent']
);
res.status(200).json({
success: true,
message: result.message,