UNPKG

n8n-nodes-sevdesk-v2

Version:

n8n community node for SevDesk API v2 integration with 24 production-ready workflow templates. Direct API access without external dependencies - simplified, secure, and optimized for German accounting automation (n8n 1.101.1 compatible).

354 lines (353 loc) 16.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SevDeskErrorFactory = exports.SevDeskFileError = exports.SevDeskBatchError = exports.SevDeskVoucherError = exports.SevDeskOrderError = exports.SevDeskInvoiceError = exports.SevDeskContactError = exports.SevDeskApiError = exports.SevDeskConfigurationError = exports.SevDeskConnectionError = exports.SevDeskServerError = exports.SevDeskRateLimitError = exports.SevDeskValidationError = exports.SevDeskNotFoundError = exports.SevDeskAuthorizationError = exports.SevDeskAuthenticationError = exports.SevDeskError = void 0; const n8n_workflow_1 = require("n8n-workflow"); class SevDeskError extends Error { constructor(message, originalError) { super(message); this.originalError = originalError; this.name = this.constructor.name; } sanitizeMessage(message) { if (!message) return 'An error occurred'; const sensitivePatterns = [ /api[_-]?key[=:\s]*[a-zA-Z0-9_-]{10,}/gi, /token[=:\s]*[a-zA-Z0-9_-]{10,}/gi, /bearer\s+[a-zA-Z0-9_-]{10,}/gi, /authorization[=:\s]*[a-zA-Z0-9_-]{10,}/gi, /([a-zA-Z0-9._%+-]+)@([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/g, /\+?[\d\s\-\(\)]{10,}/g, /[a-zA-Z]:\\[^\\]+\\[^\\]+/g, /\/[a-zA-Z0-9_-]+\/[a-zA-Z0-9_-]+\/[a-zA-Z0-9_-]+/g, /(?:mysql|postgres|mongodb|redis):\/\/[^\s]+/gi, /\b(?:\d{1,3}\.){3}\d{1,3}\b/g, /at\s+[^\s]+\s+\([^)]+\)/g, ]; let sanitized = message; sensitivePatterns.forEach(pattern => { if (pattern.source.includes('email')) { sanitized = sanitized.replace(pattern, (match, user, domain) => { const maskedUser = user.length > 1 ? user[0] + '*'.repeat(user.length - 1) : '*'; return `${maskedUser}@${domain}`; }); } else if (pattern.source.includes('IP')) { sanitized = sanitized.replace(pattern, (match) => { const parts = match.split('.'); return parts.length === 4 ? `${parts[0]}.${parts[1]}.*.*` : match; }); } else { sanitized = sanitized.replace(pattern, '[REDACTED]'); } }); sanitized = sanitized.replace(/\s+/g, ' ').trim(); return sanitized || 'An error occurred during the operation'; } getSanitizedMessage() { return this.sanitizeMessage(this.message); } toNodeApiError(node) { const sanitizedMessage = this.getSanitizedMessage(); if (process.env.NODE_ENV === 'development') { console.log('SevDesk Error Debug Info:', { nodeType: node?.type, nodeName: node?.name, errorType: this.constructor.name, errorCode: this.errorCode, sanitizedMessage: sanitizedMessage }); } const errorData = { message: sanitizedMessage, error: sanitizedMessage, errorCode: this.errorCode, type: this.constructor.name }; const nodeApiError = new n8n_workflow_1.NodeApiError(node, errorData, { description: this.getDescription(), ...(this.httpStatusCode && { httpCode: this.httpStatusCode.toString() }), }); return nodeApiError; } } exports.SevDeskError = SevDeskError; class SevDeskAuthenticationError extends SevDeskError { constructor(message = 'Authentication failed', originalError) { super(message, originalError); this.errorCode = 'AUTHENTICATION_ERROR'; this.httpStatusCode = 401; } getDescription() { return 'Please check your API key and ensure it has the necessary permissions. You can find your API key in your SevDesk account settings.'; } } exports.SevDeskAuthenticationError = SevDeskAuthenticationError; class SevDeskAuthorizationError extends SevDeskError { constructor(message = 'Insufficient permissions', originalError) { super(message, originalError); this.errorCode = 'AUTHORIZATION_ERROR'; this.httpStatusCode = 403; } getDescription() { return 'Your API key does not have sufficient permissions for this operation. Please check your SevDesk account permissions or contact your administrator.'; } } exports.SevDeskAuthorizationError = SevDeskAuthorizationError; class SevDeskNotFoundError extends SevDeskError { constructor(resource, id, originalError) { const message = id ? `${resource} with ID '${id}' not found` : `${resource} not found`; super(message, originalError); this.errorCode = 'NOT_FOUND_ERROR'; this.httpStatusCode = 404; } getDescription() { return 'The requested resource does not exist. Please verify the ID and try again.'; } } exports.SevDeskNotFoundError = SevDeskNotFoundError; class SevDeskValidationError extends SevDeskError { constructor(message = 'Validation failed', validationErrors, originalError) { super(message, originalError); this.validationErrors = validationErrors; this.errorCode = 'VALIDATION_ERROR'; this.httpStatusCode = 400; } getDescription() { if (this.validationErrors) { const errorDetails = Object.entries(this.validationErrors) .map(([field, errors]) => `${field}: ${errors.join(', ')}`) .join('; '); return `Validation failed: ${errorDetails}`; } return 'The provided data is invalid. Please check your input parameters and try again.'; } } exports.SevDeskValidationError = SevDeskValidationError; class SevDeskRateLimitError extends SevDeskError { constructor(message = 'Rate limit exceeded', retryAfter, originalError) { super(message, originalError); this.retryAfter = retryAfter; this.errorCode = 'RATE_LIMIT_ERROR'; this.httpStatusCode = 429; } getDescription() { const retryMessage = this.retryAfter ? ` Please wait ${this.retryAfter} seconds before retrying.` : ' Please wait before making additional requests.'; return `You have exceeded the API rate limit.${retryMessage}`; } } exports.SevDeskRateLimitError = SevDeskRateLimitError; class SevDeskServerError extends SevDeskError { constructor(message = 'Server error occurred', originalError) { super(message, originalError); this.errorCode = 'SERVER_ERROR'; this.httpStatusCode = 500; } getDescription() { return 'A server error occurred on SevDesk\'s side. Please try again later or contact SevDesk support if the problem persists.'; } } exports.SevDeskServerError = SevDeskServerError; class SevDeskConnectionError extends SevDeskError { constructor(message = 'Connection failed', originalError) { super(message, originalError); this.errorCode = 'CONNECTION_ERROR'; this.httpStatusCode = undefined; } getDescription() { return 'Failed to connect to SevDesk API. Please check your internet connection and try again.'; } } exports.SevDeskConnectionError = SevDeskConnectionError; class SevDeskConfigurationError extends SevDeskError { constructor(message = 'Configuration error', originalError) { super(message, originalError); this.errorCode = 'CONFIGURATION_ERROR'; this.httpStatusCode = undefined; } getDescription() { return 'There is an issue with the node configuration. Please check your settings and credentials.'; } } exports.SevDeskConfigurationError = SevDeskConfigurationError; class SevDeskApiError extends SevDeskError { constructor(message = 'API error occurred', httpStatusCode, originalError) { super(message, originalError); this.httpStatusCode = httpStatusCode; this.errorCode = 'API_ERROR'; } getDescription() { return 'An unexpected API error occurred. Please check your request parameters and try again.'; } } exports.SevDeskApiError = SevDeskApiError; class SevDeskContactError extends SevDeskError { constructor(operation, message, contactId, originalError) { super(message, originalError); this.contactId = contactId; this.errorCode = 'CONTACT_ERROR'; this.httpStatusCode = 400; } getDescription() { const suggestions = { 'create': 'Ensure required fields (name, customerNumber, category) are provided and valid.', 'update': 'Check that the contact ID exists and you have permission to modify it.', 'delete': 'Verify the contact exists and is not referenced by other records (invoices, orders).', 'get': 'Check that the contact ID is valid and you have access to it.', 'checkCustomerNumberAvailability': 'Ensure the customer number format is valid.' }; return suggestions[this.message.toLowerCase()] || 'Please check the contact data and permissions.'; } } exports.SevDeskContactError = SevDeskContactError; class SevDeskInvoiceError extends SevDeskError { constructor(operation, message, invoiceId, originalError) { super(message, originalError); this.invoiceId = invoiceId; this.errorCode = 'INVOICE_ERROR'; this.httpStatusCode = 400; } getDescription() { const suggestions = { 'create': 'Ensure contact ID, invoice date, and positions are provided. Check tax calculations.', 'update': 'Verify the invoice is still editable (not sent or paid) and data is valid.', 'delete': 'Check that the invoice exists and is not finalized or paid.', 'sendByEmail': 'Ensure the contact has a valid email address and invoice is finalized.', 'markAsSent': 'Verify the invoice is in the correct status to be marked as sent.', 'bookAmount': 'Check the amount, date, and check account are valid for payment booking.', 'cancel': 'Ensure the invoice can be cancelled (not already paid or cancelled).' }; return suggestions[this.message.toLowerCase()] || 'Please check the invoice data and status.'; } } exports.SevDeskInvoiceError = SevDeskInvoiceError; class SevDeskOrderError extends SevDeskError { constructor(operation, message, orderId, originalError) { super(message, originalError); this.orderId = orderId; this.errorCode = 'ORDER_ERROR'; this.httpStatusCode = 400; } getDescription() { const suggestions = { 'create': 'Ensure contact ID, order date, and order positions are provided.', 'update': 'Verify the order is still editable and not converted to invoice.', 'delete': 'Check that the order exists and is not referenced by invoices.', 'createInvoice': 'Ensure the order is finalized and has valid positions for invoice creation.' }; return suggestions[this.message.toLowerCase()] || 'Please check the order data and status.'; } } exports.SevDeskOrderError = SevDeskOrderError; class SevDeskVoucherError extends SevDeskError { constructor(operation, message, voucherId, originalError) { super(message, originalError); this.voucherId = voucherId; this.errorCode = 'VOUCHER_ERROR'; this.httpStatusCode = 400; } getDescription() { const suggestions = { 'create': 'Ensure voucher date, supplier, and amounts are provided. Check document upload.', 'update': 'Verify the voucher is still editable and not booked.', 'delete': 'Check that the voucher exists and is not booked to accounting.', 'upload': 'Ensure the file is valid (PDF, image) and under size limit (10MB).' }; return suggestions[this.message.toLowerCase()] || 'Please check the voucher data and document.'; } } exports.SevDeskVoucherError = SevDeskVoucherError; class SevDeskBatchError extends SevDeskError { constructor(message, batchId, failedOperations, originalError) { super(message, originalError); this.batchId = batchId; this.failedOperations = failedOperations; this.errorCode = 'BATCH_ERROR'; this.httpStatusCode = 400; } getDescription() { if (this.failedOperations && this.failedOperations.length > 0) { return `Batch operation partially failed. ${this.failedOperations.length} operations failed. Check individual operation errors for details.`; } return 'Batch operation failed. Ensure all operations are valid and within batch size limits (max 100 operations).'; } } exports.SevDeskBatchError = SevDeskBatchError; class SevDeskFileError extends SevDeskError { constructor(operation, message, fileName, originalError) { super(message, originalError); this.fileName = fileName; this.errorCode = 'FILE_ERROR'; this.httpStatusCode = 400; } getDescription() { const suggestions = { 'upload': 'Check file format (PDF, JPG, PNG), size (max 10MB), and permissions.', 'download': 'Ensure the file exists and you have access permissions.', 'downloadPdf': 'Verify the document can be generated as PDF.', 'downloadXml': 'Check that XML export is available for this resource type.' }; return suggestions[this.message.toLowerCase()] || 'Please check the file operation and permissions.'; } } exports.SevDeskFileError = SevDeskFileError; class SevDeskErrorFactory { static createError(statusCode, errorData, originalError) { const message = errorData?.message || errorData?.error || 'Unknown error'; switch (statusCode) { case 401: return new SevDeskAuthenticationError(message, originalError); case 403: return new SevDeskAuthorizationError(message, originalError); case 404: return new SevDeskNotFoundError('Resource', undefined, originalError); case 400: return new SevDeskValidationError(message, errorData?.validation_errors || errorData?.errors, originalError); case 429: const retryAfter = errorData?.retry_after || originalError?.headers?.['retry-after']; return new SevDeskRateLimitError(message, retryAfter, originalError); case 500: case 502: case 503: case 504: return new SevDeskServerError(message, originalError); default: return new SevDeskApiError(message, statusCode, originalError); } } static createResourceError(resource, operation, message, resourceId, originalError) { switch (resource.toLowerCase()) { case 'contact': return new SevDeskContactError(operation, message, resourceId, originalError); case 'invoice': return new SevDeskInvoiceError(operation, message, resourceId, originalError); case 'order': return new SevDeskOrderError(operation, message, resourceId, originalError); case 'voucher': return new SevDeskVoucherError(operation, message, resourceId, originalError); case 'batch': return new SevDeskBatchError(message, resourceId, undefined, originalError); default: return new SevDeskApiError(message, 400, originalError); } } static createFileError(operation, message, fileName, originalError) { return new SevDeskFileError(operation, message, fileName, originalError); } static createConnectionError(originalError) { const message = originalError?.message || 'Connection failed'; return new SevDeskConnectionError(message, originalError); } static createConfigurationError(message, originalError) { return new SevDeskConfigurationError(message, originalError); } static createBatchError(message, batchId, failedOperations, originalError) { return new SevDeskBatchError(message, batchId, failedOperations, originalError); } } exports.SevDeskErrorFactory = SevDeskErrorFactory;