summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElizabeth Hunt <elizabeth.hunt@simponic.xyz>2024-12-15 01:10:24 -0800
committerElizabeth Hunt <elizabeth.hunt@simponic.xyz>2024-12-15 01:24:48 -0800
commit53187bede7e871ceca8fe4fabd18822002eb9316 (patch)
treed46744420eb33760965c136a77ac0b522fcde561
parentf8777a6adf924c33b5d641b293ac707df6d0c87e (diff)
downloaduptime-53187bede7e871ceca8fe4fabd18822002eb9316.tar.gz
uptime-53187bede7e871ceca8fe4fabd18822002eb9316.zip
hack to delete uids
-rw-r--r--src/email.ts137
1 files changed, 47 insertions, 90 deletions
diff --git a/src/email.ts b/src/email.ts
index f8916d4..6ad05a0 100644
--- a/src/email.ts
+++ b/src/email.ts
@@ -4,24 +4,16 @@ import * as O from "fp-ts/lib/Option";
import { createTransport } from "nodemailer";
import { toError } from "fp-ts/lib/Either";
import { pipe } from "fp-ts/lib/function";
-import {
- ImapFlow,
- type FetchMessageObject,
- type FetchQueryObject,
- type MailboxLockObject,
-} from "imapflow";
+import { ImapFlow, type FetchMessageObject, type FetchQueryObject, type MailboxLockObject } from "imapflow";
import * as IO from "fp-ts/lib/IO";
import * as T from "fp-ts/lib/Task";
-import { ConsoleLogger } from "./logger";
+import { ConsoleLogger, type Logger } from "./logger";
interface ImapClientI {
- fetchAll: (
- range: string,
- options: FetchQueryObject,
- ) => Promise<FetchMessageObject[]>;
+ fetchAll: (range: string, options: FetchQueryObject) => Promise<FetchMessageObject[]>;
connect: () => Promise<void>;
getMailboxLock: (mailbox: string) => Promise<MailboxLockObject>;
- messageDelete: (uids: number[], opts: any) => Promise<boolean>;
+ messageDelete: (uids: number[], opts: Record<string, any>) => Promise<boolean>;
close: () => void;
}
@@ -40,10 +32,7 @@ class ErrorWithLock extends Error {
}
}
const ToErrorWithLock = (lock?: MailboxLockObject) => (error: unknown) =>
- new ErrorWithLock(
- error instanceof Error ? error.message : "Unknown error",
- lock,
- );
+ new ErrorWithLock(error instanceof Error ? error.message : "Unknown error", lock);
/**
* Generate a unique email.
@@ -51,42 +40,31 @@ const ToErrorWithLock = (lock?: MailboxLockObject) => (error: unknown) =>
* @param to is the email to send to.
* @returns an {@link Email}.
*/
-type EmailGenerator = (
- from: EmailFromInstruction,
- to: EmailToInstruction,
-) => IO.IO<Email>;
-const generateEmail: EmailGenerator =
- (from: EmailFromInstruction, to: EmailToInstruction) => () => ({
- from: from.email,
- to: to.email,
- subject: [new Date().toISOString(), crypto.randomUUID()].join(" | "),
- text: crypto.randomUUID(),
- });
+type EmailGenerator = (from: EmailFromInstruction, to: EmailToInstruction) => IO.IO<Email>;
+const generateEmail: EmailGenerator = (from: EmailFromInstruction, to: EmailToInstruction) => () => ({
+ from: from.email,
+ to: to.email,
+ subject: [new Date().toISOString(), crypto.randomUUID()].join(" | "),
+ text: crypto.randomUUID()
+});
/**
* Get the transport layer for a mailbox to send a piece of mail.
* @param param0 is the mailbox to send from.
* @returns a function that takes an email and sends it.
*/
-type GetSendEmail = (
- from: EmailFromInstruction,
-) => (email: Email) => TE.TaskEither<Error, Email>;
-const getSendTransport: GetSendEmail = ({
- username,
- password,
- server,
- send_port,
-}) => {
+type GetSendEmail = (from: EmailFromInstruction) => (email: Email) => TE.TaskEither<Error, Email>;
+const getSendTransport: GetSendEmail = ({ username, password, server, send_port }) => {
const transport = createTransport({
host: server,
port: send_port,
auth: {
user: username,
- pass: password,
+ pass: password
},
tls: {
- rejectUnauthorized: false,
- },
+ rejectUnauthorized: false
+ }
});
return (email: Email) =>
TE.tryCatch(
@@ -98,9 +76,9 @@ const getSendTransport: GetSendEmail = ({
} else {
resolve(email);
}
- }),
+ })
),
- toError,
+ toError
);
};
@@ -109,9 +87,7 @@ const getSendTransport: GetSendEmail = ({
* @param param0 is the mailbox to read from.
* @returns a Right({@link ImapFlow}) if it connected, else an Left(error).
*/
-type GetImapClient = (
- to: EmailToInstruction,
-) => TE.TaskEither<Error, ImapClientI>;
+type GetImapClient = (to: EmailToInstruction) => TE.TaskEither<Error, ImapClientI>;
const getImap: GetImapClient = ({ username, password, server, read_port }) => {
const imap = new ImapFlow({
logger: false,
@@ -120,8 +96,8 @@ const getImap: GetImapClient = ({ username, password, server, read_port }) => {
secure: true,
auth: {
user: username,
- pass: password,
- },
+ pass: password
+ }
});
return TE.tryCatch(() => imap.connect().then(() => imap), toError);
};
@@ -130,18 +106,16 @@ const getImap: GetImapClient = ({ username, password, server, read_port }) => {
* @param imap is the Imap client to fetch messages from.
* @returns a Right({@link FetchMessageObject}[]) if successful, else a Left(error).
*/
-const fetchMessages = (
- imap: ImapClientI,
-): TE.TaskEither<Error, FetchMessageObject[]> =>
+const fetchMessages = (imap: ImapClientI): TE.TaskEither<Error, FetchMessageObject[]> =>
TE.tryCatch(
() =>
imap.fetchAll("*", {
uid: true,
envelope: true,
headers: true,
- bodyParts: ["text"],
+ bodyParts: ["text"]
}),
- toError,
+ toError
);
/**
@@ -152,8 +126,7 @@ const fetchMessages = (
type EmailMatcher = (email: Email) => (message: FetchMessageObject) => boolean;
const matchesEmail: EmailMatcher = (email) => (message) => {
const subjectMatches = email.subject === message.envelope.subject;
- const bodyMatches =
- message.bodyParts.get("text")?.toString().trim() === email.text.trim();
+ const bodyMatches = message.bodyParts.get("text")?.toString().trim() === email.text.trim();
const headers = message.headers.toLocaleString();
const fromMatches = headers.includes(`Return-Path: <${email.from}>`);
const toMatches = headers.includes(`Delivered-To: ${email.to}`);
@@ -173,13 +146,9 @@ type FindEmailUidInInbox = (
equalsEmail: (message: FetchMessageObject) => boolean,
retries: number,
pollIntervalMs: number,
+ logger?: Logger,
) => TE.TaskEither<Error, number>;
-const findEmailUidInInbox: FindEmailUidInInbox = (
- imap,
- equalsEmail,
- retries,
- pollIntervalMs,
-) =>
+const findEmailUidInInbox: FindEmailUidInInbox = (imap, equalsEmail, retries, pollIntervalMs, logger = ConsoleLogger) =>
pipe(
fetchMessages(imap),
TE.flatMap((messages) => {
@@ -192,20 +161,17 @@ const findEmailUidInInbox: FindEmailUidInInbox = (
TE.fold(
(e) =>
pipe(
- TE.fromIO(
- ConsoleLogger.log(`failed to find email; ${retries} retries left.`),
- ),
- TE.chain(() =>
- retries === 0
- ? TE.left(e)
- : T.delay(pollIntervalMs)(TE.right(null)),
- ),
- TE.chain(() =>
- findEmailUidInInbox(imap, equalsEmail, retries - 1, pollIntervalMs),
- ),
+ TE.fromIO(logger.log(`failed to find email; ${retries} retries left.`)),
+ TE.chain(() => (retries === 0 ? TE.left(e) : T.delay(pollIntervalMs)(TE.right(null)))),
+ TE.chain(() => findEmailUidInInbox(imap, equalsEmail, retries - 1, pollIntervalMs))
),
- TE.of,
- ),
+ (s) =>
+ pipe(
+ s,
+ TE.of,
+ TE.tap(() => TE.fromIO(logger.log("Email succeeded")))
+ )
+ )
);
export type EmailJobDependencies = {
@@ -227,32 +193,23 @@ export const perform = (
getSendImpl = getSendTransport,
getImapImpl = getImap,
findEmailUidInInboxImpl = findEmailUidInInbox,
- matchesEmailImpl = matchesEmail,
- }: Partial<EmailJobDependencies> = {},
+ matchesEmailImpl = matchesEmail
+ }: Partial<EmailJobDependencies> = {}
): TE.TaskEither<Error, boolean> =>
pipe(
// arrange.
TE.fromIO(generateEmailImpl(from, to)),
TE.bindTo("email"),
// act.
- TE.tap(({ email }) =>
- pipe(getSendImpl(from)(email), TE.mapLeft(ToErrorWithLock())),
- ),
+ TE.tap(({ email }) => pipe(getSendImpl(from)(email), TE.mapLeft(ToErrorWithLock()))),
TE.bind("imap", () => pipe(getImapImpl(to), TE.mapLeft(ToErrorWithLock()))),
- TE.bind("mailboxLock", ({ imap }) =>
- TE.tryCatch(() => imap.getMailboxLock("INBOX"), ToErrorWithLock()),
- ),
+ TE.bind("mailboxLock", ({ imap }) => TE.tryCatch(() => imap.getMailboxLock("INBOX"), ToErrorWithLock())),
// "assert".
TE.bind("uid", ({ imap, email, mailboxLock }) =>
pipe(
- findEmailUidInInboxImpl(
- imap,
- matchesEmailImpl(email),
- retries,
- interval,
- ),
- TE.mapLeft(ToErrorWithLock(mailboxLock)),
- ),
+ findEmailUidInInboxImpl(imap, matchesEmailImpl(email), retries, interval),
+ TE.mapLeft(ToErrorWithLock(mailboxLock))
+ )
),
// cleanup.
TE.bind("deleted", ({ imap, uid, mailboxLock }) =>
@@ -271,6 +228,6 @@ export const perform = (
({ mailboxLock, deleted }) => {
mailboxLock.release();
return TE.right(deleted);
- },
- ),
+ }
+ )
);