rspace-online/shared/url-helpers.ts

121 lines
3.9 KiB
TypeScript

/**
* 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}
*/
const RESERVED_SUBDOMAINS = ["www", "rspace", "create", "new", "start", "auth"];
/** Detect if the current page is on a {space}.rspace.online subdomain */
export function isSubdomain(): boolean {
const parts = window.location.host.split(":")[0].split(".");
return (
parts.length >= 3 &&
parts.slice(-2).join(".") === "rspace.online" &&
!RESERVED_SUBDOMAINS.includes(parts[0])
);
}
/** 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";
}
/** Detect if the current page is on a standalone r*.online domain (e.g. rvote.online) */
export function isStandaloneDomain(): boolean {
const host = window.location.host.split(":")[0];
return /^r[a-z]+\.online$/.test(host) && host !== "rspace.online";
}
/** Get the current space from subdomain or path */
export function getCurrentSpace(): string {
if (isSubdomain()) {
return window.location.host.split(":")[0].split(".")[0];
}
// Bare domain or standalone domain: space is implicit (demo)
if (isBareDomain() || isStandaloneDomain()) {
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 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";
}
/**
* Generate a navigation URL for a given space + module.
*
* On subdomains: same-space links use /{moduleId}, cross-space links
* switch the subdomain to {newSpace}.rspace.online/{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 host = window.location.host.split(":")[0];
const hostParts = host.split(".");
const proto = window.location.protocol;
// Always use canonical base domain to prevent stacking (e.g. rspace.rspace.online)
const BASE = "rspace.online";
const onSubdomain =
hostParts.length >= 3 &&
hostParts.slice(-2).join(".") === BASE &&
!RESERVED_SUBDOMAINS.includes(hostParts[0]);
// Standalone r*.online domains → redirect to rspace.online for navigation
if (isStandaloneDomain()) {
if (space === "demo") {
return `${proto}//demo.${BASE}/${moduleId}`;
}
return `${proto}//${space}.${BASE}/${moduleId}`;
}
if (onSubdomain) {
// Same space → just change the path
if (hostParts[0] === space) {
return `/${moduleId}`;
}
// Guard: reserved words can't be subdomains — treat as demo
if (RESERVED_SUBDOMAINS.includes(space)) {
return `${proto}//${BASE}/${moduleId}`;
}
// Different space → switch subdomain (always use canonical base)
return `${proto}//${space}.${BASE}/${moduleId}`;
}
// Bare domain (rspace.online) or any non-standard rspace.online host
if (isBareDomain() || host.endsWith(`.${BASE}`)) {
// Default space → stay on bare domain: /{moduleId}
if (space === "demo") {
return `/${moduleId}`;
}
// Guard: reserved words can't be subdomains
if (RESERVED_SUBDOMAINS.includes(space)) {
return `/${moduleId}`;
}
// Explicit space → switch to subdomain
return `${proto}//${space}.${BASE}/${moduleId}`;
}
// Localhost/dev
return `/${space}/${moduleId}`;
}