Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesPull RequestsActionsSecurityInsightsSettings
✨ AI
More
Blame · Line-by-line history

app.tsx

Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.

app.tsxBlame450 lines · 2 contributors
79136bbClaude1import { Hono } from "hono";
2import { logger } from "hono/logger";
3import { cors } from "hono/cors";
05b973eClaude4import { compress } from "hono/compress";
79136bbClaude5import { Layout } from "./views/layout";
80bed05Claude6import { reportError } from "./lib/observability";
3ef4c9dClaude7import { requestContext } from "./middleware/request-context";
8import { rateLimit } from "./middleware/rate-limit";
79136bbClaude9import gitRoutes from "./routes/git";
10import apiRoutes from "./routes/api";
45e31d0Claude11import apiV2Routes from "./routes/api-v2";
12import apiDocsRoutes from "./routes/api-docs";
79136bbClaude13import authRoutes from "./routes/auth";
14import settingsRoutes from "./routes/settings";
7298a17Claude15import settings2faRoutes from "./routes/settings-2fa";
79136bbClaude16import issueRoutes from "./routes/issues";
17import repoSettings from "./routes/repo-settings";
23d1a81Claude18import collaboratorRoutes from "./routes/collaborators";
04f6b7fClaude19import teamCollaboratorRoutes from "./routes/team-collaborators";
20import invitesRoutes from "./routes/invites";
21import liveEventsRoutes from "./routes/live-events";
79136bbClaude22import compareRoutes from "./routes/compare";
0074234Claude23import pullRoutes from "./routes/pulls";
24import editorRoutes from "./routes/editor";
c81ab7aClaude25import forkRoutes from "./routes/fork";
26import webhookRoutes from "./routes/webhooks";
27import exploreRoutes from "./routes/explore";
28import tokenRoutes from "./routes/tokens";
43de941Claude29import contributorRoutes from "./routes/contributors";
699e5c7Claude30import healthRoutes from "./routes/health-probe";
31import healthDashboardRoutes from "./routes/health";
2316be6Claude32import statusRoutes from "./routes/status";
80bed05Claude33import helpRoutes from "./routes/help";
b0148e9Claude34import marketingRoutes from "./routes/marketing";
5f2e749Claude35import pricingRoutes from "./routes/pricing";
5618f9aClaude36import seoRoutes from "./routes/seo";
05cdb85Claude37import versionRoutes from "./routes/version";
f295f78Dictation App38import { platformStatus } from "./routes/platform-status";
52ad8b1Claude39import publicStatsRoutes from "./routes/public-stats";
40import demoRoutes from "./routes/demo";
16b325cClaude41import insightRoutes from "./routes/insights";
f1ab587Claude42import dashboardRoutes from "./routes/dashboard";
36b4cbdClaude43import legalRoutes from "./routes/legal";
bdbd0deClaude44import importRoutes from "./routes/import";
14c3cc8Claude45import importBulkRoutes from "./routes/import-bulk";
46import migrationRoutes from "./routes/migrations";
47import specsRoutes from "./routes/specs";
79136bbClaude48import webRoutes from "./routes/web";
0316dbbClaude49import hookRoutes from "./routes/hooks";
50import eventsRoutes from "./routes/events";
51import passkeyRoutes from "./routes/passkeys";
52import oauthRoutes from "./routes/oauth";
53import developerAppsRoutes from "./routes/developer-apps";
54import themeRoutes from "./routes/theme";
55import auditRoutes from "./routes/audit";
56import reactionRoutes from "./routes/reactions";
57import savedReplyRoutes from "./routes/saved-replies";
58import deploymentRoutes from "./routes/deployments";
59import orgRoutes from "./routes/orgs";
60import notificationRoutes from "./routes/notifications";
61import onboardingRoutes from "./routes/onboarding";
62import adminRoutes from "./routes/admin";
f764c07Claude63import adminDeploysRoutes from "./routes/admin-deploys";
64import adminDeploysPageRoutes from "./routes/admin-deploys-page";
0316dbbClaude65import advisoriesRoutes from "./routes/advisories";
66import aiChangelogRoutes from "./routes/ai-changelog";
67import aiExplainRoutes from "./routes/ai-explain";
68import aiTestsRoutes from "./routes/ai-tests";
69import askRoutes from "./routes/ask";
70import billingRoutes from "./routes/billing";
6778ad2Claude71import stripeWebhookRoutes from "./routes/stripe-webhook";
0316dbbClaude72import codeScanningRoutes from "./routes/code-scanning";
73import commitStatusesRoutes from "./routes/commit-statuses";
74import copilotRoutes from "./routes/copilot";
75import depUpdaterRoutes from "./routes/dep-updater";
76import depsRoutes from "./routes/deps";
77import discussionsRoutes from "./routes/discussions";
78import environmentsRoutes from "./routes/environments";
79import followsRoutes from "./routes/follows";
80import gatesRoutes from "./routes/gates";
81import gistsRoutes from "./routes/gists";
82import graphqlRoutes from "./routes/graphql";
2c2163eClaude83import mcpRoutes from "./routes/mcp";
0316dbbClaude84import marketplaceRoutes from "./routes/marketplace";
85import mergeQueueRoutes from "./routes/merge-queue";
86import mirrorsRoutes from "./routes/mirrors";
87import orgInsightsRoutes from "./routes/org-insights";
88import packagesRoutes from "./routes/packages";
89import packagesApiRoutes from "./routes/packages-api";
90import pagesRoutes from "./routes/pages";
91import projectsRoutes from "./routes/projects";
92import protectedTagsRoutes from "./routes/protected-tags";
93import pwaRoutes from "./routes/pwa";
46d6165Claude94import installRoutes from "./routes/install";
0316dbbClaude95import releasesRoutes from "./routes/releases";
96import requiredChecksRoutes from "./routes/required-checks";
97import rulesetsRoutes from "./routes/rulesets";
98import searchRoutes from "./routes/search";
99import semanticSearchRoutes from "./routes/semantic-search";
100import signingKeysRoutes from "./routes/signing-keys";
101import sponsorsRoutes from "./routes/sponsors";
102import ssoRoutes from "./routes/sso";
46d6165Claude103import githubOauthRoutes from "./routes/github-oauth";
0316dbbClaude104import symbolsRoutes from "./routes/symbols";
105import templatesRoutes from "./routes/templates";
106import trafficRoutes from "./routes/traffic";
107import wikisRoutes from "./routes/wikis";
108import workflowsRoutes from "./routes/workflows";
5ff9cc2Claude109import workflowArtifactsRoutes from "./routes/workflow-artifacts";
110import workflowSecretsRoutes from "./routes/workflow-secrets";
46d6165Claude111import sleepModeRoutes from "./routes/sleep-mode";
52ad8b1Claude112import vsGithubRoutes from "./routes/vs-github";
59b6fb2Claude113import { authRateLimit, gitRateLimit, searchRateLimit } from "./middleware/rate-limit";
114import { csrfToken, csrfProtect } from "./middleware/csrf";
79136bbClaude115
2316901Claude116import type { AuthEnv } from "./middleware/auth";
117
118const app = new Hono<AuthEnv>();
79136bbClaude119
3ef4c9dClaude120// Request context (request ID, start time) runs before everything else
121app.use("*", requestContext);
05b973eClaude122// Middleware — compression first (wraps all responses)
123app.use("*", compress());
124// Logger only on non-git routes to avoid overhead on clone/push
125app.use("*", async (c, next) => {
126 if (c.req.path.includes(".git/")) return next();
127 return logger()(c, next);
128});
79136bbClaude129app.use("/api/*", cors());
290ea77Claude130
36cc17aClaude131// Force-revalidate HTML on every request — kills browser cache holding stale
290ea77Claude132// pre-redesign markup. JSON / static assets keep their own cache rules; only
133// text/html responses get the no-cache stamp. Without this, every push to
134// main left users staring at cached 80s-looking pages from before the design
135// landed.
36cc17aClaude136//
137// We deliberately use `private, no-cache, must-revalidate` rather than
138// `no-store`. `no-store` disables Safari/Chrome's back-forward cache (bfcache),
139// which makes every Back/Forward press a cold server round-trip — that
140// contributed to the "every nav feels like a fresh login" UX complaint.
141// `no-cache` still revalidates on direct fetch but lets bfcache hold the
142// page in memory between navigations.
290ea77Claude143app.use("*", async (c, next) => {
144 await next();
145 const ct = c.res.headers.get("content-type") || "";
146 if (ct.startsWith("text/html")) {
36cc17aClaude147 c.header("cache-control", "private, no-cache, must-revalidate");
290ea77Claude148 }
149});
3ef4c9dClaude150// Rate-limit API + auth endpoints (generous default)
0316dbbClaude151app.use("/api/*", rateLimit(120, 60_000, "api"));
152app.use("/login", rateLimit(20, 60_000, "login"));
153app.use("/register", rateLimit(10, 60_000, "register"));
79136bbClaude154
59b6fb2Claude155// CSRF protection — set token on all requests, validate on mutations
156app.use("*", csrfToken);
157app.use("*", csrfProtect);
158
45e31d0Claude159// Rate limit auth routes
160app.use("/login", authRateLimit);
161app.use("/register", authRateLimit);
162
59b6fb2Claude163// Rate limit git operations
164app.use("/:owner/:repo.git/*", gitRateLimit);
165
166// Rate limit search
167app.use("/:owner/:repo/search", searchRateLimit);
168app.use("/explore", searchRateLimit);
79136bbClaude169
170// Git Smart HTTP protocol routes (must be before web routes)
171app.route("/", gitRoutes);
172
45e31d0Claude173// REST API v1 (legacy)
79136bbClaude174app.route("/", apiRoutes);
175
52ad8b1Claude176// Block L3 — /demo + /api/v2/demo/* live demo endpoints. Mounted BEFORE
177// apiV2Routes so the /api/v2/demo/* JSON endpoints win over the v2 base
178// router's catch-shape, and BEFORE adminRoutes so the live /demo page
179// wins over the legacy /demo redirect in src/routes/admin.tsx.
180app.route("/", demoRoutes);
181
0316dbbClaude182// REST API v2 (basePath /api/v2)
183app.route("/", apiV2Routes);
184
ad6d4adClaude185// Inbound API hooks (GateTest callback + backup PAT-authed /api/v1/gate-runs)
186app.route("/", hookRoutes);
21f8dbdClaude187app.route("/api/events", eventsRoutes);
ad6d4adClaude188
45e31d0Claude189// API documentation
190app.route("/", apiDocsRoutes);
79136bbClaude191
192// Auth routes (register, login, logout)
193app.route("/", authRoutes);
194
195// Settings routes (profile, SSH keys)
196app.route("/", settingsRoutes);
197
7298a17Claude198// 2FA / TOTP settings (Block B4)
199app.route("/", settings2faRoutes);
200
2df1f8cClaude201// WebAuthn / passkey routes (Block B5)
202app.route("/", passkeyRoutes);
203
058d752Claude204// OAuth 2.0 provider (Block B6)
205app.route("/", oauthRoutes);
206app.route("/", developerAppsRoutes);
207
6fc53bdClaude208// Theme toggle (dark/light cookie)
209app.route("/", themeRoutes);
210
211// Audit log UI
212app.route("/", auditRoutes);
213
214// Reactions API (issues, PRs, comments)
215app.route("/", reactionRoutes);
216
24cf2caClaude217// Saved replies (per-user canned comment templates)
218app.route("/", savedReplyRoutes);
219
220// Environments + deployment history UI
221app.route("/", deploymentRoutes);
222
6563f0aClaude223// Organizations + teams (Block B1)
224app.route("/", orgRoutes);
225
c81ab7aClaude226// API tokens
227app.route("/", tokenRoutes);
228
59b6fb2Claude229// Notifications
3ef4c9dClaude230app.route("/", notificationRoutes);
231
79136bbClaude232// Repo settings (description, visibility, delete)
233app.route("/", repoSettings);
234
23d1a81Claude235// Repo collaborators (add/list/remove)
236app.route("/", collaboratorRoutes);
237
04f6b7fClaude238// Team-based repo collaborators (invite a whole team)
239app.route("/", teamCollaboratorRoutes);
240
241// Collaborator invite accept flow (token-based)
242app.route("/", invitesRoutes);
243
244// Real-time SSE endpoint (topic-based live updates)
245app.route("/", liveEventsRoutes);
246
c81ab7aClaude247// Webhooks management
248app.route("/", webhookRoutes);
249
79136bbClaude250// Compare view (branch diffs)
251app.route("/", compareRoutes);
252
253// Issue tracker
254app.route("/", issueRoutes);
255
0074234Claude256// Pull requests
257app.route("/", pullRoutes);
258
c81ab7aClaude259// Fork
260app.route("/", forkRoutes);
261
0074234Claude262// Web file editor
263app.route("/", editorRoutes);
264
43de941Claude265// Contributors
266app.route("/", contributorRoutes);
267
699e5c7Claude268// Health liveness + metrics endpoints
2c34075Claude269app.route("/", healthRoutes);
270
f295f78Dictation App271// Cross-product platform status (public, CORS-open — see docs/PLATFORM_STATUS.md)
272app.route("/api/platform-status", platformStatus);
273
52ad8b1Claude274// Block L4 — Public stats counters (powers landing-page social proof)
275app.route("/", publicStatsRoutes);
276
277// Block L3 — Live /demo page + /api/v2/demo/* endpoints
278app.route("/", demoRoutes);
279
2316be6Claude280// Public /status — human-readable platform health page
281app.route("/", statusRoutes);
282
80bed05Claude283// /help — quickstart + API cheatsheet
284app.route("/", helpRoutes);
285
5f2e749Claude286// L8 — public /pricing page (free-tier polish). Mounted BEFORE marketing
287// so the new editorial pricing layout wins the route; the legacy marketing
288// pricing remains as a safety net but is shadowed at the router.
289app.route("/", pricingRoutes);
290
b0148e9Claude291// /pricing, /features, /about — marketing surface
292app.route("/", marketingRoutes);
293
5618f9aClaude294// SEO: robots.txt + sitemap.xml
295app.route("/", seoRoutes);
296
05cdb85Claude297// /api/version — live build SHA + uptime; client poller uses this to
298// surface 'New version available — reload' banners on deploy.
299app.route("/", versionRoutes);
300
699e5c7Claude301// Health dashboard (per-repo health page)
302app.route("/", healthDashboardRoutes);
303
16b325cClaude304// Insights (time-travel, dependencies, rollback)
305app.route("/", insightRoutes);
306
f1ab587Claude307// Command center dashboard
308app.route("/", dashboardRoutes);
309
36b4cbdClaude310// Legal pages (terms, privacy, AUP)
311app.route("/", legalRoutes);
312
bdbd0deClaude313// GitHub import / migration
314app.route("/", importRoutes);
14c3cc8Claude315app.route("/", importBulkRoutes);
316app.route("/", migrationRoutes);
317
318// Spec-to-PR (experimental AI-generated draft PRs)
319app.route("/", specsRoutes);
bdbd0deClaude320
c81ab7aClaude321// Explore page
322app.route("/", exploreRoutes);
323
59b6fb2Claude324// Onboarding
325app.route("/", onboardingRoutes);
326
0316dbbClaude327// Admin + feature routes
328app.route("/", adminRoutes);
f764c07Claude329app.route("/", adminDeploysRoutes);
330app.route("/", adminDeploysPageRoutes);
0316dbbClaude331app.route("/", advisoriesRoutes);
332app.route("/", aiChangelogRoutes);
333app.route("/", aiExplainRoutes);
334app.route("/", aiTestsRoutes);
335app.route("/", askRoutes);
336app.route("/", billingRoutes);
6778ad2Claude337app.route("/", stripeWebhookRoutes);
0316dbbClaude338app.route("/", codeScanningRoutes);
339app.route("/", commitStatusesRoutes);
340app.route("/", copilotRoutes);
341app.route("/", depUpdaterRoutes);
342app.route("/", depsRoutes);
343app.route("/", discussionsRoutes);
344app.route("/", environmentsRoutes);
345app.route("/", followsRoutes);
346app.route("/", gatesRoutes);
347app.route("/", gistsRoutes);
348app.route("/", graphqlRoutes);
2c2163eClaude349app.route("/", mcpRoutes);
0316dbbClaude350app.route("/", marketplaceRoutes);
351app.route("/", mergeQueueRoutes);
352app.route("/", mirrorsRoutes);
353app.route("/", orgInsightsRoutes);
354app.route("/", packagesRoutes);
355app.route("/", packagesApiRoutes);
356app.route("/", pagesRoutes);
357app.route("/", projectsRoutes);
358app.route("/", protectedTagsRoutes);
359app.route("/", pwaRoutes);
46d6165Claude360app.route("/", installRoutes);
0316dbbClaude361app.route("/", releasesRoutes);
362app.route("/", requiredChecksRoutes);
363app.route("/", rulesetsRoutes);
364app.route("/", searchRoutes);
365app.route("/", semanticSearchRoutes);
366app.route("/", signingKeysRoutes);
367app.route("/", sponsorsRoutes);
368app.route("/", ssoRoutes);
46d6165Claude369app.route("/", githubOauthRoutes);
0316dbbClaude370app.route("/", symbolsRoutes);
371app.route("/", templatesRoutes);
372app.route("/", trafficRoutes);
373app.route("/", wikisRoutes);
374app.route("/", workflowsRoutes);
5ff9cc2Claude375app.route("/", workflowArtifactsRoutes);
376app.route("/", workflowSecretsRoutes);
46d6165Claude377app.route("/", sleepModeRoutes);
52ad8b1Claude378app.route("/", vsGithubRoutes);
0316dbbClaude379
79136bbClaude380// Web UI (catch-all, must be last)
381app.route("/", webRoutes);
382
383// Global 404
384app.notFound((c) => {
36cc17aClaude385 const user = c.get("user") ?? null;
79136bbClaude386 return c.html(
36cc17aClaude387 <Layout title="Not Found" user={user}>
e1db10eClaude388 <div class="error-page">
389 <div class="error-page-code">404</div>
390 <div class="eyebrow">Not found</div>
391 <h1 class="display error-page-title">
392 That page <span class="gradient-text">isn't here.</span>
393 </h1>
394 <p class="error-page-sub">
395 The URL might be wrong, the resource might have moved, or you
396 might not have permission to see it.
397 </p>
398 <div class="error-page-actions">
399 <a href="/" class="btn btn-primary btn-lg">Go home</a>
400 <a href="/explore" class="btn btn-ghost btn-lg">Explore repos</a>
401 </div>
402 <div class="error-page-meta">
403 <span class="meta-mono">{c.req.method} {c.req.path}</span>
404 </div>
79136bbClaude405 </div>
406 </Layout>,
407 404
408 );
409});
410
411// Global error handler
412app.onError((err, c) => {
80bed05Claude413 reportError(err, {
414 requestId: c.get("requestId"),
415 path: c.req.path,
416 method: c.req.method,
417 });
e1db10eClaude418 const requestId = c.get("requestId" as never) as string | undefined;
36cc17aClaude419 const user = c.get("user") ?? null;
79136bbClaude420 return c.html(
36cc17aClaude421 <Layout title="Error" user={user}>
e1db10eClaude422 <div class="error-page">
423 <div class="error-page-code error-page-code-err">500</div>
424 <div class="eyebrow" style="color:var(--red)">Server error</div>
425 <h1 class="display error-page-title">
426 Something <span class="gradient-text">went wrong.</span>
427 </h1>
428 <p class="error-page-sub">
429 The error has been reported. Try again — if it persists, file
430 an issue with the request ID below.
431 </p>
432 <div class="error-page-actions">
433 <a href={c.req.path} class="btn btn-primary btn-lg">Retry</a>
434 <a href="/" class="btn btn-ghost btn-lg">Go home</a>
435 </div>
436 {requestId && (
437 <div class="error-page-meta">
438 <span class="meta-mono">request-id: {requestId}</span>
439 </div>
440 )}
79136bbClaude441 {process.env.NODE_ENV !== "production" && (
e1db10eClaude442 <pre class="error-page-trace">{err.message}</pre>
79136bbClaude443 )}
444 </div>
445 </Layout>,
446 500
447 );
448});
449
450export default app;