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
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>
143 lines
4.8 KiB
JavaScript
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}`);
|
|
});
|