import { Context } from "hono"; import { html, raw } from "hono/html"; import { Env, FeedConfig, FeedMetadata, EmailData } from "../types"; import { processEmailContent } from "../utils/html-processor"; import { formatBytes } from "../utils/format"; export async function handle(c: Context<{ Bindings: Env }>): Promise { const feedId = c.req.param("feedId"); const receivedAt = parseInt(c.req.param("entryId") ?? "", 10); if (!feedId || isNaN(receivedAt)) { return new Response("Not Found", { status: 404 }); } const emailStorage = c.env.EMAIL_STORAGE; const [feedMetadata, feedConfig] = await Promise.all([ emailStorage.get( `feed:${feedId}:metadata`, "json", ) as Promise, emailStorage.get( `feed:${feedId}:config`, "json", ) as Promise, ]); if (!feedMetadata) { return new Response("Feed not found", { status: 404 }); } if ( feedConfig?.expires_at !== undefined && feedConfig.expires_at <= Date.now() ) { return new Response("Feed has expired", { status: 410 }); } const metaEntry = feedMetadata.emails.find( (e) => e.receivedAt === receivedAt, ); if (!metaEntry) { return new Response("Entry not found", { status: 404 }); } const emailData = (await emailStorage.get( metaEntry.key, "json", )) as EmailData | null; if (!emailData) { return new Response("Entry not found", { status: 404 }); } c.header( "Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; img-src *; frame-src 'none'", ); const attachments = emailData.attachments ?? []; const attachmentsSection = attachments.length ? html`

Attachments

    ${attachments.map( (a) => html`
  • ${a.filename} ${formatBytes(a.size)}
  • `, )}
` : ""; return c.html( html` ${emailData.subject}

${emailData.subject}

From:
${emailData.from}
Date:
${new Date(emailData.receivedAt).toUTCString()}
${raw( processEmailContent(emailData.content, emailData.attachments), )}
${attachmentsSection} `, ); }