Files
smom-dbis-138/frontend-dapp/scripts/portal-serve.mjs
zaragoza444 a2db92f3cb
Some checks failed
CI/CD Pipeline / Solidity Contracts (push) Failing after 1m14s
CI/CD Pipeline / Security Scanning (push) Successful in 2m34s
CI/CD Pipeline / Lint and Format (push) Failing after 40s
CI/CD Pipeline / Terraform Validation (push) Failing after 24s
CI/CD Pipeline / Kubernetes Validation (push) Successful in 26s
Deploy ChainID 138 / Deploy ChainID 138 (push) Failing after 37s
HYBX OMNL TypeScript & anchor / token-aggregation build + reconcile artifact (push) Failing after 22s
Validation / validate-genesis (push) Successful in 24s
Validation / validate-terraform (push) Failing after 24s
Validation / validate-kubernetes (push) Failing after 9s
Validation / validate-smart-contracts (push) Failing after 10s
Validation / validate-security (push) Failing after 1m22s
Validation / validate-documentation (push) Failing after 17s
Verify Deployment / Verify Deployment (push) Failing after 47s
feat: split Z Ecosystem from OMNL/DBIS with separate builds and deploy
Add VITE_ECOSYSTEM build profiles, Z-only nginx vhosts, standalone Z production stack script, and remove Z Chain from OMNL registry.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-07 05:17:40 -07:00

143 lines
4.8 KiB
JavaScript

/**
* Production-style portal server: static dist + API proxies (matches vite.config.ts).
*/
import http from 'http';
import https from 'https';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const DIST = path.join(__dirname, '..', 'dist');
const PORT = Number(process.env.PORT || process.env.OMNL_BANK_FRONTEND_PORT || 3002);
const API_TARGET = process.env.TOKEN_AGGREGATION_URL || 'http://127.0.0.1:3000';
const SETTLEMENT_TARGET = process.env.SETTLEMENT_URL || process.env.SETTLEMENT_MIDDLEWARE_URL || 'http://127.0.0.1:3011';
const EXCHANGE_TARGET = process.env.EXCHANGE_URL || '';
const MIME = {
'.html': 'text/html; charset=utf-8',
'.js': 'application/javascript; charset=utf-8',
'.css': 'text/css; charset=utf-8',
'.json': 'application/json; charset=utf-8',
'.svg': 'image/svg+xml',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.gif': 'image/gif',
'.webp': 'image/webp',
'.ico': 'image/x-icon',
'.woff': 'font/woff',
'.woff2': 'font/woff2',
'.ttf': 'font/ttf',
'.map': 'application/json',
};
function shouldServeOmnlStatic(url) {
return (
/^\/omnl\/(compliance|dashboard|terminal)\/?(\?.*)?$/.test(url) ||
url.startsWith('/omnl/static/')
);
}
function proxyRequest(req, res, targetBase, rewrite) {
const incoming = new URL(req.url, 'http://localhost');
const target = new URL(targetBase);
const pathname = rewrite ? rewrite(incoming.pathname) : incoming.pathname;
const options = {
protocol: target.protocol,
hostname: target.hostname,
port: target.port || (target.protocol === 'https:' ? 443 : 80),
path: `${pathname}${incoming.search}`,
method: req.method,
headers: { ...req.headers, host: target.host },
};
const lib = target.protocol === 'https:' ? https : http;
const proxyReq = lib.request(options, (proxyRes) => {
res.writeHead(proxyRes.statusCode ?? 502, proxyRes.headers);
proxyRes.pipe(res);
});
proxyReq.on('error', (err) => {
if (!res.headersSent) {
res.writeHead(502, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'proxy_error', message: err.message }));
}
});
req.pipe(proxyReq);
}
function sendFile(res, filePath, cacheControl) {
const ext = path.extname(filePath).toLowerCase();
const type = MIME[ext] || 'application/octet-stream';
const headers = { 'Content-Type': type };
if (cacheControl) headers['Cache-Control'] = cacheControl;
res.writeHead(200, headers);
fs.createReadStream(filePath).pipe(res);
}
function resolveStatic(urlPath) {
const decoded = decodeURIComponent(urlPath.split('?')[0]);
if (decoded.endsWith('/')) {
const indexPath = path.join(DIST, decoded, 'index.html');
if (fs.existsSync(indexPath)) return indexPath;
}
const direct = path.join(DIST, decoded);
if (fs.existsSync(direct) && fs.statSync(direct).isFile()) return direct;
const withIndex = path.join(DIST, decoded, 'index.html');
if (fs.existsSync(withIndex)) return withIndex;
return null;
}
const server = http.createServer((req, res) => {
const url = req.url || '/';
if (url.startsWith('/api')) {
return proxyRequest(req, res, API_TARGET);
}
if (url.startsWith('/reserve')) {
return proxyRequest(req, res, API_TARGET);
}
if (url.startsWith('/settlement')) {
return proxyRequest(req, res, SETTLEMENT_TARGET, (pathname) =>
pathname.replace(/^\/settlement/, '/api/v1/settlement'),
);
}
if (url.startsWith('/exchange')) {
if (!EXCHANGE_TARGET) {
res.writeHead(404, { 'Content-Type': 'application/json' });
return res.end(JSON.stringify({ error: 'not_available', message: 'Z Ecosystem only' }));
}
return proxyRequest(req, res, EXCHANGE_TARGET);
}
if (url.startsWith('/omnl/')) {
res.writeHead(404, { 'Content-Type': 'application/json' });
return res.end(JSON.stringify({ error: 'not_available', message: 'Z Ecosystem only' }));
}
const file = resolveStatic(url === '/' ? '/index.html' : url);
if (file) {
const cache =
url.startsWith('/assets/') ? 'public, max-age=31536000, immutable' : 'no-cache, no-store, must-revalidate';
return sendFile(res, file, cache);
}
const indexHtml = path.join(DIST, 'index.html');
if (fs.existsSync(indexHtml)) {
return sendFile(res, indexHtml, 'no-cache, no-store, must-revalidate');
}
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Not found');
});
if (!fs.existsSync(DIST)) {
console.error(`Missing build output: ${DIST}\nRun: npm run build`);
process.exit(1);
}
server.listen(PORT, () => {
console.log(`portal-serve listening on http://localhost:${PORT}`);
console.log(` dist: ${DIST}`);
console.log(` api: ${API_TARGET}`);
console.log(` settlement: ${SETTLEMENT_TARGET}`);
});