forked from EduCraft/curriculum-project-hub
4e7b158ff9
Silo hostname already carries tenancy. Admin SPA routes become /admin/..., legacy /admin/org/:slug/* bookmarks redirect, login lands on /admin.
41 lines
1.5 KiB
TypeScript
41 lines
1.5 KiB
TypeScript
import type { MeResponse, OrgMembership } from './api';
|
|
|
|
/** Alpha Silo host prefix: <slug>.educraft[.dev].… */
|
|
export function hostOrgSlug(hostname: string = typeof window !== 'undefined' ? window.location.hostname : ''): string | null {
|
|
const host = hostname.toLowerCase();
|
|
const m = host.match(/^([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)\.educraft(?:-dev)?\./);
|
|
return m?.[1] ?? null;
|
|
}
|
|
|
|
export function isOrgAdmin(org: OrgMembership | null | undefined): boolean {
|
|
if (!org) return false;
|
|
const role = String(org.role ?? '').toUpperCase();
|
|
return role === 'OWNER' || role === 'ADMIN';
|
|
}
|
|
|
|
/**
|
|
* Resolve the tenancy for this browser session.
|
|
* Prefers hostname slug (silo), then ?org=, then first admin membership, then first membership.
|
|
*/
|
|
export function resolveOrg(me: MeResponse | null | undefined, search: string = ''): OrgMembership | null {
|
|
if (!me || me.organizations.length === 0) return null;
|
|
const host = hostOrgSlug();
|
|
if (host) {
|
|
const byHost = me.organizations.find((o) => o.slug === host);
|
|
if (byHost) return byHost;
|
|
}
|
|
const q = new URLSearchParams(search).get('org')?.trim();
|
|
if (q) {
|
|
const byQuery = me.organizations.find((o) => o.slug === q);
|
|
if (byQuery) return byQuery;
|
|
}
|
|
const admin = me.organizations.find((o) => isOrgAdmin(o));
|
|
return admin ?? me.organizations[0] ?? null;
|
|
}
|
|
|
|
/** SPA paths no longer embed org slug (subdomain carries tenancy). */
|
|
export function adminPath(rest: string = ''): string {
|
|
const cleaned = rest.replace(/^\/+/, '');
|
|
return cleaned === '' ? '/admin' : `/admin/${cleaned}`;
|
|
}
|