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
5ae8aeec02
commit
a732478f85
|
|
@ -779,6 +779,24 @@ const server = Bun.serve<WSData>({
|
|||
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 ──
|
||||
const response = await app.fetch(req);
|
||||
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ export function renderShell(opts: ShellOptions): string {
|
|||
<rstack-mi></rstack-mi>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
</header>
|
||||
|
|
@ -96,6 +96,17 @@ export function renderShell(opts: ShellOptions): string {
|
|||
// Provide module list to app switcher
|
||||
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 ──
|
||||
// Logged-in users on demo space → redirect to personal space
|
||||
(function() {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
* Subdomain-aware URL helpers for rSpace navigation.
|
||||
*
|
||||
* Canonical URL pattern: {space}.rspace.online/{moduleId}
|
||||
* Bare domain pattern: rspace.online/{moduleId} (implicitly demo space)
|
||||
* 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 */
|
||||
export function getCurrentSpace(): string {
|
||||
const hostParts = window.location.host.split(":")[0].split(".");
|
||||
if (
|
||||
hostParts.length >= 3 &&
|
||||
hostParts.slice(-2).join(".") === "rspace.online" &&
|
||||
!RESERVED_SUBDOMAINS.includes(hostParts[0])
|
||||
) {
|
||||
return hostParts[0];
|
||||
if (isSubdomain()) {
|
||||
return window.location.host.split(":")[0].split(".")[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);
|
||||
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 {
|
||||
const parts = window.location.pathname.split("/").filter(Boolean);
|
||||
if (isSubdomain()) {
|
||||
return parts[0] || "rspace";
|
||||
}
|
||||
// Bare domain: path is /{moduleId}
|
||||
if (isBareDomain()) {
|
||||
return parts[0] || "rspace";
|
||||
}
|
||||
// Path-based (localhost): /{space}/{moduleId}
|
||||
return parts[1] || "rspace";
|
||||
}
|
||||
|
||||
|
|
@ -45,7 +57,9 @@ export function getCurrentModule(): string {
|
|||
*
|
||||
* On subdomains: same-space links use /{moduleId}, cross-space links
|
||||
* 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 {
|
||||
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}`;
|
||||
}
|
||||
|
||||
// Bare domain (rspace.online) or localhost → use /{space}/{moduleId}
|
||||
const onRspace =
|
||||
window.location.host.includes("rspace.online") &&
|
||||
!window.location.host.startsWith("www");
|
||||
if (onRspace) {
|
||||
// Prefer subdomain routing
|
||||
// Bare domain (rspace.online)
|
||||
if (isBareDomain()) {
|
||||
// Default space → stay on bare domain: /{moduleId}
|
||||
if (space === "demo") {
|
||||
return `/${moduleId}`;
|
||||
}
|
||||
// Explicit space → switch to subdomain
|
||||
return `${window.location.protocol}//${space}.rspace.online/${moduleId}`;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue