platformservice/ts/errors/email.errors.ts

313 lines
7.5 KiB
TypeScript
Raw Normal View History

2025-05-08 12:46:10 +00:00
import {
PlatformError,
ValidationError,
NetworkError,
ResourceError,
OperationError
} from './base.errors.js';
import type { IErrorContext } from './base.errors.js';
import {
EMAIL_SERVICE_ERROR,
EMAIL_TEMPLATE_ERROR,
EMAIL_VALIDATION_ERROR,
EMAIL_SEND_ERROR,
EMAIL_RECEIVE_ERROR,
EMAIL_ATTACHMENT_ERROR,
EMAIL_PARSE_ERROR,
EMAIL_RATE_LIMIT_EXCEEDED
} from './error.codes.js';
/**
* Base class for all email service related errors
*/
export class EmailServiceError extends OperationError {
/**
* Creates a new email service error
*
* @param message Error message
* @param context Additional context
*/
constructor(
message: string,
context: IErrorContext = {}
) {
super(message, EMAIL_SERVICE_ERROR, context);
}
}
/**
* Error class for email template errors
*/
export class EmailTemplateError extends OperationError {
/**
* Creates a new email template error
*
* @param message Error message
* @param context Additional context
*/
constructor(
message: string,
context: IErrorContext = {}
) {
super(message, EMAIL_TEMPLATE_ERROR, context);
}
}
/**
* Error class for email validation errors
*/
export class EmailValidationError extends ValidationError {
/**
* Creates a new email validation error
*
* @param message Error message
* @param context Additional context
*/
constructor(
message: string,
context: IErrorContext = {}
) {
super(message, EMAIL_VALIDATION_ERROR, context);
}
}
/**
* Error class for email sending errors
*/
export class EmailSendError extends OperationError {
/**
* Creates a new email send error
*
* @param message Error message
* @param context Additional context
*/
constructor(
message: string,
context: IErrorContext = {}
) {
super(message, EMAIL_SEND_ERROR, context);
}
/**
* Creates an instance for a permanently failed send
*
* @param message Error message
* @param context Additional context
*/
public static permanent(
message: string,
context: IErrorContext = {}
): EmailSendError {
return new EmailSendError(`Permanent send failure: ${message}`, {
...context,
data: {
...context.data,
permanent: true
},
userMessage: 'The email could not be delivered due to a permanent failure.'
});
}
/**
* Creates an instance for a temporary failed send
*
* @param message Error message
* @param maxRetries Maximum number of retries
* @param currentRetry Current retry count
* @param retryDelay Delay between retries in ms
* @param context Additional context
*/
public static temporary(
message: string,
maxRetries: number = 3,
currentRetry: number = 0,
retryDelay: number = 60000,
context: IErrorContext = {}
): EmailSendError {
const error = new EmailSendError(`Temporary send failure: ${message}`, {
...context,
data: {
...context.data,
permanent: false
},
userMessage: 'The email delivery failed temporarily. It will be retried.'
});
return error.withRetry(maxRetries, currentRetry, retryDelay) as EmailSendError;
}
/**
* Check if this is a permanent send failure
*/
public isPermanent(): boolean {
return !!this.context.data?.permanent;
}
}
/**
* Error class for email receiving errors
*/
export class EmailReceiveError extends OperationError {
/**
* Creates a new email receive error
*
* @param message Error message
* @param context Additional context
*/
constructor(
message: string,
context: IErrorContext = {}
) {
super(message, EMAIL_RECEIVE_ERROR, context);
}
}
/**
* Error class for email attachment errors
*/
export class EmailAttachmentError extends ValidationError {
/**
* Creates a new email attachment error
*
* @param message Error message
* @param context Additional context
*/
constructor(
message: string,
context: IErrorContext = {}
) {
super(message, EMAIL_ATTACHMENT_ERROR, context);
}
/**
* Creates an instance for an attachment too large error
*
* @param size Attachment size in bytes
* @param maxSize Maximum allowed size in bytes
* @param filename Attachment filename
* @param context Additional context
*/
public static tooLarge(
size: number,
maxSize: number,
filename?: string,
context: IErrorContext = {}
): EmailAttachmentError {
const filenameText = filename ? ` (${filename})` : '';
return new EmailAttachmentError(
`Attachment${filenameText} size ${size} bytes exceeds maximum allowed size of ${maxSize} bytes`,
{
...context,
data: {
...context.data,
size,
maxSize,
filename
},
userMessage: `The attachment${filenameText} is too large. Maximum size is ${Math.round(maxSize / 1024 / 1024)} MB.`
}
);
}
/**
* Creates an instance for an invalid attachment type error
*
* @param contentType Attachment content type
* @param filename Attachment filename
* @param allowedTypes List of allowed content types
* @param context Additional context
*/
public static invalidType(
contentType: string,
filename: string,
allowedTypes: string[],
context: IErrorContext = {}
): EmailAttachmentError {
return new EmailAttachmentError(
`Attachment '${filename}' with content type '${contentType}' is not allowed. Allowed types: ${allowedTypes.join(', ')}`,
{
...context,
data: {
...context.data,
contentType,
filename,
allowedTypes
},
userMessage: `The attachment type ${contentType} is not allowed.`
}
);
}
}
/**
* Error class for email parsing errors
*/
export class EmailParseError extends OperationError {
/**
* Creates a new email parse error
*
* @param message Error message
* @param context Additional context
*/
constructor(
message: string,
context: IErrorContext = {}
) {
super(message, EMAIL_PARSE_ERROR, context);
}
}
/**
* Error class for email rate limit exceeded errors
*/
export class EmailRateLimitError extends ResourceError {
/**
* Creates a new email rate limit error
*
* @param message Error message
* @param context Additional context
*/
constructor(
message: string,
context: IErrorContext = {}
) {
super(message, EMAIL_RATE_LIMIT_EXCEEDED, context);
}
/**
* Creates an instance with rate limit information
*
* @param limit Rate limit
* @param remaining Remaining quota
* @param resetAt Time when the quota resets
* @param scope Rate limit scope (global, domain, user, etc.)
* @param context Additional context
*/
public static withLimitInfo(
limit: number,
remaining: number,
resetAt: Date | number,
scope: string = 'global',
context: IErrorContext = {}
): EmailRateLimitError {
const resetTime = typeof resetAt === 'number' ? new Date(resetAt) : resetAt;
const resetTimeStr = resetTime.toISOString();
return new EmailRateLimitError(
`Email rate limit exceeded: ${remaining}/${limit} remaining in ${scope} scope, resets at ${resetTimeStr}`,
{
...context,
data: {
...context.data,
limit,
remaining,
resetAt: resetTime.getTime(),
resetTimeStr,
scope
},
userMessage: `You've reached the email sending limit. Please try again later.`
}
);
}
}