import { Context } from "hono"; import { html, raw } from "hono/html"; import { Env } from "../types"; import { processEmailContent } from "../infrastructure/html-processor"; import { EmailAddress } from "../domain/value-objects/email-address"; import { formatBytes } from "../domain/format"; import { FeedRepository } from "../infrastructure/feed-repository"; import { FeedId } from "../domain/value-objects/feed-id"; import { isExpired } from "../domain/feed"; import entryCss from "../styles/entry.css"; 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 repo = FeedRepository.from(c.env); const id = FeedId.unchecked(feedId); const [feedMetadata, feedConfig] = await Promise.all([ repo.getMetadata(id), repo.getConfig(id), ]); if (!feedMetadata) { return new Response("Feed not found", { status: 404 }); } if (feedConfig && isExpired(feedConfig)) { 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 repo.getEmail(metaEntry.key); 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'", ); c.header("X-Robots-Tag", "noindex"); const bodyContent = processEmailContent( emailData.content, emailData.attachments, "", EmailAddress.parse(emailData.from)?.siteBaseUrl() ?? "", ); // Inline images render in place (cid: refs are rewritten by processEmailContent); // only genuine, downloadable attachments belong in the list below. const attachments = (emailData.attachments ?? []).filter((a) => !a.inline); const attachmentsSection = attachments.length ? html`

Attachments

` : ""; return c.html( html` ${emailData.subject}

${emailData.subject}

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