archive.ts

2.1 KB · raw

1import type { Hono } from "hono";2import type { Bucket } from "../bucket/reader.ts";3import { headId, saveByPrefix } from "../mirror/queries.ts";4import { notFound, openPublicRepo, type PageContext } from "../pages/layout.ts";5import { countInstall } from "../releases/releases.ts";6import { cachedTarball } from "./cache.ts";78// Long-lived but never `immutable` (§18): a strip can change what a save's tarball holds. `private`,9// so no shared cache keeps it after the repo turns private (§20).10const TARBALL_CACHE = "private, max-age=31536000";1112export function registerTarballRoutes(app: Hono, ctx: PageContext, bucket: Bucket): void {13	app.get("/:repo/archive/HEAD.tar.gz", (c) => {14		const repo = openPublicRepo(ctx, c.req.param("repo"));15		const head = repo && headId(repo.mirror);16		if (!repo || !head) return notFound(c, ctx);17		const path = `/${repo.settings.name}/archive/${head}.tar.gz`;18		// Nix pins the immutable link in its lock file, so it must be absolute.19		c.header("Link", `<https://${ctx.domain}${path}>; rel="immutable"`);20		c.header("Cache-Control", "no-cache");21		return c.redirect(path, 302);22	});2324	app.get("/:repo/archive/:id{[0-9a-f]+\\.tar\\.gz}", async (c) => {25		const repo = openPublicRepo(ctx, c.req.param("repo"));26		const id = c.req.param("id").replace(/\.tar\.gz$/, "");27		const save = repo && saveByPrefix(repo.mirror, id);28		if (!repo || !save) return notFound(c, ctx);29		const name = repo.settings.name;30		if (save.id !== id) {31			c.header("Cache-Control", "public, max-age=60");32			return c.redirect(`/${name}/archive/${save.id}.tar.gz`, 302);33		}34		const bytes = await cachedTarball(bucket, repo.mirror, name, save.id);35		if (!bytes) return notFound(c, ctx);36		// §22.2: a Nix fetch is an install. Only the tarball itself counts, not the redirects to it.37		if (c.req.header("User-Agent")?.includes("nix/")) countInstall(ctx.kepr, name);38		c.header("Content-Type", "application/gzip");39		c.header("Content-Disposition", `attachment; filename="${name}-${save.id.slice(0, 8)}.tar.gz"`);40		c.header("Cache-Control", TARBALL_CACHE);41		return c.body(bytes.slice());42	});43}