feat(omnl-banking-portal): hub/bridge routes, hash anchors, and /omnl nginx proxy
Some checks failed
CI/CD Pipeline / Solidity Contracts (push) Failing after 1m22s
CI/CD Pipeline / Security Scanning (push) Successful in 2m40s
CI/CD Pipeline / Lint and Format (push) Failing after 50s
CI/CD Pipeline / Terraform Validation (push) Failing after 27s
CI/CD Pipeline / Kubernetes Validation (push) Successful in 28s
Validation / validate-genesis (push) Successful in 36s
Validation / validate-terraform (push) Failing after 36s
Validation / validate-kubernetes (push) Failing after 13s
Validation / validate-smart-contracts (push) Failing after 13s
Validation / validate-security (push) Failing after 1m33s
Validation / validate-documentation (push) Failing after 24s
Some checks failed
CI/CD Pipeline / Solidity Contracts (push) Failing after 1m22s
CI/CD Pipeline / Security Scanning (push) Successful in 2m40s
CI/CD Pipeline / Lint and Format (push) Failing after 50s
CI/CD Pipeline / Terraform Validation (push) Failing after 27s
CI/CD Pipeline / Kubernetes Validation (push) Successful in 28s
Validation / validate-genesis (push) Successful in 36s
Validation / validate-terraform (push) Failing after 36s
Validation / validate-kubernetes (push) Failing after 13s
Validation / validate-smart-contracts (push) Failing after 13s
Validation / validate-security (push) Failing after 1m33s
Validation / validate-documentation (push) Failing after 24s
Add DBIS Hub landing page with /hub and /bridge routes, central-bank hash scroll targets, nginx /omnl/ proxy to token-aggregation for local dev and production hosts, and a public-url-zardasht tunnel URL template for operator handoff. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -63,6 +63,13 @@ server {
|
||||
proxy_read_timeout 120s;
|
||||
}
|
||||
|
||||
# OMNL operator HTML consoles (token-aggregation)
|
||||
location /omnl/ {
|
||||
proxy_pass http://omnl_api/omnl/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_read_timeout 120s;
|
||||
}
|
||||
|
||||
location /health {
|
||||
return 200 'OMNL Bank online — 128 chains';
|
||||
add_header Content-Type text/plain;
|
||||
|
||||
@@ -33,6 +33,18 @@ location /exchange/ {
|
||||
proxy_read_timeout 120s;
|
||||
}
|
||||
|
||||
# token-aggregation HTML consoles (/omnl/compliance, /omnl/dashboard, /omnl/terminal)
|
||||
location /omnl/ {
|
||||
include /etc/nginx/omnl-portal-auth-current.conf.inc;
|
||||
proxy_pass http://omnl_api/omnl/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_read_timeout 120s;
|
||||
}
|
||||
|
||||
location /health {
|
||||
return 200 'OMNL Bank online — 128 chains';
|
||||
add_header Content-Type text/plain;
|
||||
|
||||
@@ -56,6 +56,14 @@ http {
|
||||
proxy_read_timeout 120s;
|
||||
}
|
||||
|
||||
location /omnl/ {
|
||||
include /srv/zardasht-portal/deploy/nginx/omnl-portal-auth-current.conf.inc;
|
||||
proxy_pass http://omnl_lxc_api/omnl/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_read_timeout 120s;
|
||||
}
|
||||
|
||||
location /assets/ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
|
||||
6
deploy/public-url-zardasht.txt.example
Normal file
6
deploy/public-url-zardasht.txt.example
Normal file
@@ -0,0 +1,6 @@
|
||||
# Copy to logs/public-url-zardasht.txt after cloudflared quick tunnel starts (logs/ is gitignored).
|
||||
# Example: echo "https://YOUR-SUBDOMAIN.trycloudflare.com" > logs/public-url-zardasht.txt
|
||||
#
|
||||
# Institutional share: use production FQDNs from config/omnl-banking-portal-link-manifest.v1.json
|
||||
# Entry path for demo tunnel: /hub
|
||||
https://YOUR-SUBDOMAIN.trycloudflare.com
|
||||
@@ -15,6 +15,7 @@ import HistoryPage from './pages/HistoryPage'
|
||||
import AdminPanel from './pages/AdminPanel'
|
||||
import DocsPage from './pages/DocsPage'
|
||||
import WalletsDemoPage from './pages/WalletsDemoPage'
|
||||
import HubPage from './pages/HubPage'
|
||||
import Layout from './components/layout/Layout'
|
||||
import OmnlProductLayout from './components/layout/OmnlProductLayout'
|
||||
import ToastProvider from './components/ui/ToastProvider'
|
||||
@@ -49,6 +50,7 @@ function App() {
|
||||
<AdminProvider>
|
||||
<Routes>
|
||||
<Route element={<OmnlProductLayout />}>
|
||||
<Route path="/hub" element={<HubPage />} />
|
||||
<Route path="/central-bank" element={<CentralBankPage />} />
|
||||
<Route path="/office-24" element={<Office24Page />} />
|
||||
<Route path="/trade" element={<TradePage />} />
|
||||
@@ -62,6 +64,7 @@ function App() {
|
||||
}
|
||||
>
|
||||
<Route path="/" element={<BridgePage />} />
|
||||
<Route path="/bridge" element={<BridgePage />} />
|
||||
<Route path="/reserve" element={<ReservePage />} />
|
||||
<Route path="/history" element={<HistoryPage />} />
|
||||
<Route path="/admin" element={<AdminPanel />} />
|
||||
|
||||
@@ -26,9 +26,9 @@ export default function Layout({ children }: LayoutProps) {
|
||||
</Link>
|
||||
<div className="hidden sm:flex gap-1">
|
||||
<Link
|
||||
to="/"
|
||||
to="/bridge"
|
||||
className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
|
||||
isActive('/') ? 'bg-teal-600 text-white' : 'text-[#A0A0A0] hover:text-white hover:bg-white/5'
|
||||
isActive('/bridge') || isActive('/') ? 'bg-teal-600 text-white' : 'text-[#A0A0A0] hover:text-white hover:bg-white/5'
|
||||
}`}
|
||||
>
|
||||
Bridge
|
||||
|
||||
@@ -12,6 +12,8 @@ import NasaIcon, { type NasaIconName } from '../icons/NasaIcon';
|
||||
|
||||
const PRODUCTS: { path: string; label: string; icon: NasaIconName }[] = [
|
||||
|
||||
{ path: '/hub', label: 'Hub', icon: 'satellite' },
|
||||
|
||||
{ path: '/central-bank', label: 'Central Bank', icon: 'central-bank' },
|
||||
|
||||
{ path: '/office-24', label: 'Office 24', icon: 'office' },
|
||||
@@ -46,7 +48,7 @@ export default function OmnlProductLayout() {
|
||||
|
||||
<div className="max-w-[1600px] mx-auto px-4 h-14 flex items-center gap-4">
|
||||
|
||||
<Link to="/central-bank" className="flex items-center gap-2 shrink-0">
|
||||
<Link to="/hub" className="flex items-center gap-2 shrink-0">
|
||||
|
||||
<span className="w-8 h-8 border border-[#2d6bb8] bg-[#163a6b] text-[#00d4ff] font-bold flex items-center justify-center text-sm">
|
||||
|
||||
@@ -88,7 +90,7 @@ export default function OmnlProductLayout() {
|
||||
|
||||
<Link
|
||||
|
||||
to="/"
|
||||
to="/bridge"
|
||||
|
||||
className="nasa-nav-link hidden md:inline shrink-0 !border-transparent"
|
||||
|
||||
|
||||
@@ -114,6 +114,8 @@ export default function CentralBankDashboard() {
|
||||
|
||||
|
||||
|
||||
<section id="z-full-settlement" className="scroll-mt-20">
|
||||
|
||||
<h2 className="dash-section-title mb-1 flex items-center gap-2">
|
||||
|
||||
<NasaIcon name="money" size={14} className="nasa-icon--telemetry" />
|
||||
@@ -144,8 +146,14 @@ export default function CentralBankDashboard() {
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
|
||||
<div id="z-settlement-protocols" className="scroll-mt-20">
|
||||
|
||||
<DashboardSection
|
||||
|
||||
title="Settlement service"
|
||||
@@ -216,6 +224,10 @@ export default function CentralBankDashboard() {
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<DashboardSection title="General ledger map" subtitle={`Money layers · Office ${office?.officeId ?? 24}`} icon="ledger">
|
||||
|
||||
<div className="flex flex-wrap gap-2 mb-4">
|
||||
|
||||
@@ -1,109 +1,109 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { fetchOmnlChainRegistry, type ChainRegistryResponse, type OmnlChainSummary } from '../../config/supported-chains';
|
||||
|
||||
import NasaIcon, { StatusIcon } from '../../components/icons/NasaIcon';
|
||||
|
||||
|
||||
|
||||
type Filter = 'all' | 'active' | 'primary' | 'l2';
|
||||
|
||||
|
||||
|
||||
export default function ChainMatrixPanel() {
|
||||
|
||||
const [registry, setRegistry] = useState<ChainRegistryResponse | null>(null);
|
||||
|
||||
const [filter, setFilter] = useState<Filter>('active');
|
||||
|
||||
const [search, setSearch] = useState('');
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
fetchOmnlChainRegistry().then(setRegistry);
|
||||
|
||||
}, []);
|
||||
|
||||
|
||||
|
||||
const chains = (registry?.chains ?? []).filter((c) => {
|
||||
|
||||
if (filter === 'active' && c.status !== 'active') return false;
|
||||
|
||||
if (filter === 'primary' && c.omnlRole !== 'settlement-hub' && c.omnlRole !== 'mirror-target') return false;
|
||||
|
||||
if (filter === 'l2' && c.tier !== 'l2') return false;
|
||||
|
||||
if (search) {
|
||||
|
||||
const q = search.toLowerCase();
|
||||
|
||||
return c.name.toLowerCase().includes(q) || String(c.chainId).includes(q) || c.nativeSymbol.toLowerCase().includes(q);
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
return (
|
||||
|
||||
<section className="dash-card cb-card lg:col-span-2">
|
||||
|
||||
<div className="flex flex-wrap items-center justify-between gap-3 mb-4">
|
||||
|
||||
<div>
|
||||
|
||||
<h2 className="dash-section-title flex items-center gap-2">
|
||||
|
||||
<NasaIcon name="network" size={14} className="nasa-icon--telemetry" />
|
||||
|
||||
128-chain network matrix
|
||||
|
||||
</h2>
|
||||
|
||||
<p className="text-xs text-[#7a9bb8] font-mono mt-1">
|
||||
|
||||
{registry?.stats?.active ?? 0} active · {registry?.stats?.staged ?? 0} staged ·{' '}
|
||||
|
||||
{registry?.stats?.reserved ?? 0} reserved · primary {registry?.primaryChainId ?? 138}
|
||||
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
|
||||
<NasaIcon name="search" size={14} className="nasa-icon--telemetry" />
|
||||
|
||||
<input
|
||||
|
||||
type="search"
|
||||
|
||||
placeholder="Search chain…"
|
||||
|
||||
value={search}
|
||||
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
|
||||
className="nasa-input !min-h-[36px] !w-48 text-sm"
|
||||
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div className="flex gap-2 mb-4 flex-wrap">
|
||||
|
||||
{(['all', 'active', 'primary', 'l2'] as Filter[]).map((f) => (
|
||||
|
||||
<button
|
||||
|
||||
key={f}
|
||||
import { useEffect, useState } from 'react';
|
||||
import { fetchOmnlChainRegistry, type ChainRegistryResponse, type OmnlChainSummary } from '../../config/supported-chains';
|
||||
import NasaIcon, { StatusIcon } from '../../components/icons/NasaIcon';
|
||||
|
||||
type Filter = 'all' | 'active' | 'primary' | 'l2';
|
||||
|
||||
export default function ChainMatrixPanel() {
|
||||
const [registry, setRegistry] = useState<ChainRegistryResponse | null>(null);
|
||||
const [filter, setFilter] = useState<Filter>('active');
|
||||
const [search, setSearch] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
fetchOmnlChainRegistry().then(setRegistry);
|
||||
}, []);
|
||||
|
||||
const chains = (registry?.chains ?? []).filter((c) => {
|
||||
if (filter === 'active' && c.status !== 'active') return false;
|
||||
if (filter === 'primary' && c.omnlRole !== 'settlement-hub' && c.omnlRole !== 'mirror-target') return false;
|
||||
if (filter === 'l2' && c.tier !== 'l2') return false;
|
||||
if (search) {
|
||||
const q = search.toLowerCase();
|
||||
return c.name.toLowerCase().includes(q) || String(c.chainId).includes(q) || c.nativeSymbol.toLowerCase().includes(q);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
return (
|
||||
<section id="z-settlement-protocols-chains" className="dash-card cb-card lg:col-span-2 scroll-mt-20">
|
||||
<div className="flex flex-wrap items-center justify-between gap-3 mb-4">
|
||||
<div>
|
||||
<h2 className="dash-section-title flex items-center gap-2">
|
||||
<NasaIcon name="network" size={14} className="nasa-icon--telemetry" />
|
||||
128-chain network matrix
|
||||
</h2>
|
||||
<p className="text-xs text-[#7a9bb8] font-mono mt-1">
|
||||
{registry?.stats?.active ?? 0} active · {registry?.stats?.staged ?? 0} staged ·{' '}
|
||||
{registry?.stats?.reserved ?? 0} reserved · primary {registry?.primaryChainId ?? 138}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<NasaIcon name="search" size={14} className="nasa-icon--telemetry" />
|
||||
<input
|
||||
type="search"
|
||||
placeholder="Search chain…"
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
className="nasa-input !min-h-[36px] !w-48 text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 mb-4 flex-wrap">
|
||||
{(['all', 'active', 'primary', 'l2'] as Filter[]).map((f) => (
|
||||
<button
|
||||
key={f}
|
||||
type="button"
|
||||
onClick={() => setFilter(f)}
|
||||
className={`px-3 py-1 text-xs capitalize font-mono border ${
|
||||
filter === f
|
||||
? 'border-[#00d4ff] text-[#00d4ff] bg-[#00d4ff]/10'
|
||||
: 'border-[#1e4a8a] text-[#7a9bb8]'
|
||||
}`}
|
||||
>
|
||||
{f}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="overflow-x-auto max-h-[360px] overflow-y-auto">
|
||||
<table className="w-full text-sm nasa-table">
|
||||
<thead className="sticky top-0">
|
||||
<tr className="text-left">
|
||||
<th className="py-2 pr-3">ID</th>
|
||||
<th className="py-2 pr-3">Network</th>
|
||||
<th className="py-2 pr-3">Role</th>
|
||||
<th className="py-2 pr-3">Status</th>
|
||||
<th className="py-2 pr-3">Swap</th>
|
||||
<th className="py-2">Bridge</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{chains.map((c: OmnlChainSummary) => (
|
||||
<tr key={c.chainId}>
|
||||
<td className="py-1.5 pr-3 font-mono text-[#00d4ff]">{c.chainId}</td>
|
||||
<td className="py-1.5 pr-3 text-[#e8f4ff]">{c.name}</td>
|
||||
<td className="py-1.5 pr-3 text-xs text-[#7a9bb8]">{c.omnlRole}</td>
|
||||
<td className="py-1.5 pr-3">
|
||||
<span
|
||||
className={`text-xs px-1.5 py-0.5 font-mono uppercase ${
|
||||
c.status === 'active'
|
||||
? 'text-[#3dff8a] border border-[#3dff8a]/35'
|
||||
: c.status === 'staged'
|
||||
? 'text-[#ffb347] border border-[#ffb347]/35'
|
||||
: 'text-[#7a9bb8] border border-[#7a9bb8]/35'
|
||||
}`}
|
||||
>
|
||||
{c.status}
|
||||
</span>
|
||||
</td>
|
||||
<td className="py-1.5 pr-3"><StatusIcon ok={!!c.settlement?.swapEnabled} /></td>
|
||||
<td className="py-1.5"><StatusIcon ok={!!c.settlement?.bridgeEnabled} /></td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import CentralBankDashboard from '../features/central-bank/CentralBankDashboard';
|
||||
|
||||
export default function CentralBankPage() {
|
||||
const { hash } = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
if (!hash) return;
|
||||
const id = hash.replace(/^#/, '');
|
||||
const el = document.getElementById(id);
|
||||
el?.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}, [hash]);
|
||||
|
||||
return <CentralBankDashboard />;
|
||||
}
|
||||
|
||||
76
frontend-dapp/src/pages/HubPage.tsx
Normal file
76
frontend-dapp/src/pages/HubPage.tsx
Normal file
@@ -0,0 +1,76 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import NasaIcon, { type NasaIconName } from '../components/icons/NasaIcon';
|
||||
|
||||
const BANKING: { path: string; label: string; icon: NasaIconName }[] = [
|
||||
{ path: '/central-bank', label: 'Z Online Bank / Central Bank', icon: 'central-bank' },
|
||||
{ path: '/central-bank#z-full-settlement', label: 'M0→M4 settlement', icon: 'money' },
|
||||
{ path: '/central-bank#z-settlement-protocols', label: 'Wire & chain protocols', icon: 'network' },
|
||||
{ path: '/central-bank#z-settlement-protocols-chains', label: '128-chain matrix', icon: 'network' },
|
||||
{ path: '/office-24', label: 'Office 24', icon: 'office' },
|
||||
{ path: '/trade', label: 'DBIS Trade', icon: 'trade' },
|
||||
{ path: '/swap', label: 'Digital Swap', icon: 'swap' },
|
||||
];
|
||||
|
||||
const CHAIN138: { path: string; label: string; icon: NasaIconName }[] = [
|
||||
{ path: '/bridge', label: 'Bridge', icon: 'bridge' },
|
||||
{ path: '/reserve', label: 'Reserve', icon: 'ledger' },
|
||||
{ path: '/history', label: 'History', icon: 'chart' },
|
||||
{ path: '/wallets', label: 'Wallets', icon: 'globe' },
|
||||
{ path: '/docs', label: 'Developers / API docs', icon: 'link' },
|
||||
{ path: '/admin', label: 'Admin', icon: 'satellite' },
|
||||
];
|
||||
|
||||
const OMNL_OPS: { path: string; label: string; icon: NasaIconName }[] = [
|
||||
{ path: '/omnl/compliance', label: 'OMNL Compliance console', icon: 'ledger' },
|
||||
{ path: '/omnl/dashboard', label: 'OMNL API snapshot', icon: 'chart' },
|
||||
{ path: '/omnl/terminal', label: 'Settlement terminal', icon: 'rail' },
|
||||
];
|
||||
|
||||
function LinkGrid({
|
||||
title,
|
||||
items,
|
||||
}: {
|
||||
title: string;
|
||||
items: { path: string; label: string; icon: NasaIconName }[];
|
||||
}) {
|
||||
return (
|
||||
<section className="dash-card cb-card p-4">
|
||||
<h2 className="dash-section-title mb-3">{title}</h2>
|
||||
<ul className="grid grid-cols-1 sm:grid-cols-2 gap-2">
|
||||
{items.map(({ path, label, icon }) => (
|
||||
<li key={path}>
|
||||
<Link
|
||||
to={path}
|
||||
className="dash-quick-links__chip inline-flex w-full !justify-start gap-2 py-2.5 px-3"
|
||||
>
|
||||
<NasaIcon name={icon} size={14} />
|
||||
{label}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default function HubPage() {
|
||||
return (
|
||||
<div className="dash-page cb-page max-w-[1400px] mx-auto px-4 py-6">
|
||||
<header className="mb-8">
|
||||
<p className="text-xs font-mono text-[#7a9bb8] mb-1">DBIS · OMNL · Chain 138</p>
|
||||
<h1 className="text-2xl font-semibold text-[#e8f4ff] flex items-center gap-2">
|
||||
<NasaIcon name="satellite" size={22} className="nasa-icon--telemetry" />
|
||||
DBIS Hub
|
||||
</h1>
|
||||
<p className="text-sm text-[#7a9bb8] mt-2 max-w-2xl">
|
||||
Mission control entry — banking portals, settlement surfaces, and Chain 138 tools on this host.
|
||||
</p>
|
||||
</header>
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 mb-4">
|
||||
<LinkGrid title="Banking & settlement" items={BANKING} />
|
||||
<LinkGrid title="Chain 138 & operators" items={CHAIN138} />
|
||||
</div>
|
||||
<LinkGrid title="OMNL operator consoles" items={OMNL_OPS} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user