diff options
Diffstat (limited to 'services')
-rw-r--r-- | services/docker/mail-server/aws-sendmail/aws.ts | 16 | ||||
-rw-r--r-- | services/docker/mail-server/aws-sendmail/aws/base.ts | 15 | ||||
-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.ts | 89 | ||||
-rw-r--r-- | services/docker/mail-server/aws-sendmail/base.ts | 35 | ||||
-rw-r--r-- | services/docker/mail-server/aws-sendmail/deno.lock | 10 | ||||
-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.ts | 11 | ||||
-rw-r--r-- | services/docker/mail-server/aws-sendmail/main.ts | 2 | ||||
-rw-r--r-- | services/docker/mail-server/aws-sendmail/traffic.ts | 6 |
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( |