refactor(backend): standardize error responses, logging, pagination

- errorResponse(res, error, status, publicMessage) in routeHelpers,
  wired into 125 catch blocks across 10 route files; wire format
  ({ error: <string> }) unchanged byte-for-byte
- Replace remaining console.* with logger across src (178 sites);
  3 intentional console sites kept (install boot, unbound .catch ref)
- Adopt getPagination in 6 routes where semantics match exactly
This commit is contained in:
Paul Nothaft
2026-07-03 07:49:45 +02:00
parent d44ead41c5
commit 6eb46d8c31
41 changed files with 372 additions and 482 deletions
+4 -3
View File
@@ -1,4 +1,5 @@
const { db } = require('../database/db');
const logger = require('./logger');
// Default date format settings
const DEFAULT_FORMAT = {
@@ -19,7 +20,7 @@ async function formatDate(date, language = 'en') {
try {
dateConfig = JSON.parse(setting.setting_value);
} catch (e) {
console.warn('Failed to parse date format setting:', e.message);
logger.warn('Failed to parse date format setting:', e.message);
dateConfig = DEFAULT_FORMAT;
}
} else {
@@ -46,7 +47,7 @@ async function formatDate(date, language = 'en') {
// Check if date is valid
if (isNaN(dateObj.getTime())) {
console.error('Invalid date provided to formatDate:', date);
logger.error('Invalid date provided to formatDate:', date);
throw new Error('Invalid date');
}
@@ -91,7 +92,7 @@ async function formatDate(date, language = 'en') {
});
}
} catch (error) {
console.error('Error formatting date:', error);
logger.error('Error formatting date:', error);
// Fallback to basic formatting
return date instanceof Date ? date.toLocaleDateString() : new Date(date).toLocaleDateString();
}
+2 -1
View File
@@ -5,6 +5,7 @@
// Note: Requiring db here creates circular dependency
// db should be passed as parameter or required where needed
const logger = require('./logger');
/**
* Get database client type
@@ -91,7 +92,7 @@ async function getDatabaseSize(db, dbName) {
const stats = await fs.stat(dbPath);
return stats.size;
} catch (error) {
console.error('Error getting SQLite database size:', error);
logger.error('Error getting SQLite database size:', error);
return 0;
}
}
+4 -3
View File
@@ -1,5 +1,6 @@
const path = require('path');
const fs = require('fs').promises;
const logger = require('./logger');
/**
* Secure file security utilities to prevent path traversal and validate file types
@@ -174,7 +175,7 @@ async function validateFileContent(filePath, expectedMimeType) {
return true;
});
} catch (error) {
console.error('Error validating file content:', error);
logger.error('Error validating file content:', error);
return false;
}
}
@@ -239,7 +240,7 @@ function createFileUploadValidator(options = {}) {
try {
await fs.unlink(file.path);
} catch (err) {
console.error('Error removing invalid file:', err);
logger.error('Error removing invalid file:', err);
}
return res.status(400).json({
error: `File content does not match declared type: ${file.originalname}`
@@ -250,7 +251,7 @@ function createFileUploadValidator(options = {}) {
next();
} catch (error) {
console.error('File validation error:', error);
logger.error('File validation error:', error);
res.status(500).json({ error: 'File validation failed' });
}
};
+15 -13
View File
@@ -5,6 +5,7 @@
const { validationResult } = require('express-validator');
const { ValidationError } = require('./errors');
const logger = require('./logger');
/**
* Wraps an async route handler to catch errors and pass them to the error handler.
@@ -69,25 +70,26 @@ const successResponse = (res, data, statusCode = 200, message = null) => {
};
/**
* Sends a standardized error response.
* Note: Prefer throwing custom errors and letting the error handler format the response.
* Logs an error and sends a standardized error response of shape `{ error: <string> }`.
*
* @param {Response} res - Express response object
* @param {string} message - Error message
* @param {Error|*} error - The caught error (logged, never sent to the client)
* @param {number} [statusCode=500] - HTTP status code
* @param {string} [code] - Optional error code
* @param {*} [details] - Optional additional error details
* @param {string} [publicMessage] - Message sent to the client; falls back to the error's message
*
* @example
* errorResponse(res, 'Invalid input', 400, 'VALIDATION_ERROR', { field: 'email' });
* } catch (error) {
* errorResponse(res, error, 500, 'Failed to fetch events');
* }
*/
const errorResponse = (res, message, statusCode = 500, code = null, details = null) => {
const response = {
error: message,
...(code && { code }),
...(details && { details })
};
res.status(statusCode).json(response);
const errorResponse = (res, error, statusCode = 500, publicMessage) => {
const message = publicMessage || (error instanceof Error ? error.message : String(error));
const route = res.req ? `${res.req.method} ${res.req.originalUrl}` : null;
logger.error(route ? `${route} - ${message}` : message, {
error: error instanceof Error ? error.message : error,
stack: error instanceof Error ? error.stack : undefined
});
res.status(statusCode).json({ error: message });
};
/**