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
JavaScript
"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;