Files
cc-platform-core/server/proxy.js
2026-03-26 15:00:49 -04:00

96 lines
3.2 KiB
JavaScript

/**
* @cc-platform/server/proxy — Cross-service integration proxy
*
* Extracted from the reciprocal proxy endpoints in both servers:
* remoteturtle: /api/integration/inventory-locate → INVENTORY_SERVER_URL
* inventory-manager: /api/integration/turtle-status → TURTLE_SERVER_URL
*
* Both use the exact same pattern: check URL configured → fetch → forward JSON.
*
* Usage:
* const { proxyRequest, createProxyEndpoint } = require('@cc-platform/server/proxy');
*
* // One-off proxy call
* const result = await proxyRequest('http://other-server:3001', '/api/items', { q: 'diamond' });
*
* // Register a proxy endpoint on Express
* createProxyEndpoint(app, '/api/integration/items', 'INVENTORY_SERVER_URL', '/api/integration/items');
*/
'use strict';
/**
* Proxy a request to a remote service, returning JSON.
*
* @param {string} baseUrl - Base URL of the remote service (e.g. 'http://localhost:3001')
* @param {string} remotePath - Path on the remote service (e.g. '/api/items')
* @param {Object} [query={}] - Query parameters to forward
* @param {Object} [options={}] - Additional options
* @param {number} [options.timeoutMs=5000] - Request timeout in milliseconds
* @returns {Promise<Object>} Parsed JSON response, or error/unconfigured object
*/
async function proxyRequest(baseUrl, remotePath, query = {}, options = {}) {
if (!baseUrl) {
return {
configured: false,
message: 'Integration not configured — remote service URL not set',
};
}
const timeoutMs = options.timeoutMs || 5000;
try {
const params = new URLSearchParams(query).toString();
const url = params
? `${baseUrl}${remotePath}?${params}`
: `${baseUrl}${remotePath}`;
const resp = await fetch(url, {
signal: AbortSignal.timeout(timeoutMs),
});
if (!resp.ok) {
return {
error: `Remote service returned ${resp.status}: ${resp.statusText}`,
status: resp.status,
};
}
return await resp.json();
} catch (e) {
return {
error: `Integration unavailable: ${e.message}`,
};
}
}
/**
* Register a proxy endpoint on an Express app.
*
* Creates a GET endpoint at `localPath` that proxies requests to
* `remotePath` on the service URL specified by the given environment variable.
*
* @param {Express.Application} app - Express app instance
* @param {string} localPath - Local endpoint path (e.g. '/api/integration/items')
* @param {string} remoteBaseUrlEnvVar - Env var name for the remote URL (e.g. 'INVENTORY_SERVER_URL')
* @param {string} remotePath - Path on the remote service (e.g. '/api/integration/items')
* @param {Object} [options={}] - proxyRequest options
* @returns {string} The resolved base URL (for logging)
*/
function createProxyEndpoint(app, localPath, remoteBaseUrlEnvVar, remotePath, options = {}) {
const baseUrl = process.env[remoteBaseUrlEnvVar] || '';
app.get(localPath, async (req, res) => {
try {
const result = await proxyRequest(baseUrl, remotePath, req.query, options);
res.json(result);
} catch (e) {
res.status(500).json({ error: e.message });
}
});
return baseUrl;
}
module.exports = { proxyRequest, createProxyEndpoint };