- AuthService mit allen Methoden (register, login, forgotPassword, resetPassword, logout) - ErrorHandler für zentrale Fehlerbehandlung - authController.js auf neue Clean Code Architektur umgestellt - Alle Authentifizierungsendpunkte verwenden jetzt Service Layer Pattern
139 lines
3.6 KiB
JavaScript
139 lines
3.6 KiB
JavaScript
/**
|
|
* Zentrale Fehlerbehandlung für die Anwendung
|
|
*/
|
|
class ErrorHandler {
|
|
/**
|
|
* Erstellt eine standardisierte Fehlerantwort
|
|
* @param {Object} res - Express Response Objekt
|
|
* @param {number} statusCode - HTTP Status Code
|
|
* @param {string} message - Fehlermeldung
|
|
* @param {Object} details - Zusätzliche Fehlerdetails (optional)
|
|
*/
|
|
static errorResponse(res, statusCode, message, details = null) {
|
|
const error = {
|
|
success: false,
|
|
message,
|
|
timestamp: new Date().toISOString()
|
|
};
|
|
|
|
if (details) {
|
|
error.details = details;
|
|
}
|
|
|
|
console.error(`[${statusCode}] ${message}`, details ? details : '');
|
|
return res.status(statusCode).json(error);
|
|
}
|
|
|
|
/**
|
|
* Erstellt eine erfolgreiche Antwort
|
|
* @param {Object} res - Express Response Objekt
|
|
* @param {Object} data - Antwortdaten
|
|
* @param {string} message - Erfolgsmeldung
|
|
* @param {number} statusCode - HTTP Status Code (default: 200)
|
|
*/
|
|
static successResponse(res, data, message = 'Erfolgreich', statusCode = 200) {
|
|
const response = {
|
|
success: true,
|
|
message,
|
|
data,
|
|
timestamp: new Date().toISOString()
|
|
};
|
|
|
|
return res.status(statusCode).json(response);
|
|
}
|
|
|
|
/**
|
|
* Wrapper für async Controller-Funktionen mit automatischer Fehlerbehandlung
|
|
* @param {Function} fn - Async Controller-Funktion
|
|
* @returns {Function} - Wrapped Controller-Funktion
|
|
*/
|
|
static asyncHandler(fn) {
|
|
return (req, res, next) => {
|
|
Promise.resolve(fn(req, res, next)).catch((error) => {
|
|
console.error('Unhandled error in async handler:', error);
|
|
this.errorResponse(res, 500, 'Ein unerwarteter Fehler ist aufgetreten', {
|
|
error: error.message,
|
|
stack: process.env.NODE_ENV === 'development' ? error.stack : undefined
|
|
});
|
|
});
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Behandelt Sequelize-Validierungsfehler
|
|
* @param {Object} error - Sequelize Error
|
|
* @returns {Object} - Standardisierte Fehlerantwort
|
|
*/
|
|
static handleSequelizeError(error) {
|
|
if (error.name === 'SequelizeValidationError') {
|
|
const validationErrors = error.errors.map(err => ({
|
|
field: err.path,
|
|
message: err.message,
|
|
value: err.value
|
|
}));
|
|
return {
|
|
statusCode: 400,
|
|
message: 'Validierungsfehler',
|
|
details: { validationErrors }
|
|
};
|
|
}
|
|
|
|
if (error.name === 'SequelizeUniqueConstraintError') {
|
|
return {
|
|
statusCode: 409,
|
|
message: 'Eindeutigkeitsverletzung',
|
|
details: { field: error.errors[0]?.path }
|
|
};
|
|
}
|
|
|
|
if (error.name === 'SequelizeForeignKeyConstraintError') {
|
|
return {
|
|
statusCode: 400,
|
|
message: 'Referenzfehler',
|
|
details: { field: error.fields[0] }
|
|
};
|
|
}
|
|
|
|
if (error.name === 'SequelizeDatabaseError') {
|
|
return {
|
|
statusCode: 500,
|
|
message: 'Datenbankfehler',
|
|
details: { original: error.original?.message }
|
|
};
|
|
}
|
|
|
|
return {
|
|
statusCode: 500,
|
|
message: 'Datenbankfehler',
|
|
details: { error: error.message }
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Behandelt JWT-Fehler
|
|
* @param {Object} error - JWT Error
|
|
* @returns {Object} - Standardisierte Fehlerantwort
|
|
*/
|
|
static handleJWTError(error) {
|
|
if (error.name === 'JsonWebTokenError') {
|
|
return {
|
|
statusCode: 401,
|
|
message: 'Ungültiger Token'
|
|
};
|
|
}
|
|
|
|
if (error.name === 'TokenExpiredError') {
|
|
return {
|
|
statusCode: 401,
|
|
message: 'Token abgelaufen'
|
|
};
|
|
}
|
|
|
|
return {
|
|
statusCode: 401,
|
|
message: 'Authentifizierungsfehler'
|
|
};
|
|
}
|
|
}
|
|
|
|
module.exports = ErrorHandler; |