import { Hono } from "hono"; import { Env, FeedConfig, FeedMetadata, EmailData, EmailMetadata, } from "../../types"; import { logger } from "../../lib/logger"; import { Layout, clampText } from "./ui"; import { deleteKeysWithConcurrency } from "./helpers"; import { feedRssUrl, feedAtomUrl, feedEmailAddress } from "../../utils/urls"; import { getAttachmentBucket } from "../../utils/attachments"; import { emailsPageScript } from "../../scripts/generated/emails-page"; type AppEnv = { Bindings: Env }; export const emailsRouter = new Hono(); // ── Shared SVG icons ────────────────────────────────────────────────────────── const CopyIcon = () => ( ); const CheckIcon = () => ( ); type CopyFieldProps = { label: string; value: string; display?: string; }; const CopyField = ({ label, value, display }: CopyFieldProps) => (
{label}
{display ?? value}
); function extractSenderEmail(from: string): string { const match = from.match(/<([^>]+@[^>]+)>/); return match ? match[1].trim().toLowerCase() : from.trim().toLowerCase(); } type SenderFieldProps = { from: string; feedId: string; }; const SenderField = ({ from, feedId }: SenderFieldProps) => { const senderEmail = extractSenderEmail(from); const senderDomain = senderEmail.split("@")[1] || ""; return (
From:
{from}
); }; // ── View all emails for a feed ──────────────────────────────────────────────── emailsRouter.get("/feeds/:feedId/emails", async (c) => { const env = c.env; const emailStorage = env.EMAIL_STORAGE; const feedId = c.req.param("feedId"); const message = c.req.query("message"); const count = Number(c.req.query("count") || "0"); const feedConfig = (await emailStorage.get(`feed:${feedId}:config`, { type: "json", })) as FeedConfig | null; const feedMetadata = (await emailStorage.get(`feed:${feedId}:metadata`, { type: "json", })) as FeedMetadata | null; if (!feedConfig || !feedMetadata) { return c.text("Feed not found", 404); } const emailAddress = feedEmailAddress(feedId, env); const rssUrl = feedRssUrl(feedId, env); const atomUrl = feedAtomUrl(feedId, env); return c.html(

{feedConfig.title} - Emails

Feed Details

Emails ( {feedMetadata.emails.length})

{message === "bulkDeleted" && (

Deleted {Number.isFinite(count) ? count : 0} email(s).

)} {message === "bulkDeleteNoop" && (

No emails were selected.

)} {feedMetadata.emails.length > 0 ? (
Showing {feedMetadata.emails.length} 0 selected
{feedMetadata.emails.map((email: EmailMetadata) => { const subjectDisplay = clampText(email.subject, 180); const subjectHover = clampText(email.subject, 1000); const attachmentCount = email.attachmentIds?.length ?? 0; const attachmentLabel = `${attachmentCount} attachment${ attachmentCount > 1 ? "s" : "" }`; const sortSubject = subjectHover.toLowerCase(); const sortReceivedAt = String(email.receivedAt); const searchHaystack = clampText( email.subject, 320, ).toLowerCase(); return ( ); })}
Actions
) : (

No emails received yet. Subscribe to newsletters using the email address above.

)}
{/* Config bootstrap — injects dynamic server-side data before the static compiled script */}