1import type { Hono } from "hono";2import { isSafePath, isVersion } from "../bucket/repo.ts";3import { notFound, type PageContext, pathUrl } from "./layout.ts";456const PANELS = new Map([7 ["issues", "/issues"],8 ["builds", "/releases"],9 ["archives", "/archives"],10 ["trust", ""],11]);1213const GONE = [14 "/tickets/*",15 "/drafts/*",16 "/join",17 "/cli-auth",18 "/api/cli-auth/*",19 "/api/repos/*",20 "/api/releases/upload",21 "/protocol/*",22 "/feed",23 "/:repo/feed",24];2526const RELEASE_ROUTES = new Set(["latest", "latest.json", "history.json"]);272829export function registerKepr2Routes(app: Hono, ctx: PageContext): void {30 for (const path of GONE) app.all(path, (c) => c.text("Gone", 410));31 app.get("/source", (c) => c.redirect("/kepr", 301));32 app.get("/:repo", async (c, next) => {33 const panel = PANELS.get(c.req.query("panel") ?? "");34 if (panel === undefined) return next();35 return c.redirect(`/${encodeURIComponent(c.req.param("repo"))}${panel}`, 301);36 });37 app.get("/:repo/releases/:version", async (c, next) => {38 const version = c.req.param("version");39 if (RELEASE_ROUTES.has(version)) return next();40 if (!isVersion(version)) return notFound(c, ctx);41 return c.redirect(`/${encodeURIComponent(c.req.param("repo"))}/releases#${version}`, 301);42 });43 app.get("/:repo/file", (c) => {44 const path = c.req.query("path") ?? "";45 if (!isSafePath(path)) return notFound(c, ctx);46 return c.redirect(`/${encodeURIComponent(c.req.param("repo"))}/files/${pathUrl(path)}`, 301);47 });48 app.get("/:repo/bundle", (c) => c.redirect(headTarball(c.req.param("repo")), 301));49 app.get("/:repo/bundle.blake3", (c) => c.redirect(headTarball(c.req.param("repo")), 301));50}5152const headTarball = (repo: string) => `/${encodeURIComponent(repo)}/archive/HEAD.tar.gz`;