diff options
| author | Joe Mou <dev@mou.fo> | 2026-08-27 14:08:06 -0400 |
|---|---|---|
| committer | Joe Mou <dev@mou.fo> | 2026-08-27 14:53:38 -0400 |
| commit | 4797a627f461204b289f95f61b64e3dab8bc00ff (patch) | |
| tree | a044ef3d0419c95aad9fa98a093e0c39bdce5459 /src/app.ts | |
| parent | 7c928d30b16b37624841c4615c0d4bda426e4159 (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 'src/app.ts')
| -rw-r--r-- | src/app.ts | 26 |
1 files changed, 26 insertions, 0 deletions
@@ -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"); |
