Diffinery

codician/diffinerypublic

Open-source, agent-first GitHub and GitHub Actions alternative that runs on your own Cloudflare account: git hosting, pull requests, code review and drop-in GitHub Actions YAML on Workers, Durable Objects, R2 and Containers.

96 lines · 4.3 KBRaw History
1/** @jsxImportSource hono/jsx */
2import { Hono } from "hono";
3import type { Env } from "../env.js";
4import type { AppVars } from "../auth/principal.js";
5import { registerAccount } from "./account.js";
6import { registerAccountLifecycle } from "./account-lifecycle.js";
7import { registerTasks } from "../agentic/tasks-web.js";
8import { registerOrgAgents } from "../agentic/org-web.js";
9import { registerReviews } from "../agentic/review-web.js";
10import { registerProject } from "../agentic/project-web.js";
11import { registerTerminals } from "../agentic/terminal-web.js";
12import { registerMailWeb } from "../mail/web.js";
13import { registerSecurityReports } from "./security-reports.js";
14import { registerStatus } from "./status.js";
15import { registerAuth } from "./auth.js";
16import { registerCliLogin } from "./cli-login.js";
17import { registerOAuth } from "./oauth.js";
18import { registerActions } from "./actions.js";
19import { registerCode } from "./code.js";
20import { registerCollab } from "./collab.js";
21import { registerReleases } from "./releases.js";
22import { registerRepoSettings } from "./repo-settings.js";
23import { registerImports } from "./imports.js";
24import { registerSeo } from "./seo.js";
25import { errorPage } from "./ui.js";
26import { RESERVED_LOGINS } from "../lib/util.js";
27import { registerKumoAssets } from "./kumo.js";
28import { registerPackages } from "./packages.js";
29import { registerDocs } from "../docs/web.js";
30import { registerModeration } from "./moderation.js";
31
32/**
33 * Server-rendered web UI. Mount at "/" after the API, Git and runner routes:
34 * it owns `/`, `/docs/*`, `/llms.txt`, `/login`, `/auth/*`, `/signin/*`, `/new`, `/settings/*`, `/notifications/*`, `/invite/*`, `/-/*`,
35 * `/:owner` and `/:owner/:repo/...` (including `/:owner/:repo/raw/*`).
36 *
37 * Expects the `principal` and `dir` variables to be set by an earlier middleware.
38 * Every POST is CSRF-checked (double-submit cookie, see ./csrf.ts).
39 */
40export const web = new Hono<{ Bindings: Env; Variables: AppVars }>();
41
42web.onError((err, c) => errorPage(c, err));
43
44/**
45 * A limited API token (read or write scope, or limited to some repositories) reaches the web only
46 * for repository pages (/:owner/:repo/...), where loadRepo applies its scope and repository limits as
47 * the API does. Everywhere else (the dashboard, the command palette, notifications, profiles,
48 * settings, site administration) it would see what the API refuses it, so those pages treat it as
49 * signed out. An unlimited admin token can already read all of that through the API.
50 */
51export function tokenMayUseWebPage(path: string): boolean {
52 const segs = path.split("/").filter(Boolean);
53 return segs.length >= 2 && !RESERVED_LOGINS.has(segs[0]!.toLowerCase()) && segs[1] !== "-";
54}
55
56web.use("*", async (c, next) => {
57 const p = c.get("principal");
58 const limited = p.type === "account" && p.via === "token" && (!!p.repoIds || !p.scopes.includes("admin"));
59 if (limited && !tokenMayUseWebPage(c.req.path)) c.set("principal", { type: "anonymous" });
60 await next();
61});
62
63// Browsers and crawlers ask for these on every origin; keep them off the /:owner route.
64registerSeo(web);
65// Public docs (/docs, /llms.txt, ...): before /:owner/:repo, which would otherwise match /docs/<page>.
66registerDocs(web);
67
68// Order matters: fixed paths (/settings/tokens, /notifications/:id, …) must be
69// registered before the /:owner/:repo patterns they would otherwise match.
70registerKumoAssets(web);
71registerAuth(web);
72registerCliLogin(web);
73registerOAuth(web);
74registerPackages(web);
75registerMailWeb(web);
76registerSecurityReports(web);
77// /status: before the /:owner catch-all.
78registerStatus(web);
79// Before registerAccount: its /:owner catch-all would otherwise swallow /imports, /new/import and /settings/linked-accounts.
80registerImports(web);
81// Settings → Model access and /:owner/:repo/tasks: before the /:owner and /:owner/:repo catch-alls.
82registerTasks(web);
83registerOrgAgents(web);
84registerReviews(web);
85registerProject(web);
86registerTerminals(web);
87// Settings → Account and the account pages of site administration: fixed /settings paths, before /:owner.
88registerAccountLifecycle(web);
89// /-/report and /:owner/:repo/comments/:id/...: before the /:owner and /:owner/:repo catch-alls.
90registerModeration(web);
91registerAccount(web);
92registerRepoSettings(web);
93registerActions(web);
94registerReleases(web);
95registerCollab(web);
96registerCode(web);