summaryrefslogtreecommitdiff
path: root/cgithub/src/app.ts
diff options
context:
space:
mode:
authorJoe Mou <dev@mou.fo>2026-08-27 14:08:06 -0400
committerJoe Mou <dev@mou.fo>2026-08-27 14:53:38 -0400
commit0d60b8c8235c3c07f675aba7a56dd37b62519f9e (patch)
tree8c94cef7b3530e72fc8542072b037081e5bb18ae /cgithub/src/app.ts
parent16dfeb61f79e6cd44c667422b8dc50f226269d80 (diff)
Display PDF blobs
GitHub renders a PDF with a viewer of its own and so ships no content for one, leaving the blob view with nothing to show. Its raw bytes come as application/octet-stream, which browsers download rather than display, so serve them from a new embed route that retypes them and point an <object> at that. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BErPR92xppzGFXkPqXPYj2
Diffstat (limited to 'cgithub/src/app.ts')
-rw-r--r--cgithub/src/app.ts26
1 files changed, 26 insertions, 0 deletions
diff --git a/cgithub/src/app.ts b/cgithub/src/app.ts
index 9781a98..33b408e 100644
--- a/cgithub/src/app.ts
+++ b/cgithub/src/app.ts
@@ -10,6 +10,7 @@ import {
getGitHubLatestCommit,
getGitHubOwner,
getGitHubPulls,
+ getGitHubRaw,
getGitHubRefs,
getGitHubRelease,
getGitHubReleases,
@@ -120,6 +121,31 @@ export function createApp(eta: Eta) {
);
});
+ // This is not a native GitHub route, but instead it lets us render blob
+ // content whose raw headers prevent hotlinking.
+ app.get("/:owner/:repo/embed/:type/:branch/:path{.*}", async (c) => {
+ const { owner, repo, type, branch, path } = c.req.param();
+
+ if (type !== "pdf") {
+ return c.notFound();
+ }
+
+ let response: Response;
+ try {
+ response = await getGitHubRaw(owner, repo, branch, path);
+ } catch (e) {
+ c.status(e instanceof GitHubHTTPError ? (e.status as StatusCode) : 500);
+ return c.body(null);
+ }
+
+ let contentType = response.headers.get("Content-Type");
+ if (contentType === null || contentType === "application/octet-stream") {
+ contentType = "application/pdf";
+ }
+
+ return new Response(response.body, { headers: { "Content-Type": contentType } });
+ });
+
app.get("/:owner/:repo/issues", async (c) => {
const { owner, repo } = c.req.param();
const q = c.req.query("q");