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.

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