diff options
Diffstat (limited to 'services/docker/mail-server/aws-sendmail/mail.ts')
-rw-r--r-- | services/docker/mail-server/aws-sendmail/mail.ts | 91 |
1 files changed, 89 insertions, 2 deletions
diff --git a/services/docker/mail-server/aws-sendmail/mail.ts b/services/docker/mail-server/aws-sendmail/mail.ts index e673593..de7942f 100644 --- a/services/docker/mail-server/aws-sendmail/mail.ts +++ b/services/docker/mail-server/aws-sendmail/mail.ts @@ -1,12 +1,57 @@ import { encodeBase64 } from "@std/encoding/base64"; import { getLogger } from "./logger.ts"; +export type MailDeliverStateNotSent = { kind: "not-sent" }; +export type MailDeliverStateDelivered = { + kind: "delivered"; + deliverer: MailDeliverer; + error?: Error; +}; +export type MailDeliverStateError = { + kind: "error"; + deliverer: MailDeliverer; + error: Error; +}; +export type MailDeliverState = + | MailDeliverStateNotSent + | MailDeliverStateDelivered + | MailDeliverStateError; + +class MailDeliverError extends Error { + constructor( + message: string, + options: ErrorOptions, + public readonly mail: Mail, + ) { + super(message, options); + } +} + export class Mail { - message_id: string | null = null; - aws_message_id: string | null = null; + messageId: string | null = null; + awsMessageId: string | null = null; + deliverState: MailDeliverState = { kind: "not-sent" }; constructor(public raw: string) {} + setDelivered(deliverer: MailDeliverer, error?: Error) { + this.deliverState = { "kind": "delivered", deliverer, error }; + } + + throwDeliverError( + deliverer: MailDeliverer, + reason: string, + cause?: unknown, + ): never { + const error = new MailDeliverError( + `Failed to deliver mail to ${deliverer.destination}: ${reason}`, + { cause }, + this, + ); + this.deliverState = { "kind": "error", deliverer, error }; + throw error; + } + encodeUtf8(): Uint8Array { const utf8Encoder = new TextEncoder(); // TODO: A problem here is if mail is VERY long, this will block for a long time. @@ -40,3 +85,45 @@ export class Mail { rawMail.slice(endOfHeadersIndex); } } + +type MailDeliverHook = (mail: Mail) => Promise<void>; + +export abstract class MailDeliverer { + preHooks: MailDeliverHook[] = []; + postHooks: MailDeliverHook[] = []; + + constructor(public readonly destination: string) {} + + protected doPrepare(_mail: Mail): Promise<void> { + return Promise.resolve(); + } + protected abstract doDeliver(mail: Mail): Promise<void>; + protected doFinalize(_mail: Mail): Promise<void> { + return Promise.resolve(); + } + + async deliverRaw(raw: string): Promise<void> { + const mail = new Mail(raw); + await this.deliver(mail); + } + + async deliver(mail: Mail): Promise<void> { + this.doPrepare(mail); + + for (const hook of this.preHooks) { + await hook(mail); + } + + await this.doDeliver(mail); + + if (mail.deliverState.kind === "not-sent") { + mail.setDelivered(this); + } + + for (const hook of this.postHooks) { + await hook(mail); + } + + await this.doFinalize(mail); + } +} |