diff options
author | Yuqian Yang <crupest@crupest.life> | 2025-04-10 15:12:46 +0800 |
---|---|---|
committer | Yuqian Yang <crupest@crupest.life> | 2025-06-05 21:07:37 +0800 |
commit | f1bd9a925804e9f9effe1b180f3d4ab890cf6757 (patch) | |
tree | 0ddb044b64f54942a22d0f8a18179f3578247601 /deno/mail-relay/app.ts | |
parent | 2f6809e0de0fff00a77d2baddb57ac7b947e8566 (diff) | |
download | crupest-f1bd9a925804e9f9effe1b180f3d4ab890cf6757.tar.gz crupest-f1bd9a925804e9f9effe1b180f3d4ab890cf6757.tar.bz2 crupest-f1bd9a925804e9f9effe1b180f3d4ab890cf6757.zip |
feat(mail-server): done aws message id mapping.
Diffstat (limited to 'deno/mail-relay/app.ts')
-rw-r--r-- | deno/mail-relay/app.ts | 81 |
1 files changed, 81 insertions, 0 deletions
diff --git a/deno/mail-relay/app.ts b/deno/mail-relay/app.ts new file mode 100644 index 0000000..deb72c2 --- /dev/null +++ b/deno/mail-relay/app.ts @@ -0,0 +1,81 @@ +import { join } from "@std/path"; +import { Hono } from "hono"; +import { logger as honoLogger } from "hono/logger"; + +import log from "./log.ts"; +import config from "./config.ts"; +import { DbService } from "./db.ts"; +import { + AliasRecipientMailHook, + FallbackRecipientHook, + MailDeliverer, +} from "./mail.ts"; +import { DovecotMailDeliverer } from "./dovecot/deliver.ts"; +import { CronTask, CronTaskConfig } from "./cron.ts"; +import { DumbSmtpServer } from "./dumb-smtp-server.ts"; + +export abstract class AppBase { + protected readonly db: DbService; + protected readonly crons: CronTask[] = []; + protected readonly routes: Hono[] = []; + protected readonly inboundDeliverer: MailDeliverer; + protected readonly hono = new Hono(); + + protected abstract readonly outboundDeliverer: MailDeliverer; + + constructor() { + const dataPath = config.get("dataPath"); + Deno.mkdirSync(dataPath, { recursive: true }); + log.path = join(dataPath, "log"); + log.info(config); + + this.db = new DbService(join(dataPath, "db.sqlite")); + this.inboundDeliverer = new DovecotMailDeliverer(); + this.inboundDeliverer.preHooks.push( + new FallbackRecipientHook(new Set(config.getList("inboundFallback"))), + new AliasRecipientMailHook(join(dataPath, "aliases.csv")), + ); + + this.hono.onError((err, c) => { + log.error(err); + return c.json({ msg: "Server error, check its log." }, 500); + }); + + this.hono.use(honoLogger()); + this.hono.post("/send/raw", async (context) => { + const body = await context.req.text(); + if (body.trim().length === 0) { + return context.json({ msg: "Can't send an empty mail." }, 400); + } else { + const result = await this.outboundDeliverer.deliverRaw(body); + return context.json({ + awsMessageId: result.awsMessageId, + }); + } + }); + this.hono.post("/receive/raw", async (context) => { + await this.inboundDeliverer.deliverRaw(await context.req.text()); + return context.json({ "msg": "Done!" }); + }); + } + + createCron(config: CronTaskConfig): CronTask { + const cron = new CronTask(config); + this.crons.push(cron); + return cron; + } + + async setup() { + await this.db.migrate() + } + + serve(): { smtp: DumbSmtpServer; http: Deno.HttpServer } { + const smtp = new DumbSmtpServer(this.outboundDeliverer); + smtp.serve(); + const http = Deno.serve({ + hostname: config.HTTP_HOST, + port: config.HTTP_PORT, + }, this.hono.fetch); + return { smtp, http }; + } +} |