aboutsummaryrefslogtreecommitdiff
path: root/deno/mail/app.ts
diff options
context:
space:
mode:
authorYuqian Yang <crupest@crupest.life>2025-06-30 14:25:02 +0800
committerYuqian Yang <crupest@crupest.life>2025-06-30 14:25:02 +0800
commit5f00fdd06fa51bb0c82928e060d3f8811bc171e9 (patch)
tree4aaf446eeb120ccf62bbbd2696d576a3235c7f4f /deno/mail/app.ts
parent7178983a5b8a27d5f0f5b447528f0e23b50d75c1 (diff)
downloadcrupest-5f00fdd06fa51bb0c82928e060d3f8811bc171e9.tar.gz
crupest-5f00fdd06fa51bb0c82928e060d3f8811bc171e9.tar.bz2
crupest-5f00fdd06fa51bb0c82928e060d3f8811bc171e9.zip
mail: revert removing.
Diffstat (limited to 'deno/mail/app.ts')
-rw-r--r--deno/mail/app.ts83
1 files changed, 83 insertions, 0 deletions
diff --git a/deno/mail/app.ts b/deno/mail/app.ts
new file mode 100644
index 0000000..332c430
--- /dev/null
+++ b/deno/mail/app.ts
@@ -0,0 +1,83 @@
+import { Hono } from "hono";
+import { logger as honoLogger } from "hono/logger";
+
+import {
+ AliasRecipientMailHook,
+ FallbackRecipientHook,
+ MailDeliverer,
+ RecipientFromHeadersHook,
+} from "./mail.ts";
+import { DovecotMailDeliverer } from "./dovecot.ts";
+import { DumbSmtpServer } from "./dumb-smtp-server.ts";
+
+export function createInbound(
+ {
+ fallback,
+ mailDomain,
+ aliasFile,
+ ldaPath,
+ doveadmPath,
+ }: {
+ fallback: string[];
+ mailDomain: string;
+ aliasFile: string;
+ ldaPath: string;
+ doveadmPath: string;
+ },
+) {
+ const deliverer = new DovecotMailDeliverer(ldaPath, doveadmPath);
+ deliverer.preHooks.push(
+ new RecipientFromHeadersHook(mailDomain),
+ new FallbackRecipientHook(new Set(fallback)),
+ new AliasRecipientMailHook(aliasFile),
+ );
+ return deliverer;
+}
+
+export function createHono(outbound: MailDeliverer, inbound: MailDeliverer) {
+ const hono = new Hono();
+
+ hono.onError((err, c) => {
+ console.error("Hono handler threw an uncaught error.", err);
+ return c.json({ message: "Server error, check its log." }, 500);
+ });
+ hono.use(honoLogger());
+ hono.post("/send/raw", async (context) => {
+ const body = await context.req.text();
+ if (body.trim().length === 0) {
+ return context.json({ message: "Can't send an empty mail." }, 400);
+ } else {
+ const result = await outbound.deliverRaw(body);
+ return context.json({
+ awsMessageId: result.awsMessageId,
+ });
+ }
+ });
+ hono.post("/receive/raw", async (context) => {
+ await inbound.deliverRaw(await context.req.text());
+ return context.json({ message: "Done!" });
+ });
+
+ return hono;
+}
+
+export function createSmtp(outbound: MailDeliverer) {
+ return new DumbSmtpServer(outbound);
+}
+
+export async function sendMail(port: number) {
+ const decoder = new TextDecoder();
+ let text = "";
+ for await (const chunk of Deno.stdin.readable) {
+ text += decoder.decode(chunk);
+ }
+
+ const res = await fetch(`http://127.0.0.1:${port}/send/raw`, {
+ method: "post",
+ body: text,
+ });
+ const fn = res.ok ? "info" : "error";
+ console[fn](res);
+ console[fn](await res.text());
+ if (!res.ok) Deno.exit(-1);
+}