Files
kill-the-news/src/routes/files.ts
T
Julien Herr f150d40c45 feat(attachments): R2 toggle, storage metrics, and demo R2 config
Add an ATTACHMENTS_ENABLED switch (default on when R2 is bound) via a
central getAttachmentBucket helper, surface R2 + estimated KV usage
against the free tier on the status page and /api/stats (refreshed by the
hourly cron), let setup.sh create and wire the R2 bucket, and bind the
demo bucket so the deployed demo has attachments.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 17:33:50 +02:00

38 lines
1.0 KiB
TypeScript

import { Context } from "hono";
import { Env } from "../types";
import { getAttachmentBucket } from "../utils/attachments";
export async function handle(c: Context<{ Bindings: Env }>): Promise<Response> {
const bucket = getAttachmentBucket(c.env);
if (!bucket) {
return new Response("Attachment storage not configured", { status: 404 });
}
const attachmentId = c.req.param("attachmentId");
const filename = c.req.param("filename");
if (!attachmentId || !filename) {
return new Response("Not found", { status: 404 });
}
const object = await bucket.get(attachmentId);
if (!object) {
return new Response("Not found", { status: 404 });
}
const headers = new Headers();
object.writeHttpMetadata(headers);
headers.set("etag", object.httpEtag);
headers.set("Cache-Control", "public, max-age=31536000, immutable");
if (!headers.get("Content-Disposition")) {
headers.set(
"Content-Disposition",
`attachment; filename="${decodeURIComponent(filename)}"`,
);
}
return new Response(object.body, { headers });
}