feat(hub): drop redundant /admin/org/:slug path + release v0.0.35

Silo hostname already carries tenancy. Admin SPA routes become /admin/...,
legacy /admin/org/:slug/* bookmarks redirect, login lands on /admin.
This commit is contained in:
2026-07-18 17:36:08 +00:00
parent eb0be43eac
commit 4e7b158ff9
25 changed files with 221 additions and 129 deletions
+9 -6
View File
@@ -68,14 +68,14 @@ describe("session cookie signing", () => {
describe("oauth state signing", () => {
it("round-trips state with returnTo", () => {
const token = signOAuthState(
{ nonce: "abc", returnTo: "/admin/org/acme" },
{ nonce: "abc", returnTo: "/admin" },
SECRET,
600,
1_700_000_000,
);
expect(verifyOAuthState(token, SECRET, 1_700_000_100)).toEqual({
nonce: "abc",
returnTo: "/admin/org/acme",
returnTo: "/admin",
exp: 1_700_000_600,
});
});
@@ -83,13 +83,13 @@ describe("oauth state signing", () => {
it("binds state to an Organization and connection as one inseparable scope", () => {
const token = signOAuthState({
nonce: "scoped",
returnTo: "/admin/org/acme",
returnTo: "/admin",
organizationId: "org-acme",
connectionId: "connection-acme",
}, SECRET, 600, 1_700_000_000);
expect(verifyOAuthState(token, SECRET, 1_700_000_100)).toEqual({
nonce: "scoped",
returnTo: "/admin/org/acme",
returnTo: "/admin",
organizationId: "org-acme",
connectionId: "connection-acme",
exp: 1_700_000_600,
@@ -98,8 +98,11 @@ describe("oauth state signing", () => {
});
describe("sanitizeReturnTo", () => {
it("allows admin paths", () => {
expect(sanitizeReturnTo("/admin/org/acme")).toBe("/admin/org/acme");
it("allows admin paths and rewrites legacy org slug prefixes", () => {
expect(sanitizeReturnTo("/admin")).toBe("/admin");
expect(sanitizeReturnTo("/admin/usage")).toBe("/admin/usage");
expect(sanitizeReturnTo("/admin/org/acme")).toBe("/admin");
expect(sanitizeReturnTo("/admin/org/acme/usage")).toBe("/admin/usage");
});
it("blocks open redirects", () => {
+1
View File
@@ -23,6 +23,7 @@ describe("isSiloHttpRateLimitExempt", () => {
expect(isSiloHttpRateLimitExempt("/favicon.svg")).toBe(true);
expect(isSiloHttpRateLimitExempt("/robots.txt")).toBe(true);
expect(isSiloHttpRateLimitExempt("/admin")).toBe(true);
expect(isSiloHttpRateLimitExempt("/admin/members")).toBe(true);
expect(isSiloHttpRateLimitExempt("/admin/org/para-26071100/members")).toBe(true);
});