Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ side quests, discovery, and personal history. It owns the existing
`significanthobbies` Cloudflare Worker and D1 data authority.

The Worker retains apex compatibility routes for the existing web application.
The Hub path allowlist is delegated through `HUB_SERVICE` on apex hosts.
The Hub path allowlist, including the `/landing/` asset prefix (not exact
`/landing`), is delegated through `HUB_SERVICE` on apex hosts.
Exactly `/hub` also delegates on the Live host to preserve its host-only session.
The canonical Significant Hobbies Hub retains rendering and data ownership. Journal and Hub Backend source belong in
their independent repositories and must not be reintroduced here.
Expand Down
2 changes: 1 addition & 1 deletion hub-routing.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const HUB_SERVICE_EXACT_PATHS = new Set([
'/mcp',
...HUB_AGENT_CONTRACT_PATHS,
]);
const HUB_SERVICE_PREFIX_PATHS = ['/.well-known/agent-skills/'];
const HUB_SERVICE_PREFIX_PATHS = ['/.well-known/agent-skills/', '/landing/'];
const PERSONAL_PLATFORM_INTERNAL_HOST = 'personal-auth.internal';
export const PERSONAL_PLATFORM_INTERNAL_HEADER = 'X-Personal-Platform-Internal';

Expand Down
55 changes: 55 additions & 0 deletions src/server/hub-routing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,61 @@ describe('Hub edge routing', () => {
expect(isHubServicePath(pathname)).toBe(false);
});

describe('Hub landing assets', () => {
it.each([
'/landing/',
'/landing/x',
'/landing/fonts/home.woff2',
'/landing/home.css',
'/landing/images/home.webp',
])('delegates the precise prefix %s only on apex hosts', (pathname) => {
expect(isHubServicePath(pathname)).toBe(true);
for (const hostname of ['significanthobbies.com', 'www.significanthobbies.com']) {
const url = new URL(`https://${hostname}${pathname}`);
expect(shouldDelegateHub(url)).toBe(true);
expect(legacyLiveRedirect(url)).toBeNull();
}
expect(shouldDelegateHub(new URL(`https://live.significanthobbies.com${pathname}`))).toBe(
false
);
expect(shouldDelegateHub(new URL(`https://other.example${pathname}`))).toBe(false);
});

it.each(['/landing', '/landing-extra/home.css', '/landings/home.css'])(
'redirects the non-prefix path %s to Live',
(pathname) => {
expect(isHubServicePath(pathname)).toBe(false);
for (const hostname of ['significanthobbies.com', 'www.significanthobbies.com']) {
const url = new URL(`https://${hostname}${pathname}`);
expect(shouldDelegateHub(url)).toBe(false);
expect(legacyLiveRedirect(url)?.href).toBe(
`https://live.significanthobbies.com${pathname}`
);
}
}
);

it('forwards apex assets and preserves their Hub cache headers', async () => {
const request = new Request('https://significanthobbies.com/landing/home.css');
const response = await fetchHubRoute(request, {
HUB_SERVICE: {
fetch: async (forwarded: Request) => {
expect(forwarded).toBe(request);
return new Response('body {}', {
headers: { 'Cache-Control': 'public, max-age=3600' },
});
},
},
});
expect(await response?.text()).toBe('body {}');
expect(response?.headers.get('cache-control')).toBe('public, max-age=3600');
expect(response?.headers.has('cdn-cache-control')).toBe(false);
expect(
await fetchHubRoute(new Request('https://live.significanthobbies.com/landing/home.css'), {})
).toBeNull();
});
});

it('keeps every Live path on the Live host', () => {
expect(legacyLiveRedirect(new URL('https://live.significanthobbies.com/live-more'))).toBeNull();
});
Expand Down
Loading