105 lines
2.8 KiB
JavaScript
105 lines
2.8 KiB
JavaScript
|
|
/**
|
||
|
|
* Centralized Error Formatter Middleware
|
||
|
|
*
|
||
|
|
* Standard error response format:
|
||
|
|
* {
|
||
|
|
* "error": "ValidationError",
|
||
|
|
* "message": "Human-readable description",
|
||
|
|
* "field": "optional-field-name",
|
||
|
|
* "code": "machine-readable-code"
|
||
|
|
* }
|
||
|
|
*/
|
||
|
|
|
||
|
|
const { ValidationError, formatError } = require('../utils/apiError');
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Extract field name from various validation error patterns
|
||
|
|
*/
|
||
|
|
function extractFieldFromError(message) {
|
||
|
|
if (!message) return null;
|
||
|
|
|
||
|
|
// Patterns like "field must be ..." or "field is ..."
|
||
|
|
const fieldMatch = message.match(/^(\w+)\s+(?:must be|is|are)\s+/i);
|
||
|
|
if (fieldMatch) return fieldMatch[1];
|
||
|
|
|
||
|
|
// Patterns like "year must be..." or "month must be..."
|
||
|
|
const yearMonthMatch = message.match(/^(year|month|due_day|interest_rate|actual_amount|start_year|start_month|end_year|end_month)\s+must\b/i);
|
||
|
|
if (yearMonthMatch) return yearMonthMatch[1];
|
||
|
|
|
||
|
|
// Patterns like "name is required"
|
||
|
|
const requiredMatch = message.match(/^(username|password|name|bill_id|category_id|due_day)\s+(?:is|are)\s+required/i);
|
||
|
|
if (requiredMatch) return requiredMatch[1];
|
||
|
|
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Convert a plain error message/string into a standardized error object
|
||
|
|
*/
|
||
|
|
function standardizeError(message, code = 'VALIDATION_ERROR', fieldOverride = null) {
|
||
|
|
const field = fieldOverride || extractFieldFromError(message);
|
||
|
|
|
||
|
|
return {
|
||
|
|
error: code,
|
||
|
|
message: typeof message === 'string' ? message : String(message),
|
||
|
|
field: field || null,
|
||
|
|
code: code
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Express middleware that ensures all error responses follow standard format
|
||
|
|
*/
|
||
|
|
function errorFormatter(req, res, next) {
|
||
|
|
const originalJson = res.json;
|
||
|
|
|
||
|
|
res.json = function(data) {
|
||
|
|
// If data is an error object (has error property), standardize it
|
||
|
|
if (data && typeof data === 'object' && data.error && !data.success) {
|
||
|
|
const standardized = standardizeError(data.error, data.error || 'ERROR', data.field);
|
||
|
|
return originalJson.call(this, standardized);
|
||
|
|
}
|
||
|
|
return originalJson.call(this, data);
|
||
|
|
};
|
||
|
|
|
||
|
|
next();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Helper to format validation errors with proper field extraction
|
||
|
|
*/
|
||
|
|
function formatValidationError(message, field) {
|
||
|
|
return standardizeError(message, 'VALIDATION_ERROR', field);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Helper to format not found errors
|
||
|
|
*/
|
||
|
|
function formatNotFoundError(message, field) {
|
||
|
|
return standardizeError(message, 'NOT_FOUND', field);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Helper to format authentication errors
|
||
|
|
*/
|
||
|
|
function formatAuthError(message) {
|
||
|
|
return standardizeError(message, 'AUTH_ERROR');
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Helper to format forbidden/access errors
|
||
|
|
*/
|
||
|
|
function formatForbiddenError(message) {
|
||
|
|
return standardizeError(message, 'FORBIDDEN');
|
||
|
|
}
|
||
|
|
|
||
|
|
module.exports = {
|
||
|
|
errorFormatter,
|
||
|
|
standardizeError,
|
||
|
|
formatValidationError,
|
||
|
|
formatNotFoundError,
|
||
|
|
formatAuthError,
|
||
|
|
formatForbiddenError,
|
||
|
|
extractFieldFromError,
|
||
|
|
};
|