aboutsummaryrefslogtreecommitdiff
path: root/services/docker/mail-server/aws-sendmail/mail.ts
diff options
context:
space:
mode:
authorYuqian Yang <crupest@crupest.life>2025-04-30 00:20:23 +0800
committerYuqian Yang <crupest@crupest.life>2025-04-30 13:32:43 +0800
commita58326c5cbe9a73a6b204dac4b9f077d8cf886f4 (patch)
treed01c21a1b621cc9fd738a816c4a4c4138eecae07 /services/docker/mail-server/aws-sendmail/mail.ts
parent84148346e29547cfba92dd697be9bbf31a23a926 (diff)
downloadcrupest-a58326c5cbe9a73a6b204dac4b9f077d8cf886f4.tar.gz
crupest-a58326c5cbe9a73a6b204dac4b9f077d8cf886f4.tar.bz2
crupest-a58326c5cbe9a73a6b204dac4b9f077d8cf886f4.zip
HALF WORK!: 2025-4-30 2
Diffstat (limited to 'services/docker/mail-server/aws-sendmail/mail.ts')
-rw-r--r--services/docker/mail-server/aws-sendmail/mail.ts91
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);
+ }
+}