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
${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(bodyContent)}
${attachmentsSection}
`,
);
}