feat: bare-domain module routing — rspace.online/{moduleId} as default
App dropdown links now go to rspace.online/r* (bare domain) instead of
demo.rspace.online/r*. Only the "Try Demo" button links to the explicit
demo subdomain. Server internally rewrites bare-domain module paths to
/demo/{moduleId} while preserving the browser URL.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
92bec8243d
commit
1bedc4e504
|
|
@ -779,6 +779,24 @@ const server = Bun.serve<WSData>({
|
||||||
return app.fetch(rewrittenReq);
|
return app.fetch(rewrittenReq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Bare-domain module routes: rspace.online/{moduleId} → internal rewrite ──
|
||||||
|
// When on the bare domain (no subdomain), if the first path segment is a
|
||||||
|
// known module ID, rewrite internally to /demo/{moduleId}/... so Hono's
|
||||||
|
// /:space/:moduleId routes handle it. The browser URL stays as-is.
|
||||||
|
if (!subdomain && hostClean.includes("rspace.online")) {
|
||||||
|
const pathSegments = url.pathname.split("/").filter(Boolean);
|
||||||
|
if (pathSegments.length >= 1) {
|
||||||
|
const firstSegment = pathSegments[0];
|
||||||
|
const knownModuleIds = new Set(getAllModules().map((m) => m.id));
|
||||||
|
if (knownModuleIds.has(firstSegment)) {
|
||||||
|
const rewrittenPath = `/demo${url.pathname}`;
|
||||||
|
const rewrittenUrl = new URL(rewrittenPath + url.search, `http://localhost:${PORT}`);
|
||||||
|
const rewrittenReq = new Request(rewrittenUrl, req);
|
||||||
|
return app.fetch(rewrittenReq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ── Hono handles everything else ──
|
// ── Hono handles everything else ──
|
||||||
const response = await app.fetch(req);
|
const response = await app.fetch(req);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ export function renderShell(opts: ShellOptions): string {
|
||||||
<rstack-mi></rstack-mi>
|
<rstack-mi></rstack-mi>
|
||||||
</div>
|
</div>
|
||||||
<div class="rstack-header__right">
|
<div class="rstack-header__right">
|
||||||
${spaceSlug !== "demo" ? `<a class="rstack-header__demo-btn" href="https://demo.rspace.online/${escapeAttr(moduleId)}">Try Demo</a>` : ""}
|
<a class="rstack-header__demo-btn" ${spaceSlug === "demo" ? 'data-hide' : ''} href="https://demo.rspace.online/${escapeAttr(moduleId)}">Try Demo</a>
|
||||||
<rstack-identity></rstack-identity>
|
<rstack-identity></rstack-identity>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
@ -96,6 +96,17 @@ export function renderShell(opts: ShellOptions): string {
|
||||||
// Provide module list to app switcher
|
// Provide module list to app switcher
|
||||||
document.querySelector('rstack-app-switcher')?.setModules(${moduleListJSON});
|
document.querySelector('rstack-app-switcher')?.setModules(${moduleListJSON});
|
||||||
|
|
||||||
|
// ── Bare-domain "Try Demo" button visibility ──
|
||||||
|
// On rspace.online (bare domain), the server internally rewrites to demo space,
|
||||||
|
// but we still want the "Try Demo" button visible since it links to the explicit demo subdomain.
|
||||||
|
(function() {
|
||||||
|
var host = window.location.host.split(':')[0];
|
||||||
|
if (host === 'rspace.online' || host === 'www.rspace.online') {
|
||||||
|
var btn = document.querySelector('.rstack-header__demo-btn');
|
||||||
|
if (btn) btn.removeAttribute('data-hide');
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
// ── Auto-space resolution ──
|
// ── Auto-space resolution ──
|
||||||
// Logged-in users on demo space → redirect to personal space
|
// Logged-in users on demo space → redirect to personal space
|
||||||
(function() {
|
(function() {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
* Subdomain-aware URL helpers for rSpace navigation.
|
* Subdomain-aware URL helpers for rSpace navigation.
|
||||||
*
|
*
|
||||||
* Canonical URL pattern: {space}.rspace.online/{moduleId}
|
* Canonical URL pattern: {space}.rspace.online/{moduleId}
|
||||||
|
* Bare domain pattern: rspace.online/{moduleId} (implicitly demo space)
|
||||||
* Fallback (localhost): /{space}/{moduleId}
|
* Fallback (localhost): /{space}/{moduleId}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
@ -17,26 +18,37 @@ export function isSubdomain(): boolean {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Detect if the current page is on the bare rspace.online domain (no subdomain) */
|
||||||
|
export function isBareDomain(): boolean {
|
||||||
|
const host = window.location.host.split(":")[0];
|
||||||
|
return host === "rspace.online" || host === "www.rspace.online";
|
||||||
|
}
|
||||||
|
|
||||||
/** Get the current space from subdomain or path */
|
/** Get the current space from subdomain or path */
|
||||||
export function getCurrentSpace(): string {
|
export function getCurrentSpace(): string {
|
||||||
const hostParts = window.location.host.split(":")[0].split(".");
|
if (isSubdomain()) {
|
||||||
if (
|
return window.location.host.split(":")[0].split(".")[0];
|
||||||
hostParts.length >= 3 &&
|
|
||||||
hostParts.slice(-2).join(".") === "rspace.online" &&
|
|
||||||
!RESERVED_SUBDOMAINS.includes(hostParts[0])
|
|
||||||
) {
|
|
||||||
return hostParts[0];
|
|
||||||
}
|
}
|
||||||
|
// Bare domain: space is implicit (demo by default, until auto-provision)
|
||||||
|
if (isBareDomain()) {
|
||||||
|
return "demo";
|
||||||
|
}
|
||||||
|
// Path-based (localhost): /{space}/{moduleId}
|
||||||
const pathParts = window.location.pathname.split("/").filter(Boolean);
|
const pathParts = window.location.pathname.split("/").filter(Boolean);
|
||||||
return pathParts[0] || "demo";
|
return pathParts[0] || "demo";
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get the current module from the path (works for both subdomain and path routing) */
|
/** Get the current module from the path (works for subdomain, bare domain, and path routing) */
|
||||||
export function getCurrentModule(): string {
|
export function getCurrentModule(): string {
|
||||||
const parts = window.location.pathname.split("/").filter(Boolean);
|
const parts = window.location.pathname.split("/").filter(Boolean);
|
||||||
if (isSubdomain()) {
|
if (isSubdomain()) {
|
||||||
return parts[0] || "rspace";
|
return parts[0] || "rspace";
|
||||||
}
|
}
|
||||||
|
// Bare domain: path is /{moduleId}
|
||||||
|
if (isBareDomain()) {
|
||||||
|
return parts[0] || "rspace";
|
||||||
|
}
|
||||||
|
// Path-based (localhost): /{space}/{moduleId}
|
||||||
return parts[1] || "rspace";
|
return parts[1] || "rspace";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,7 +57,9 @@ export function getCurrentModule(): string {
|
||||||
*
|
*
|
||||||
* On subdomains: same-space links use /{moduleId}, cross-space links
|
* On subdomains: same-space links use /{moduleId}, cross-space links
|
||||||
* switch the subdomain to {newSpace}.rspace.online/{moduleId}.
|
* switch the subdomain to {newSpace}.rspace.online/{moduleId}.
|
||||||
* On bare domain or localhost: uses /{space}/{moduleId}.
|
* On bare domain (rspace.online): stays on bare domain as /{moduleId}
|
||||||
|
* for default (demo) space, subdomain for explicit spaces.
|
||||||
|
* On localhost: uses /{space}/{moduleId}.
|
||||||
*/
|
*/
|
||||||
export function rspaceNavUrl(space: string, moduleId: string): string {
|
export function rspaceNavUrl(space: string, moduleId: string): string {
|
||||||
const hostParts = window.location.host.split(":")[0].split(".");
|
const hostParts = window.location.host.split(":")[0].split(".");
|
||||||
|
|
@ -64,12 +78,13 @@ export function rspaceNavUrl(space: string, moduleId: string): string {
|
||||||
return `${window.location.protocol}//${space}.${baseDomain}/${moduleId}`;
|
return `${window.location.protocol}//${space}.${baseDomain}/${moduleId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bare domain (rspace.online) or localhost → use /{space}/{moduleId}
|
// Bare domain (rspace.online)
|
||||||
const onRspace =
|
if (isBareDomain()) {
|
||||||
window.location.host.includes("rspace.online") &&
|
// Default space → stay on bare domain: /{moduleId}
|
||||||
!window.location.host.startsWith("www");
|
if (space === "demo") {
|
||||||
if (onRspace) {
|
return `/${moduleId}`;
|
||||||
// Prefer subdomain routing
|
}
|
||||||
|
// Explicit space → switch to subdomain
|
||||||
return `${window.location.protocol}//${space}.rspace.online/${moduleId}`;
|
return `${window.location.protocol}//${space}.rspace.online/${moduleId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue