import PostalMime from "postal-mime";
import { env } from "cloudflare:workers";
import { loadUserById, toViewer } from "./auth";
import { hmacSha256Hex, timingSafeEqualStrings } from "./crypto";
import { first } from "./db";
import { HttpError } from "./errors";
import { createIssueComment } from "./issue-comments";
import type { IssueRow } from "./issues";
import { limitWrite } from "./limits";
import { loadRepoById, requireRepoMutation } from "./repos";
import { listAccountEmails } from "./user-emails";
import { extractReplyText } from "~/lib/email-reply-text";
export const REPLY_PREFIX = "reply+";
const MAC_LENGTH = 16;
const MAX_LOCAL_PART = 64;
const MAX_RAW_BYTES = 1024 * 1024;
const MAX_BODY_LENGTH = 65_536;
const ID = /^[a-z0-9]+$/;
const AUTOMATED_PRECEDENCE = /^(bulk|junk|list|auto_reply)$/i;
/** EMAIL_REPLY_DOMAIN, else the sending domain of EMAIL_FROM; empty (replies off) for localhost senders. */
export function replyDomain(): string {
const configured = (env.EMAIL_REPLY_DOMAIN ?? "").trim().toLowerCase();
if (configured) return configured;
const sender = /@([^>\s]+)/.exec(env.EMAIL_FROM ?? "")?.[1]?.toLowerCase() ?? "";
return sender.includes(".") && sender !== "localhost" ? sender : "";
}
async function replyMac(userId: string, issueId: string): Promise<string> {
return (await hmacSha256Hex(env.SECRETS_KEY, `email-reply:v1:${issueId}:${userId}`)).slice(0, MAC_LENGTH);
}
/**
* A per-recipient, per-thread reply address: `reply+<issue>.<user>.<mac>@<domain>`. The MAC binds the
* address to one user and one thread, so only mail that user received can be answered; null when
* replies are not configured.
*/
export async function replyAddressFor(userId: string, issueId: string): Promise<string | null> {
const domain = replyDomain();