aboutsummaryrefslogtreecommitdiff
path: root/deno/mail-relay/app.ts
blob: 3cac44bf2ea07d3a8ec1596a61c90d618a0b685b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import { Hono } from "hono";
import { logger as honoLogger } from "hono/logger";

import { Logger } from "@crupest/base/log";

import {
  AliasRecipientMailHook,
  FallbackRecipientHook,
  MailDeliverer,
  RecipientFromHeadersHook,
} from "./mail.ts";
import { DovecotMailDeliverer } from "./dovecot.ts";
import { DumbSmtpServer } from "./dumb-smtp-server.ts";

export function createInbound(
  logger: Logger,
  {
    fallback,
    mailDomain,
    aliasFile,
    ldaPath,
  }: {
    fallback: string[];
    mailDomain: string;
    aliasFile: string;
    ldaPath: string;
  },
) {
  const deliverer = new DovecotMailDeliverer(logger, ldaPath);
  deliverer.preHooks.push(
    new RecipientFromHeadersHook(mailDomain),
    new FallbackRecipientHook(new Set(fallback)),
    new AliasRecipientMailHook(aliasFile),
  );
  return deliverer;
}

export function createHono(
  logger: Logger,
  outbound: MailDeliverer,
  inbound: MailDeliverer,
) {
  const hono = new Hono();

  hono.onError((err, c) => {
    logger.error(err);
    return c.json({ msg: "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({ msg: "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({ msg: "Done!" });
  });

  return hono;
}

export function createSmtp(logger: Logger, outbound: MailDeliverer) {
  return new DumbSmtpServer(logger, outbound);
}

export async function sendMail(logger: Logger, 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,
  });
  logger.builder(res).setError(!res.ok).write();
  logger
    .builder("Body\n" + (await res.text()))
    .setError(!res.ok)
    .write();
  if (!res.ok) Deno.exit(-1);
}