archives.ts

2.5 KB · raw

1import type { Hono } from "hono";2import { fileAt, headId, history, type Save, saveByPrefix } from "../mirror/queries.ts";3import { type Html, html } from "./html.ts";4import {5	cacheFor,6	notFound,7	openPublicRepo,8	type PageContext,9	type PublicRepo,10	page,11	relativeTime,12} from "./layout.ts";1314const RECENT = 20;1516export function registerArchivesPage(app: Hono, ctx: PageContext): void {17	app.get("/:repo/archives", (c) => {18		const repo = openPublicRepo(ctx, c.req.param("repo"));19		if (!repo) return notFound(c, ctx);20		const name = repo.settings.name;21		const id = headId(repo.mirror);22		const head = id ? saveByPrefix(repo.mirror, id) : null;23		const recent = history(repo.mirror, RECENT + 1, 0).filter((save) => save.id !== head?.id);24		cacheFor(c, false);25		return c.html(26			page(ctx, {27				title: `archives · ${name}`,28				description: `Source tarballs of ${name}, one per save.`,29				path: `/${name}/archives`,30				body: html`<p class="breadcrumb mono"><a href="/${name}">${name}</a></p>31<h1 class="file-page-title">archives</h1>32<section class="repo-section">33${head ? [row(ctx, repo, head, true), ...recent.slice(0, RECENT).map((save) => row(ctx, repo, save, false))] : html`<p class="muted mono save-empty">no saves yet</p>`}34</section>`,35			}),36		);37	});38}3940// HEAD's row names HEAD.tar.gz, which always follows the newest save; the others pin their save.41// The `nix profile add` line shows only where the save has a flake to install, as in Kepr 2.42function row(ctx: PageContext, repo: PublicRepo, save: Save, isHead: boolean): Html {43	const name = repo.settings.name;44	const file = isHead ? "HEAD" : save.id;45	const tarball = `/${name}/archive/${file}.tar.gz`;46	const nix = `nix profile add https://${ctx.domain}${tarball}`;47	const hasFlake = fileAt(repo.mirror, save.id, "flake.nix") !== null;48	// HEAD can be a checkpoint, which has no page of its own (§21.4).49	const id = save.is_checkpoint50		? html`<span class="history-id">${save.id}</span>`51		: html`<a class="history-id" href="/${name}/saves/${save.id}">${save.id}</a>`;52	return html`<div class="archive-row">53<p class="archive-save">${isHead && html`<span class="archive-head mono">HEAD</span>`}${id}<span class="history-msg archive-msg">${save.message.split("\n")[0]}</span><span class="history-when">${relativeTime(save.created_at)}</span></p>54<p class="archive-links mono"><a href="${tarball}">↓ ${name}-${isHead ? "HEAD" : save.id.slice(0, 8)}.tar.gz</a></p>55${hasFlake && html`<div class="install-line"><code>${nix}</code><button type="button" class="copy-btn" data-copy="${nix}" hidden>copy</button></div>`}56</div>`;57}