Commitad189d6
fix(routing): stop /admin/autopilot/health being shadowed by the repo-health page
fix(routing): stop /admin/autopilot/health being shadowed by the repo-health page
adminRoutes was mounted at the very end of app.tsx, well after
healthDashboardRoutes' generic GET /:owner/:repo/health. Hono matches
in registration order, so a request for /admin/autopilot/health was
being parsed as :owner=admin :repo=autopilot :page=health by the repo
health-score page, which correctly reported "repo not found" -- a
real 404, just for the wrong reason. The actual admin page was
unreachable, not merely unauthenticated.
Same bug class the adjacent adminOpsRoutes comment already documents
("MUST be mounted BEFORE insightRoutes because its POST
/:owner/:repo/rollback catch-all would otherwise intercept
/admin/ops/rollback") -- that fix just never got applied to the main
adminRoutes router itself. Moved its mount to before
healthDashboardRoutes; verified admin.tsx has no route pattern broad
enough to shadow anything mounted in between (only two narrow
:id-scoped routes, no wildcards beyond the sub-router's own softAuth).
Found via a full production HTTP sweep of all 201 static page routes
-- 2 real 500s (this repo's other commit fixes /standups) and this
one silent-404 misroute. No test exercised this route through
app.request() before.2 files changed+23−2ad189d6beed93c44816546e59f8b2aa5c3ba2931
2 changed files+23−2
Modifiedsrc/__tests__/admin.test.ts+12−0View fileUnifiedSplit
@@ -39,6 +39,18 @@ describe("admin — auth gate", () => {
3939 expect(res.headers.get("location") || "").toContain("/login");
4040 });
4141
42 // Regression: adminRoutes used to be mounted AFTER healthDashboardRoutes,
43 // whose generic GET /:owner/:repo/health matched :owner=admin
44 // :repo=autopilot and returned a real "repo not found" 404 -- the actual
45 // admin page was unreachable, not just unauthenticated. Found via a live
46 // production HTTP sweep. It must reach admin.tsx's own gate() (302 to
47 // login), not a repo-not-found 404.
48 it("GET /admin/autopilot/health reaches the real admin route, not the repo-health 404", async () => {
49 const res = await app.request("/admin/autopilot/health");
50 expect(res.status).toBe(302);
51 expect(res.headers.get("location") || "").toContain("/login");
52 });
53
4254 it("POST /admin/flags without auth → 302 /login", async () => {
4355 const res = await app.request("/admin/flags", {
4456 method: "POST",
Modifiedsrc/app.tsx+11−2View fileUnifiedSplit
@@ -703,6 +703,15 @@ app.route("/", seoRoutes);
703703// surface 'New version available — reload' banners on deploy.
704704app.route("/", versionRoutes);
705705
706// Site-admin console (admin.tsx). MUST be mounted BEFORE healthDashboardRoutes
707// — its generic GET `/:owner/:repo/health` (matching :owner=admin :repo=autopilot)
708// was shadowing `/admin/autopilot/health`, returning a real "repo not found"
709// 404 for a route that exists and works fine once actually reached. Same bug
710// class as the adminOpsRoutes/insightRoutes note below; found via a live
711// production HTTP sweep (curled all 201 static page routes) — the health
712// dashboard existing didn't stop this specific admin page being unreachable.
713app.route("/", adminRoutes);
714
706715// Health dashboard (per-repo health page)
707716app.route("/", healthDashboardRoutes);
708717
@@ -762,8 +771,8 @@ app.route("/", exploreRoutes);
762771// Onboarding
763772app.route("/", onboardingRoutes);
764773
765// Admin + feature routes
766app.route("/", adminRoutes);
774// Admin + feature routes (adminRoutes itself is mounted earlier — see the
775// comment above healthDashboardRoutes)
767776app.route("/", adminDeletionsRoutes);
768777app.route("/", adminStripeRoutes);
769778
770779