feat: wire live Fineract M1 to Central Bank UI and Office 24 journal seed
Some checks failed
CI/CD Pipeline / Solidity Contracts (push) Failing after 1m21s
CI/CD Pipeline / Security Scanning (push) Successful in 2m42s
CI/CD Pipeline / Terraform Validation (push) Has been cancelled
CI/CD Pipeline / Kubernetes Validation (push) Has been cancelled
CI/CD Pipeline / Lint and Format (push) Has been cancelled
Deploy ChainID 138 / Deploy ChainID 138 (push) Has been cancelled
Validation / validate-genesis (push) Has been cancelled
Validation / validate-terraform (push) Has been cancelled
Validation / validate-kubernetes (push) Has been cancelled
Validation / validate-smart-contracts (push) Has been cancelled
Validation / validate-security (push) Has been cancelled
Validation / validate-documentation (push) Has been cancelled
Verify Deployment / Verify Deployment (push) Has been cancelled
Some checks failed
CI/CD Pipeline / Solidity Contracts (push) Failing after 1m21s
CI/CD Pipeline / Security Scanning (push) Successful in 2m42s
CI/CD Pipeline / Terraform Validation (push) Has been cancelled
CI/CD Pipeline / Kubernetes Validation (push) Has been cancelled
CI/CD Pipeline / Lint and Format (push) Has been cancelled
Deploy ChainID 138 / Deploy ChainID 138 (push) Has been cancelled
Validation / validate-genesis (push) Has been cancelled
Validation / validate-terraform (push) Has been cancelled
Validation / validate-kubernetes (push) Has been cancelled
Validation / validate-smart-contracts (push) Has been cancelled
Validation / validate-security (push) Has been cancelled
Validation / validate-documentation (push) Has been cancelled
Verify Deployment / Verify Deployment (push) Has been cancelled
Add a read-only money-supply endpoint for Office 24 dashboards, pass Fineract env through deploy scripts, and provide a seed script for the opening Dr 1410 / Cr 2100 journal. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -38,10 +38,12 @@ CHAIN_8453_RPC_URL=https://mainnet.base.org
|
||||
CHAIN_10_RPC_URL=https://mainnet.optimism.io
|
||||
|
||||
# === Fineract / Office 24 ===
|
||||
FINERACT_BASE_URL=
|
||||
FINERACT_TENANT=omnl
|
||||
OMNL_FINERACT_USERNAME=
|
||||
OMNL_FINERACT_BASE_URL=
|
||||
OMNL_FINERACT_TENANT=omnl
|
||||
OMNL_FINERACT_USERNAME=ali_hospitallers_tenant
|
||||
OMNL_FINERACT_PASSWORD=
|
||||
OMNL_PUBLIC_MONEY_SUPPLY=1
|
||||
OFFICE24_OPENING_M1_USD=
|
||||
|
||||
# === Frontend ===
|
||||
VITE_WALLETCONNECT_PROJECT_ID=
|
||||
|
||||
14
config/offices/office-24-opening-journal.v1.json
Normal file
14
config/offices/office-24-opening-journal.v1.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"$schema": "Office 24 opening M1 allocation from Head Office (Fineract journal)",
|
||||
"version": "1.0.0",
|
||||
"officeId": 24,
|
||||
"externalId": "HOSPITALLERS-ALI-IRAQ-IRAN",
|
||||
"referenceNumber": "HOSPITALLERS-24-OPENING-M1",
|
||||
"comments": "M1 allocation from Head Office — Dr 1410 Due From HO / Cr 2100 M1 Central Liabilities",
|
||||
"debitGlCode": "1410",
|
||||
"creditGlCode": "2100",
|
||||
"currency": "USD",
|
||||
"ipsasRef": "IPSAS 28",
|
||||
"amountEnv": "OFFICE24_OPENING_M1_USD",
|
||||
"notes": "Set OFFICE24_OPENING_M1_USD in .env before running scripts/deployment/seed-office-24-opening-journal.mjs"
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useCentralBank } from './useCentralBank';
|
||||
import ChainMatrixPanel from './ChainMatrixPanel';
|
||||
import { SETTLEMENT_MIDDLEWARE_URL, DBIS_EXCHANGE_URL } from '../../config/dex';
|
||||
import { formatUsd } from '../../utils/formatMoney';
|
||||
|
||||
function StatCard({
|
||||
label,
|
||||
@@ -58,19 +59,19 @@ export default function CentralBankDashboard() {
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
|
||||
<StatCard
|
||||
label="M0 Reserve (GL 1050)"
|
||||
value={cb.moneySupply?.M0?.gl1050 ?? '—'}
|
||||
value={formatUsd(cb.moneySupply?.M0?.gl1050)}
|
||||
sub={`Backing ${cb.moneySupply?.M0?.backingRatio ?? 1.2}×`}
|
||||
accent="blue"
|
||||
/>
|
||||
<StatCard
|
||||
label="M1 Circulating (GL 2100)"
|
||||
value={cb.moneySupply?.M1?.circulating ?? '—'}
|
||||
value={formatUsd(cb.moneySupply?.M1?.circulating)}
|
||||
sub="Narrow money · eMoney"
|
||||
accent="green"
|
||||
/>
|
||||
<StatCard
|
||||
label="M2 Broad (GL 2200)"
|
||||
value={cb.moneySupply?.M2?.broadMoney ?? '—'}
|
||||
value={formatUsd(cb.moneySupply?.M2?.broadMoney)}
|
||||
sub="Token load source"
|
||||
accent="gold"
|
||||
/>
|
||||
@@ -82,10 +83,27 @@ export default function CentralBankDashboard() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{cb.moneySupply?.fineractLive === false && (
|
||||
<p className="text-xs text-[#f6465d] mb-6">
|
||||
Fineract GL balances unavailable — check OMNL_FINERACT_* on settlement middleware and seed
|
||||
Office 24 opening journal (Dr 1410 / Cr 2100).
|
||||
</p>
|
||||
)}
|
||||
|
||||
{cb.moneySupply?.asOf && (
|
||||
<p className="text-xs text-[#848e9c] mb-6">
|
||||
Live Fineract · Office {cb.moneySupply.officeId ?? 24} · as of{' '}
|
||||
{cb.moneySupply.asOf.slice(0, 19).replace('T', ' ')} UTC
|
||||
{cb.moneySupply.interoffice?.dueFromHeadOffice
|
||||
? ` · Due from HO (GL 1410): ${formatUsd(cb.moneySupply.interoffice.dueFromHeadOffice)}`
|
||||
: ''}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{!cb.moneySupply && (
|
||||
<p className="text-xs text-[#848e9c] mb-6">
|
||||
Set <code className="text-[#f0b90b]">VITE_OMNL_API_KEY</code> in .env.local to load live M0/M1/M2
|
||||
balances from Fineract.
|
||||
Money supply loads from{' '}
|
||||
<code className="text-[#f0b90b]">/money-supply/public</code> when online bank mode is enabled.
|
||||
</p>
|
||||
)}
|
||||
|
||||
|
||||
@@ -4,11 +4,26 @@ import { DBIS_EXCHANGE_URL, SETTLEMENT_MIDDLEWARE_URL } from '../../config/dex';
|
||||
export type MoneySupply = {
|
||||
asOf?: string;
|
||||
officeId?: number;
|
||||
fineractLive?: boolean;
|
||||
interoffice?: { dueFromHeadOffice?: string; gl1410?: string };
|
||||
M0?: { gl1050?: string; backingRatio?: number };
|
||||
M1?: { circulating?: string; gl2100?: string };
|
||||
M2?: { broadMoney?: string; gl2200?: string; metaFiatPending?: string };
|
||||
};
|
||||
|
||||
async function loadMoneySupply(settlement: string): Promise<MoneySupply | null> {
|
||||
const publicRes = await fetch(`${settlement}/api/v1/settlement/money-supply/public`);
|
||||
if (publicRes.ok) return publicRes.json();
|
||||
|
||||
const apiKey = import.meta.env.VITE_OMNL_API_KEY;
|
||||
if (!apiKey) return null;
|
||||
|
||||
const authRes = await fetch(`${settlement}/api/v1/settlement/money-supply`, {
|
||||
headers: { Authorization: `Bearer ${apiKey}` },
|
||||
});
|
||||
return authRes.ok ? authRes.json() : null;
|
||||
}
|
||||
|
||||
export function useCentralBank() {
|
||||
const [health, setHealth] = useState<Record<string, unknown> | null>(null);
|
||||
const [office, setOffice] = useState<Record<string, unknown> | null>(null);
|
||||
@@ -25,24 +40,18 @@ export function useCentralBank() {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const [h, o, m2, mon] = await Promise.all([
|
||||
const [h, o, m2, mon, ms] = await Promise.all([
|
||||
fetch(`${settlement}/api/v1/settlement/health`).then((r) => r.json()),
|
||||
fetch(`${settlement}/api/v1/settlement/office`).then((r) => r.json()),
|
||||
fetch(`${settlement}/api/v1/settlement/tokens/m2`).then((r) => r.json()),
|
||||
fetch(`${exchange}/api/v1/exchange/swap/monitor?limit=10`).then((r) => r.json()),
|
||||
loadMoneySupply(settlement),
|
||||
]);
|
||||
setHealth(h);
|
||||
setOffice(o);
|
||||
setTokenCount(Array.isArray(m2.tokens) ? m2.tokens.length : 0);
|
||||
setMonitor(mon);
|
||||
|
||||
const apiKey = import.meta.env.VITE_OMNL_API_KEY;
|
||||
if (apiKey) {
|
||||
const ms = await fetch(`${settlement}/api/v1/settlement/money-supply`, {
|
||||
headers: { Authorization: `Bearer ${apiKey}` },
|
||||
});
|
||||
if (ms.ok) setMoneySupply(await ms.json());
|
||||
}
|
||||
setMoneySupply(ms);
|
||||
} catch (e) {
|
||||
setError(e instanceof Error ? e.message : 'Failed to load central bank data');
|
||||
} finally {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useOffice24 } from './useOffice24';
|
||||
import { formatUsd } from '../../utils/formatMoney';
|
||||
|
||||
export default function Office24Dashboard() {
|
||||
const { office, tokens, health, loading, refresh } = useOffice24();
|
||||
const { office, tokens, health, moneySupply, loading, refresh } = useOffice24();
|
||||
const profile = office as {
|
||||
officeId?: number;
|
||||
externalId?: string;
|
||||
@@ -34,14 +35,22 @@ export default function Office24Dashboard() {
|
||||
</Link>
|
||||
</header>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-8">
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 mb-8">
|
||||
<div className="o24-card">
|
||||
<p className="text-xs text-[#848e9c] uppercase">Office ID</p>
|
||||
<p className="text-2xl font-bold text-[#0ecb81]">{profile?.officeId ?? 24}</p>
|
||||
</div>
|
||||
<div className="o24-card">
|
||||
<p className="text-xs text-[#848e9c] uppercase">Default currency</p>
|
||||
<p className="text-2xl font-bold text-[#eaecef]">{profile?.settlement?.defaultCurrency ?? 'USD'}</p>
|
||||
<p className="text-xs text-[#848e9c] uppercase">M1 circulating (GL 2100)</p>
|
||||
<p className="text-2xl font-bold text-[#eaecef]">
|
||||
{formatUsd(moneySupply?.M1?.circulating)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="o24-card">
|
||||
<p className="text-xs text-[#848e9c] uppercase">Due from HO (GL 1410)</p>
|
||||
<p className="text-2xl font-bold text-[#3861fb]">
|
||||
{formatUsd(moneySupply?.interoffice?.dueFromHeadOffice)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="o24-card">
|
||||
<p className="text-xs text-[#848e9c] uppercase">M2 tradable tokens</p>
|
||||
@@ -49,6 +58,12 @@ export default function Office24Dashboard() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{moneySupply?.fineractLive === false && (
|
||||
<p className="text-sm text-[#f6465d] mb-6">
|
||||
No Fineract journals for Office 24 yet. Post opening M1: Dr 1410 / Cr 2100 via{' '}
|
||||
<code className="text-[#f0b90b]">scripts/deployment/seed-office-24-opening-journal.mjs</code>
|
||||
</p>
|
||||
)}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-8">
|
||||
<section className="o24-card lg:col-span-1">
|
||||
<h2 className="font-semibold text-[#eaecef] mb-4">Settlement rails</h2>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { DBIS_EXCHANGE_URL, SETTLEMENT_MIDDLEWARE_URL, type DexToken } from '../../config/dex';
|
||||
import type { MoneySupply } from '../central-bank/useCentralBank';
|
||||
|
||||
export type M2Token = DexToken & {
|
||||
moneyLayer?: string;
|
||||
@@ -13,6 +14,7 @@ export function useOffice24() {
|
||||
const [office, setOffice] = useState<Record<string, unknown> | null>(null);
|
||||
const [tokens, setTokens] = useState<M2Token[]>([]);
|
||||
const [health, setHealth] = useState<Record<string, unknown> | null>(null);
|
||||
const [moneySupply, setMoneySupply] = useState<MoneySupply | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const settlement = SETTLEMENT_MIDDLEWARE_URL.replace(/\/$/, '');
|
||||
@@ -21,14 +23,18 @@ export function useOffice24() {
|
||||
const refresh = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const [o, t, h] = await Promise.all([
|
||||
const [o, t, h, ms] = await Promise.all([
|
||||
fetch(`${settlement}/api/v1/settlement/office`).then((r) => r.json()),
|
||||
fetch(`${settlement}/api/v1/settlement/tokens/m2`).then((r) => r.json()),
|
||||
fetch(`${exchange}/api/v1/exchange/health`).then((r) => r.json()),
|
||||
fetch(`${settlement}/api/v1/settlement/money-supply/public`).then((r) =>
|
||||
r.ok ? r.json() : null,
|
||||
),
|
||||
]);
|
||||
setOffice(o);
|
||||
setTokens(Array.isArray(t.tokens) ? t.tokens : []);
|
||||
setHealth(h);
|
||||
setMoneySupply(ms);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -38,5 +44,5 @@ export function useOffice24() {
|
||||
refresh();
|
||||
}, [refresh]);
|
||||
|
||||
return { office, tokens, health, loading, refresh };
|
||||
return { office, tokens, health, moneySupply, loading, refresh };
|
||||
}
|
||||
|
||||
11
frontend-dapp/src/utils/formatMoney.ts
Normal file
11
frontend-dapp/src/utils/formatMoney.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/** Format Fineract USD balance strings for dashboard display */
|
||||
export function formatUsd(value: string | number | undefined | null): string {
|
||||
if (value === undefined || value === null || value === '') return '—';
|
||||
const n = typeof value === 'number' ? value : parseFloat(String(value));
|
||||
if (!Number.isFinite(n)) return '—';
|
||||
return new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'USD',
|
||||
maximumFractionDigits: 2,
|
||||
}).format(n);
|
||||
}
|
||||
@@ -73,6 +73,12 @@ start_svc() {
|
||||
DBIS_EXCHANGE_CONFIG="$DBIS_EXCHANGE_CONFIG" \
|
||||
OMNL_SUPPORTED_CHAINS_CONFIG="$OMNL_SUPPORTED_CHAINS_CONFIG" \
|
||||
OMNL_M2_TOKEN_REGISTRY="$OMNL_M2_TOKEN_REGISTRY" \
|
||||
OMNL_API_KEY="${OMNL_API_KEY:-}" \
|
||||
OMNL_FINERACT_BASE_URL="${OMNL_FINERACT_BASE_URL:-${FINERACT_BASE_URL:-}}" \
|
||||
OMNL_FINERACT_TENANT="${OMNL_FINERACT_TENANT:-${FINERACT_TENANT:-omnl}}" \
|
||||
OMNL_FINERACT_USERNAME="${OMNL_FINERACT_USERNAME:-}" \
|
||||
OMNL_FINERACT_PASSWORD="${OMNL_FINERACT_PASSWORD:-}" \
|
||||
OMNL_PUBLIC_MONEY_SUPPLY="${OMNL_PUBLIC_MONEY_SUPPLY:-1}" \
|
||||
bash -c "$cmd" >"$LOG_DIR/$name.log" 2>&1 &
|
||||
echo $! >"$LOG_DIR/$name.pid"
|
||||
}
|
||||
@@ -94,6 +100,8 @@ sleep 5
|
||||
log "Health checks..."
|
||||
curl -sf "http://127.0.0.1:3011/api/v1/settlement/health" | head -c 200 || true
|
||||
echo
|
||||
curl -sf "http://127.0.0.1:3011/api/v1/settlement/money-supply/public" | head -c 300 || true
|
||||
echo
|
||||
curl -sf "http://127.0.0.1:3012/api/v1/exchange/health" | head -c 200 || true
|
||||
echo
|
||||
curl -sf -o /dev/null -w "frontend=%{http_code}\n" "http://127.0.0.1:$FRONTEND_PORT/central-bank"
|
||||
|
||||
73
scripts/deployment/seed-office-24-opening-journal.mjs
Normal file
73
scripts/deployment/seed-office-24-opening-journal.mjs
Normal file
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Post Office 24 opening M1 journal to Fineract:
|
||||
* Dr 1410 Due From Head Office
|
||||
* Cr 2100 M1 Central Liabilities
|
||||
*
|
||||
* Requires: OMNL_FINERACT_BASE_URL, OMNL_FINERACT_PASSWORD
|
||||
* Amount: OFFICE24_OPENING_M1_USD (required unless --dry-run)
|
||||
*/
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const repoRoot = path.resolve(__dirname, '../..');
|
||||
const configPath = path.join(repoRoot, 'config/offices/office-24-opening-journal.v1.json');
|
||||
|
||||
const dryRun = process.argv.includes('--dry-run');
|
||||
const cfg = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
||||
|
||||
const base = (process.env.OMNL_FINERACT_BASE_URL || process.env.FINERACT_BASE_URL || '').replace(/\/$/, '');
|
||||
const tenant = process.env.OMNL_FINERACT_TENANT || process.env.FINERACT_TENANT || 'omnl';
|
||||
const user =
|
||||
process.env.OMNL_FINERACT_USER?.trim() ||
|
||||
process.env.OMNL_FINERACT_USERNAME?.trim() ||
|
||||
'ali_hospitallers_tenant';
|
||||
const pass = process.env.OMNL_FINERACT_PASSWORD || '';
|
||||
const amountRaw = process.env[cfg.amountEnv] || process.env.OFFICE24_OPENING_M1_USD || '';
|
||||
const amount = parseFloat(amountRaw);
|
||||
|
||||
if (!base || !pass) {
|
||||
console.error('Set OMNL_FINERACT_BASE_URL and OMNL_FINERACT_PASSWORD');
|
||||
process.exit(1);
|
||||
}
|
||||
if (!Number.isFinite(amount) || amount <= 0) {
|
||||
console.error(`Set ${cfg.amountEnv} to a positive USD amount (e.g. export OFFICE24_OPENING_M1_USD=1000000)`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const entry = {
|
||||
officeId: cfg.officeId,
|
||||
transactionDate: new Date().toISOString().slice(0, 10),
|
||||
referenceNumber: cfg.referenceNumber,
|
||||
comments: cfg.comments,
|
||||
debits: [{ glAccountId: parseInt(cfg.debitGlCode, 10), amount }],
|
||||
credits: [{ glAccountId: parseInt(cfg.creditGlCode, 10), amount }],
|
||||
};
|
||||
|
||||
console.log(JSON.stringify({ dryRun, officeId: cfg.officeId, amount, entry }, null, 2));
|
||||
|
||||
if (dryRun) {
|
||||
console.log('Dry run — no journal posted.');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const auth = Buffer.from(`${user}:${pass}`).toString('base64');
|
||||
const res = await fetch(`${base}/journalentries`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Basic ${auth}`,
|
||||
'Fineract-Platform-TenantId': tenant,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(entry),
|
||||
});
|
||||
|
||||
const body = await res.text();
|
||||
if (!res.ok) {
|
||||
console.error(`Fineract POST failed (${res.status}): ${body}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log('Office 24 opening journal posted:', body);
|
||||
@@ -20,6 +20,12 @@ start() {
|
||||
DBIS_EXCHANGE_CONFIG="$DBIS_EXCHANGE_CONFIG" \
|
||||
OMNL_SUPPORTED_CHAINS_CONFIG="$OMNL_SUPPORTED_CHAINS_CONFIG" \
|
||||
OMNL_M2_TOKEN_REGISTRY="$OMNL_M2_TOKEN_REGISTRY" \
|
||||
OMNL_API_KEY="${OMNL_API_KEY:-}" \
|
||||
OMNL_FINERACT_BASE_URL="${OMNL_FINERACT_BASE_URL:-}" \
|
||||
OMNL_FINERACT_TENANT="${OMNL_FINERACT_TENANT:-omnl}" \
|
||||
OMNL_FINERACT_USERNAME="${OMNL_FINERACT_USERNAME:-}" \
|
||||
OMNL_FINERACT_PASSWORD="${OMNL_FINERACT_PASSWORD:-}" \
|
||||
OMNL_PUBLIC_MONEY_SUPPLY="${OMNL_PUBLIC_MONEY_SUPPLY:-1}" \
|
||||
bash -c "$cmd" >"${LOG_DIR}/${name}.log" 2>&1 &
|
||||
echo $! >"${LOG_DIR}/${name}.pid"
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ const transfer_1 = require("../../workflows/transfer");
|
||||
const office_scope_1 = require("../../workflows/office-scope");
|
||||
const settlement_store_1 = require("../../store/settlement-store");
|
||||
const swift_inbound_1 = require("../../workflows/swift-inbound");
|
||||
const fineract_1 = require("../../adapters/fineract");
|
||||
function requireApiKey(req, res) {
|
||||
const cfg = (0, config_1.loadConfig)();
|
||||
if (!cfg.production.requireApiKey)
|
||||
@@ -54,6 +55,29 @@ function createSettlementRouter() {
|
||||
storePath: `data/settlement-store/office-${officeId}`,
|
||||
});
|
||||
});
|
||||
router.get('/money-supply/public', async (_req, res) => {
|
||||
const cfg = (0, config_1.loadConfig)();
|
||||
const allow = cfg.production.onlineBankMode || process.env.OMNL_PUBLIC_MONEY_SUPPLY === '1';
|
||||
if (!allow) {
|
||||
res.status(404).json({ error: 'Public money supply disabled' });
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const balances = await (0, fineract_1.fetchGlBalances)(officeId);
|
||||
const snapshot = (0, settlement_core_1.buildMoneySupplySnapshot)(officeId, balances);
|
||||
res.json({
|
||||
...snapshot,
|
||||
fineractLive: Object.keys(balances).length > 0,
|
||||
interoffice: {
|
||||
dueFromHeadOffice: balances['1410'] ?? '0',
|
||||
gl1410: balances['1410'] ?? '0',
|
||||
},
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
res.status(500).json({ error: e instanceof Error ? e.message : String(e) });
|
||||
}
|
||||
});
|
||||
router.get('/money-supply', async (req, res) => {
|
||||
if (!requireApiKey(req, res))
|
||||
return;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Router, type Request, type Response } from 'express';
|
||||
import type { ExternalTransferRequest, TradeFinanceInstrument, TokenLoadRequest, TransferRequest } from '@dbis/settlement-core';
|
||||
import { loadOmnlChainRegistry, getActiveChains } from '@dbis/settlement-core';
|
||||
import { buildMoneySupplySnapshot, loadOmnlChainRegistry, getActiveChains } from '@dbis/settlement-core';
|
||||
import { loadConfig, loadOfficeProfile, settlementOfficeId } from '../../config';
|
||||
import {
|
||||
getMoneySupply,
|
||||
@@ -8,9 +8,11 @@ import {
|
||||
processExternalTransfer,
|
||||
} from '../../workflows/external-transfer';
|
||||
import { processTokenLoad } from '../../workflows/token-load';
|
||||
import { processTransfer } from '../../workflows/transfer';import { bindOffice24 } from '../../workflows/office-scope';
|
||||
import { processTransfer } from '../../workflows/transfer';
|
||||
import { bindOffice24 } from '../../workflows/office-scope';
|
||||
import { settlementStore } from '../../store/settlement-store';
|
||||
import { processSwiftInbound } from '../../workflows/swift-inbound';
|
||||
import { fetchGlBalances } from '../../adapters/fineract';
|
||||
|
||||
function requireApiKey(req: Request, res: Response): boolean {
|
||||
const cfg = loadConfig();
|
||||
@@ -57,6 +59,30 @@ export function createSettlementRouter(): Router {
|
||||
});
|
||||
});
|
||||
|
||||
router.get('/money-supply/public', async (_req, res) => {
|
||||
const cfg = loadConfig();
|
||||
const allow =
|
||||
cfg.production.onlineBankMode || process.env.OMNL_PUBLIC_MONEY_SUPPLY === '1';
|
||||
if (!allow) {
|
||||
res.status(404).json({ error: 'Public money supply disabled' });
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const balances = await fetchGlBalances(officeId);
|
||||
const snapshot = buildMoneySupplySnapshot(officeId, balances);
|
||||
res.json({
|
||||
...snapshot,
|
||||
fineractLive: Object.keys(balances).length > 0,
|
||||
interoffice: {
|
||||
dueFromHeadOffice: balances['1410'] ?? '0',
|
||||
gl1410: balances['1410'] ?? '0',
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
res.status(500).json({ error: e instanceof Error ? e.message : String(e) });
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/money-supply', async (req, res) => {
|
||||
if (!requireApiKey(req, res)) return;
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user