/** * Request Validation Middleware * Validates request body, query params, and route params */ import { ValidationError } from './errorHandler.mjs' /** * Validation rules */ export const rules = { required: (field) => ({ validate: (value) => value !== undefined && value !== null && value !== '', message: `${field} ist erforderlich`, }), email: () => ({ validate: (value) => !value || /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value), message: 'Ungültige E-Mail-Adresse', }), minLength: (field, min) => ({ validate: (value) => !value || value.length >= min, message: `${field} muss mindestens ${min} Zeichen lang sein`, }), maxLength: (field, max) => ({ validate: (value) => !value || value.length <= max, message: `${field} darf maximal ${max} Zeichen lang sein`, }), isIn: (field, values) => ({ validate: (value) => !value || values.includes(value), message: `${field} muss einer der folgenden Werte sein: ${values.join(', ')}`, }), isNumber: (field) => ({ validate: (value) => !value || !isNaN(Number(value)), message: `${field} muss eine Zahl sein`, }), isPositive: (field) => ({ validate: (value) => !value || Number(value) > 0, message: `${field} muss positiv sein`, }), isArray: (field) => ({ validate: (value) => !value || Array.isArray(value), message: `${field} muss ein Array sein`, }), isObject: (field) => ({ validate: (value) => !value || (typeof value === 'object' && !Array.isArray(value)), message: `${field} muss ein Objekt sein`, }), } /** * Validate request against schema * @param {Object} schema - Validation schema { body: {}, query: {}, params: {} } */ export function validate(schema) { return (req, res, next) => { const errors = {} // Validate each part of the request for (const [location, fields] of Object.entries(schema)) { const data = req[location] || {} for (const [field, fieldRules] of Object.entries(fields)) { const value = data[field] const fieldErrors = [] for (const rule of fieldRules) { if (!rule.validate(value)) { fieldErrors.push(rule.message) } } if (fieldErrors.length > 0) { errors[field] = fieldErrors } } } if (Object.keys(errors).length > 0) { return next(new ValidationError('Validierungsfehler', errors)) } next() } } /** * Common validation schemas */ export const schemas = { // User registration register: { body: { email: [rules.required('E-Mail'), rules.email()], password: [rules.required('Passwort'), rules.minLength('Passwort', 8)], }, }, // Email connection connectEmail: { body: { userId: [rules.required('User ID')], provider: [rules.required('Provider'), rules.isIn('Provider', ['gmail', 'outlook'])], email: [rules.required('E-Mail'), rules.email()], }, }, // Checkout checkout: { body: { userId: [rules.required('User ID')], plan: [rules.required('Plan'), rules.isIn('Plan', ['basic', 'pro', 'business'])], }, }, // Email sorting sortEmails: { body: { userId: [rules.required('User ID')], accountId: [rules.required('Account ID')], maxEmails: [rules.isNumber('maxEmails'), rules.isPositive('maxEmails')], }, }, }