313 lines
7.5 KiB
TypeScript
313 lines
7.5 KiB
TypeScript
|
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.`
|
||
|
}
|
||
|
);
|
||
|
}
|
||
|
}
|