(orgslug: string, path: string)
| 270 | * the menu to forge a non-existent subdomain like `default.localhost:3000`. |
| 271 | */ |
| 272 | export const getUriWithOrg = (orgslug: string, path: string) => { |
| 273 | const tenancy = getTenancy() |
| 274 | |
| 275 | // Client-side |
| 276 | if (typeof window !== 'undefined') { |
| 277 | // Single tenancy → always relative. The browser keeps us on the same host. |
| 278 | // Custom domain → relative (we're already on the org's host). |
| 279 | // Missing slug → relative (caller wants a generic intra-app URL). |
| 280 | if (tenancy === 'single' || getCustomDomainFromContext() || !orgslug) { |
| 281 | return path |
| 282 | } |
| 283 | |
| 284 | // Multi tenancy: relative if we're already on the correct subdomain. |
| 285 | const baseDomain = stripPort(getLEARNHOUSE_DOMAIN()) |
| 286 | const currentHostname = window.location.hostname |
| 287 | const expectedHostname = `${orgslug}.${baseDomain}` |
| 288 | if (currentHostname === expectedHostname) { |
| 289 | return path |
| 290 | } |
| 291 | |
| 292 | // Safety net: only synthesize an absolute subdomain URL when the user is |
| 293 | // on the apex base domain itself (e.g. the org-selection screen) or on |
| 294 | // some subdomain of it. On any other host — localhost, a host that |
| 295 | // doesn't end in `.{baseDomain}` — building `${slug}.${baseDomain}` would |
| 296 | // land them on a hostname that may not resolve (e.g. `default.localhost`), |
| 297 | // so we return a relative path and keep navigation on the current origin. |
| 298 | // |
| 299 | // The apex case is essential: the org-selection screen lives on the apex |
| 300 | // (`{baseDomain}`), and from there every org link must cross to its |
| 301 | // `${slug}.${baseDomain}` subdomain. `isSubdomainOf` is false for the apex |
| 302 | // (a host is not a subdomain of itself), so without the `isSameHost` check |
| 303 | // org links would collapse to the apex path and loop back to the selector. |
| 304 | if (!isSubdomainOf(currentHostname, baseDomain) && !isSameHost(currentHostname, baseDomain)) { |
| 305 | return path |
| 306 | } |
| 307 | |
| 308 | // Crossing subdomains — build an absolute URL with current scheme/port. |
| 309 | const protocol = window.location.protocol + '//' |
| 310 | const port = window.location.port |
| 311 | const portSuffix = port && port !== '80' && port !== '443' ? `:${port}` : '' |
| 312 | return `${protocol}${orgslug}.${baseDomain}${portSuffix}${path}` |
| 313 | } |
| 314 | |
| 315 | // Server-side |
| 316 | // Single tenancy → relative. The page will render on whatever host the |
| 317 | // request came in on; relative URLs resolve correctly at the client. |
| 318 | if (tenancy === 'single') { |
| 319 | return path |
| 320 | } |
| 321 | |
| 322 | // Multi tenancy server-side: build the subdomain URL because we can't |
| 323 | // assume server components know the user's current host. |
| 324 | if (orgslug) { |
| 325 | const protocol = getLEARNHOUSE_HTTP_PROTOCOL() |
| 326 | const domain = getLEARNHOUSE_DOMAIN() |
| 327 | return `${protocol}${orgslug}.${domain}${path}` |
| 328 | } |
| 329 | const explicitDomain = getConfig('NEXT_PUBLIC_LEARNHOUSE_DOMAIN') |
no test coverage detected