initial commit
This commit is contained in:
95
server/proxy.js
Normal file
95
server/proxy.js
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* @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 };
|
||||
Reference in New Issue
Block a user