Some checks failed
deploy / deploy (push) Failing after 1m35s
Proof: first non-mocked tradeable loop for one pair using funded NEAR Intents inventory, Kafka, and PostgreSQL. Assumptions: solver-side execution is performed by signed token_diff quote responses over the Solver Relay; EURe is treated as 1:1 with EUR; k3s runtime uses unrip-dev.near as the named signer account. Still fake: signer key is not yet registered on intents.near, strategy and executor remain disarmed by default, and no live mainnet quote response has been submitted from this repo yet.
121 lines
2.7 KiB
JavaScript
121 lines
2.7 KiB
JavaScript
import http from 'node:http';
|
|
|
|
export function startControlApi({
|
|
host = '0.0.0.0',
|
|
port = 8081,
|
|
logger = null,
|
|
service = 'service',
|
|
namespace = 'unrip',
|
|
stateProvider = null,
|
|
healthProvider = null,
|
|
routes = [],
|
|
} = {}) {
|
|
const routeMap = new Map(routes.map((route) => [`${route.method} ${route.path}`, route]));
|
|
|
|
const server = http.createServer(async (req, res) => {
|
|
try {
|
|
if (req.method === 'GET' && req.url === '/healthz') {
|
|
return sendJson(res, 200, {
|
|
ok: true,
|
|
service,
|
|
namespace,
|
|
...(await healthProvider?.getHealth?.()),
|
|
});
|
|
}
|
|
|
|
if (req.method === 'GET' && req.url === '/state') {
|
|
return sendJson(res, 200, {
|
|
service,
|
|
namespace,
|
|
...(await stateProvider?.getState?.()),
|
|
});
|
|
}
|
|
|
|
const route = routeMap.get(`${req.method} ${req.url}`);
|
|
if (!route) {
|
|
return sendJson(res, 404, {
|
|
error: 'not_found',
|
|
});
|
|
}
|
|
|
|
const body = route.readBody === false ? null : await readJsonBody(req);
|
|
const result = await route.handler({
|
|
req,
|
|
res,
|
|
body: body || {},
|
|
});
|
|
|
|
if (result == null) return;
|
|
if (result.statusCode != null) {
|
|
return sendJson(res, result.statusCode, result.payload ?? {});
|
|
}
|
|
return sendJson(res, 200, result);
|
|
} catch (error) {
|
|
logger?.error('control_api_request_failed', {
|
|
details: {
|
|
method: req.method,
|
|
path: req.url,
|
|
error: error.message,
|
|
},
|
|
});
|
|
return sendJson(res, 500, {
|
|
error: error.message,
|
|
});
|
|
}
|
|
});
|
|
|
|
server.listen(port, host, () => {
|
|
logger?.info('control_api_started', {
|
|
details: {
|
|
host,
|
|
port,
|
|
},
|
|
});
|
|
});
|
|
|
|
return {
|
|
close() {
|
|
return new Promise((resolve, reject) => {
|
|
server.close((error) => {
|
|
if (error) reject(error);
|
|
else resolve();
|
|
});
|
|
});
|
|
},
|
|
};
|
|
}
|
|
|
|
export function sendJson(res, statusCode, payload) {
|
|
const body = JSON.stringify(payload, null, 2);
|
|
res.statusCode = statusCode;
|
|
res.setHeader('content-type', 'application/json; charset=utf-8');
|
|
res.end(`${body}\n`);
|
|
}
|
|
|
|
export function readJsonBody(req) {
|
|
return new Promise((resolve, reject) => {
|
|
let raw = '';
|
|
|
|
req.on('data', (chunk) => {
|
|
raw += chunk;
|
|
if (raw.length > 64 * 1024) {
|
|
reject(new Error('request body too large'));
|
|
}
|
|
});
|
|
|
|
req.on('end', () => {
|
|
if (!raw.trim()) {
|
|
resolve({});
|
|
return;
|
|
}
|
|
|
|
try {
|
|
resolve(JSON.parse(raw));
|
|
} catch {
|
|
reject(new Error('invalid_json'));
|
|
}
|
|
});
|
|
|
|
req.on('error', reject);
|
|
});
|
|
}
|