aboutsummaryrefslogtreecommitdiff
path: root/services/docker/mail-server/aws-sendmail
diff options
context:
space:
mode:
Diffstat (limited to 'services/docker/mail-server/aws-sendmail')
-rw-r--r--services/docker/mail-server/aws-sendmail/aws.ts16
-rw-r--r--services/docker/mail-server/aws-sendmail/aws/base.ts15
-rw-r--r--services/docker/mail-server/aws-sendmail/aws/deliver.ts (renamed from services/docker/mail-server/aws-sendmail/delivers/aws.ts)2
-rw-r--r--services/docker/mail-server/aws-sendmail/aws/retriver.ts89
-rw-r--r--services/docker/mail-server/aws-sendmail/base.ts35
-rw-r--r--services/docker/mail-server/aws-sendmail/deno.lock10
-rw-r--r--services/docker/mail-server/aws-sendmail/dovecot.ts (renamed from services/docker/mail-server/aws-sendmail/delivers/dovecot.ts)4
-rw-r--r--services/docker/mail-server/aws-sendmail/logger.ts11
-rw-r--r--services/docker/mail-server/aws-sendmail/main.ts2
-rw-r--r--services/docker/mail-server/aws-sendmail/traffic.ts6
10 files changed, 151 insertions, 39 deletions
diff --git a/services/docker/mail-server/aws-sendmail/aws.ts b/services/docker/mail-server/aws-sendmail/aws.ts
deleted file mode 100644
index cd1c453..0000000
--- a/services/docker/mail-server/aws-sendmail/aws.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { getEnvRequired } from "./base.ts";
-
-export class AwsContext {
- readonly region = "ap-southeast-1"
- readonly credentials = () => {
- return Promise.resolve(
- {
- accessKeyId: getEnvRequired("AWS_USER", "aws access key id"),
- secretAccessKey: getEnvRequired(
- "AWS_PASSWORD",
- "aws secret access key",
- ),
- } as const,
- );
- };
-}
diff --git a/services/docker/mail-server/aws-sendmail/aws/base.ts b/services/docker/mail-server/aws-sendmail/aws/base.ts
new file mode 100644
index 0000000..1e23009
--- /dev/null
+++ b/services/docker/mail-server/aws-sendmail/aws/base.ts
@@ -0,0 +1,15 @@
+import { getEnvRequired } from "../base.ts";
+
+export class AwsContext {
+ readonly region = "ap-southeast-1";
+
+ accessKeyId = getEnvRequired("AWS_USER", "aws access key id");
+ secretAccessKey = getEnvRequired("AWS_PASSWORD", "aws secret access key");
+
+ getCredentials() {
+ const { accessKeyId, secretAccessKey } = this;
+ return Promise.resolve({ accessKeyId, secretAccessKey });
+ }
+
+ readonly credentials = this.getCredentials.bind(this);
+}
diff --git a/services/docker/mail-server/aws-sendmail/delivers/aws.ts b/services/docker/mail-server/aws-sendmail/aws/deliver.ts
index ecc9956..bdc9584 100644
--- a/services/docker/mail-server/aws-sendmail/delivers/aws.ts
+++ b/services/docker/mail-server/aws-sendmail/aws/deliver.ts
@@ -1,6 +1,6 @@
import { SendEmailCommand, SESv2Client } from "@aws-sdk/client-sesv2";
-import { AwsContext } from "../aws.ts";
+import { AwsContext } from "./base.ts";
import { Mail, MailDeliverer } from "../mail.ts";
export class AwsMailDeliverer extends MailDeliverer {
diff --git a/services/docker/mail-server/aws-sendmail/aws/retriver.ts b/services/docker/mail-server/aws-sendmail/aws/retriver.ts
new file mode 100644
index 0000000..cdfe6f1
--- /dev/null
+++ b/services/docker/mail-server/aws-sendmail/aws/retriver.ts
@@ -0,0 +1,89 @@
+/// <reference types="npm:@types/node" />
+
+import {
+ GetObjectCommand,
+ ListObjectsV2Command,
+ S3Client,
+} from "@aws-sdk/client-s3";
+
+import { generateTimeStringForFileName, getEnvRequired } from "../base.ts";
+import { getLogger } from "../logger.ts";
+import { AwsContext } from "./base.ts";
+import { MailDeliverer } from "../mail.ts";
+
+export class AwsMailRetriever {
+ mailBucket = getEnvRequired(
+ "AWS_MAIL_BUCKET",
+ "aws s3 bucket saving raw mails",
+ );
+ liveMailPrefix = "mail/live/";
+ archiveMailPrefix = "mail/archive/";
+
+ private s3Client;
+ private liveMailRecyclerAborter = new AbortController();
+
+ constructor(private aws: AwsContext, private localDeliverer: MailDeliverer) {
+ const { region, credentials } = aws;
+ this.s3Client = new S3Client({ region, credentials });
+ }
+
+ setupLiveMailRecycler() {
+ Deno.cron("live-mail-recycler", "0 */6 * * *", {
+ signal: this.liveMailRecyclerAborter.signal,
+ }, () => {
+ });
+ }
+
+ generateArchivePrefix(instant: Date | Temporal.Instant): string {
+ return `${this.archiveMailPrefix}${
+ generateTimeStringForFileName(instant, true)
+ }/`;
+ }
+
+ async listLiveMails(): Promise<string[]> {
+ const listCommand = new ListObjectsV2Command({
+ Bucket: this.mailBucket,
+ Prefix: this.liveMailPrefix,
+ });
+ const res = await this.s3Client.send(listCommand);
+
+ if (res.Contents == null) {
+ getLogger().warn("Listing live mails in S3 returns null Content.");
+ return [];
+ }
+
+ const result: string[] = [];
+ for (const object of res.Contents) {
+ if (object.Key != null) {
+ result.push(object.Key);
+ } else {
+ getLogger().warn(
+ "Listing live mails in S3 returns an object with no Key.",
+ );
+ }
+ }
+ return result;
+ }
+
+ async deliverS3MailObject(messageId: string) {
+ const command = new GetObjectCommand({
+ Bucket: this.mailBucket,
+ Key: `${this.liveMailPrefix}${messageId}`,
+ });
+ const res = await this.s3Client.send(command);
+
+ if (res.Body == null) {
+ // TODO: Better error.
+ throw new Error();
+ }
+
+ const rawMail = await res.Body.transformToString();
+ await this.localDeliverer.deliverRaw(rawMail);
+
+ const archiveCommand = new
+ }
+
+ async recycleLiveMails() {
+ const mails = await this.listLiveMails();
+ }
+}
diff --git a/services/docker/mail-server/aws-sendmail/base.ts b/services/docker/mail-server/aws-sendmail/base.ts
index 97d377b..08b592a 100644
--- a/services/docker/mail-server/aws-sendmail/base.ts
+++ b/services/docker/mail-server/aws-sendmail/base.ts
@@ -1,11 +1,34 @@
-export const APP_PREFIX="crupest"
-export const APP_NAME="mailserver"
+export const APP_PREFIX = "crupest";
+export const APP_NAME = "mailserver";
export function getEnvRequired(key: string, usage: string): string {
- key = `${APP_PREFIX.toUpperCase()}_${APP_NAME.toUpperCase()}_${key}`
- const value = Deno.env.get(key)
+ key = `${APP_PREFIX.toUpperCase()}_${APP_NAME.toUpperCase()}_${key}`;
+ const value = Deno.env.get(key);
if (value == null) {
- throw new Error(`Env ${key} does not exist, used for ${usage}.`)
+ throw new Error(`Env ${key} does not exist, used for ${usage}.`);
+ }
+ return value;
+}
+
+function getZonedDateTime(instant?: Temporal.Instant | Date) {
+ if (instant == null) {
+ instant = Temporal.Now.instant();
+ } else if (instant instanceof Date) {
+ instant = instant.toTemporalInstant();
+ }
+
+ return instant.toZonedDateTimeISO("UTC");
+}
+
+export function generateTimeStringForFileName(
+ instant?: Temporal.Instant | Date,
+ dateOnly: boolean = false,
+): string {
+ const time = getZonedDateTime(instant);
+
+ if (dateOnly) {
+ return time.toPlainDate().toString();
+ } else {
+ return time.toPlainDateTime().toString().replaceAll(/:|\./g, "-");
}
- return value
}
diff --git a/services/docker/mail-server/aws-sendmail/deno.lock b/services/docker/mail-server/aws-sendmail/deno.lock
index 2731eb5..81e20d7 100644
--- a/services/docker/mail-server/aws-sendmail/deno.lock
+++ b/services/docker/mail-server/aws-sendmail/deno.lock
@@ -15,6 +15,7 @@
"jsr:@std/path@^1.0.9": "1.0.9",
"npm:@aws-sdk/client-s3@^3.797.0": "3.797.0",
"npm:@aws-sdk/client-sesv2@^3.782.0": "3.782.0",
+ "npm:@types/node@*": "22.12.0",
"npm:kysely@~0.28.2": "0.28.2",
"npm:path-to-regexp@^6.3.0": "6.3.0",
"npm:sqlocal@0.14": "0.14.0_kysely@0.28.2"
@@ -1357,6 +1358,12 @@
"@sqlite.org/sqlite-wasm@3.48.0-build4": {
"integrity": "sha512-hI6twvUkzOmyGZhQMza1gpfqErZxXRw6JEsiVjUbo7tFanVD+8Oil0Ih3l2nGzHdxPI41zFmfUQG7GHqhciKZQ=="
},
+ "@types/node@22.12.0": {
+ "integrity": "sha512-Fll2FZ1riMjNmlmJOdAyY5pUbkftXslB5DgEzlIuNaiWhXd00FhWxVC/r4yV/4wBb9JfImTu+jiSvXTkJ7F/gA==",
+ "dependencies": [
+ "undici-types"
+ ]
+ },
"@ungap/structured-clone@1.3.0": {
"integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="
},
@@ -1408,6 +1415,9 @@
"tslib@2.8.1": {
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
},
+ "undici-types@6.20.0": {
+ "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="
+ },
"uuid@9.0.1": {
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="
},
diff --git a/services/docker/mail-server/aws-sendmail/delivers/dovecot.ts b/services/docker/mail-server/aws-sendmail/dovecot.ts
index 1b8c911..520f93b 100644
--- a/services/docker/mail-server/aws-sendmail/delivers/dovecot.ts
+++ b/services/docker/mail-server/aws-sendmail/dovecot.ts
@@ -1,5 +1,5 @@
-import { getLogger } from "../logger.ts";
-import { Mail, MailDeliverer } from "../mail.ts";
+import { getLogger } from "./logger.ts";
+import { Mail, MailDeliverer } from "./mail.ts";
export class DovecotMailDeliverer extends MailDeliverer {
constructor() {
diff --git a/services/docker/mail-server/aws-sendmail/logger.ts b/services/docker/mail-server/aws-sendmail/logger.ts
index 8cea3b0..3a0cff6 100644
--- a/services/docker/mail-server/aws-sendmail/logger.ts
+++ b/services/docker/mail-server/aws-sendmail/logger.ts
@@ -1,16 +1,7 @@
import * as path from "@std/path";
+import { generateTimeStringForFileName } from "./base.ts";
-function generateTimeStringForFileName(
- instant?: Temporal.Instant | Date,
-): string {
- if (instant == null) {
- instant = Temporal.Now.instant();
- } else if (instant instanceof Date) {
- instant = instant.toTemporalInstant();
- }
- return instant.toString().replaceAll(/:|\./g, "-");
-}
export class Logger {
constructor(public readonly path: string) {}
diff --git a/services/docker/mail-server/aws-sendmail/main.ts b/services/docker/mail-server/aws-sendmail/main.ts
index 7702c75..7207188 100644
--- a/services/docker/mail-server/aws-sendmail/main.ts
+++ b/services/docker/mail-server/aws-sendmail/main.ts
@@ -1,4 +1,4 @@
-import { AwsContext } from "./aws.ts";
+import { AwsContext } from "./aws/base.ts";
import { MailTrafficHandler } from "./traffic.ts";
import { Logger, setLogger } from "./logger.ts";
diff --git a/services/docker/mail-server/aws-sendmail/traffic.ts b/services/docker/mail-server/aws-sendmail/traffic.ts
index f31e9ae..4e92557 100644
--- a/services/docker/mail-server/aws-sendmail/traffic.ts
+++ b/services/docker/mail-server/aws-sendmail/traffic.ts
@@ -1,8 +1,8 @@
import { MailDeliverer } from "./mail.ts";
-import { AwsContext } from "./aws.ts";
import { DbService } from "./db.ts";
-import { DovecotMailDeliverer } from "./delivers/dovecot.ts";
-import { AwsMailDeliverer } from "./delivers/aws.ts";
+import { DovecotMailDeliverer } from "./dovecot.ts";
+import { AwsContext } from "./aws/base.ts";
+import { AwsMailDeliverer } from "./aws/deliver.ts";
export abstract class MailTrafficHandler {
constructor(