fix: resilient dashboard loading when ledger API unavailable

Tolerate partial API failures and infer Fineract connected state from health.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-29 01:31:07 -07:00
parent 156591d707
commit abed3d9f8a
2 changed files with 55 additions and 27 deletions

View File

@@ -12,17 +12,25 @@ export type MoneySupply = {
M2?: { broadMoney?: string; gl2200?: string; metaFiatPending?: string };
};
async function fetchJson<T>(url: string): Promise<T | null> {
try {
const res = await fetch(url);
return res.ok ? ((await res.json()) as T) : null;
} catch {
return null;
}
}
async function loadMoneySupply(): Promise<MoneySupply | null> {
for (const path of ['/public/money-supply', '/money-supply/public', '/health/ledger']) {
const res = await fetch(settlementApi(path));
if (res.ok) return res.json();
const data = await fetchJson<MoneySupply>(settlementApi(path));
if (data) return data;
}
const healthRes = await fetch(`${settlementApi('/health')}?ledger=1`);
if (healthRes.ok) {
const data = (await healthRes.json()) as { moneySupply?: MoneySupply };
if (data.moneySupply) return data.moneySupply;
}
const health = await fetchJson<{ moneySupply?: MoneySupply }>(
`${settlementApi('/health')}?ledger=1`,
);
if (health?.moneySupply) return health.moneySupply;
const apiKey = import.meta.env.VITE_OMNL_API_KEY;
if (!apiKey) return null;
@@ -47,17 +55,25 @@ export function useCentralBank() {
setError(null);
try {
const [h, o, m2, mon, ms] = await Promise.all([
fetch(settlementApi('/health')).then((r) => r.json()),
fetch(settlementApi('/office')).then((r) => r.json()),
fetch(settlementApi('/tokens/m2')).then((r) => r.json()),
fetch(exchangeApi('/swap/monitor?limit=10')).then((r) => r.json()),
fetchJson<Record<string, unknown>>(settlementApi('/health')),
fetchJson<Record<string, unknown>>(settlementApi('/office')),
fetchJson<{ tokens?: unknown[] }>(settlementApi('/tokens/m2')),
fetchJson<Record<string, unknown>>(exchangeApi('/swap/monitor?limit=10')),
loadMoneySupply(),
]);
setHealth(h);
setOffice(o);
setTokenCount(Array.isArray(m2.tokens) ? m2.tokens.length : 0);
setTokenCount(Array.isArray(m2?.tokens) ? m2.tokens.length : 0);
setMonitor(mon);
setMoneySupply(ms);
setMoneySupply(
ms ??
(h?.status === 'ok'
? { fineractConnected: true, fineractLive: false, officeId: 24 }
: null),
);
if (!h && !o) {
setError('Settlement middleware unreachable');
}
} catch (e) {
setError(e instanceof Error ? e.message : 'Failed to load central bank data');
} finally {

View File

@@ -10,17 +10,24 @@ export type M2Token = DexToken & {
transferableExternal?: boolean;
};
async function fetchJson<T>(url: string): Promise<T | null> {
try {
const res = await fetch(url);
return res.ok ? ((await res.json()) as T) : null;
} catch {
return null;
}
}
async function loadOfficeMoneySupply(): Promise<MoneySupply | null> {
for (const path of ['/public/money-supply', '/money-supply/public', '/health/ledger']) {
const res = await fetch(settlementApi(path));
if (res.ok) return res.json();
const data = await fetchJson<MoneySupply>(settlementApi(path));
if (data) return data;
}
const healthRes = await fetch(`${settlementApi('/health')}?ledger=1`);
if (healthRes.ok) {
const data = (await healthRes.json()) as { moneySupply?: MoneySupply };
if (data.moneySupply) return data.moneySupply;
}
return null;
const health = await fetchJson<{ moneySupply?: MoneySupply }>(
`${settlementApi('/health')}?ledger=1`,
);
return health?.moneySupply ?? null;
}
export function useOffice24() {
@@ -34,15 +41,20 @@ export function useOffice24() {
setLoading(true);
try {
const [o, t, h, ms] = await Promise.all([
fetch(settlementApi('/office')).then((r) => r.json()),
fetch(settlementApi('/tokens/m2')).then((r) => r.json()),
fetch(exchangeApi('/health')).then((r) => r.json()),
fetch(loadOfficeMoneySupply()),
fetchJson<Record<string, unknown>>(settlementApi('/office')),
fetchJson<{ tokens?: M2Token[] }>(settlementApi('/tokens/m2')),
fetchJson<Record<string, unknown>>(exchangeApi('/health')),
loadOfficeMoneySupply(),
]);
setOffice(o);
setTokens(Array.isArray(t.tokens) ? t.tokens : []);
setTokens(Array.isArray(t?.tokens) ? t.tokens : []);
setHealth(h);
setMoneySupply(ms);
setMoneySupply(
ms ??
(o
? { fineractConnected: true, fineractLive: false, officeId: 24 }
: null),
);
} finally {
setLoading(false);
}