| 35 | const INSTANCE_CACHE_TTL = 30 * 1000 |
| 36 | |
| 37 | async function getInstanceInfo(): Promise<InstanceInfo> { |
| 38 | if (_instanceCache && Date.now() - _instanceCache.ts < INSTANCE_CACHE_TTL) { |
| 39 | return _instanceCache.data |
| 40 | } |
| 41 | |
| 42 | try { |
| 43 | const apiUrl = getAPIUrl() |
| 44 | const res = await fetch(`${apiUrl}instance/info`, { signal: AbortSignal.timeout(3000) }) |
| 45 | if (res.ok) { |
| 46 | const raw = await res.json() |
| 47 | // Older backends only return `multi_org_enabled`; derive `tenancy`. |
| 48 | const tenancy: 'multi' | 'single' = |
| 49 | raw.tenancy === 'multi' || raw.multi_org_enabled ? 'multi' : 'single' |
| 50 | _instanceCache = { data: { ...raw, tenancy }, ts: Date.now() } |
| 51 | return _instanceCache.data |
| 52 | } |
| 53 | } catch { |
| 54 | // Backend unavailable — use safe defaults |
| 55 | } |
| 56 | return { |
| 57 | multi_org_enabled: false, |
| 58 | default_org_slug: 'default', |
| 59 | mode: 'oss' as const, |
| 60 | tenancy: 'single', |
| 61 | frontend_domain: 'localhost:3000', |
| 62 | top_domain: 'localhost', |
| 63 | } |
| 64 | } |
| 65 | |
| 66 | // ============================================================================= |
| 67 | // Resolver |