133 lines
4.1 KiB
JavaScript
133 lines
4.1 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* conductor-mock-server.js
|
|
*
|
|
* A minimal HTTP mock server for local Conductor development and validation.
|
|
* It echoes request bodies through routes that mimic the public HTTPBin API,
|
|
* so Conductor project examples can run fully offline or from within Docker.
|
|
*
|
|
* Usage:
|
|
* node examples/mock-server/conductor-mock-server.js
|
|
*
|
|
* Listens on:
|
|
* http://localhost:8787 (host access)
|
|
* http://host.docker.internal:8787 (Docker container access on Mac/Win)
|
|
*
|
|
* Routes:
|
|
* POST /anything — parse JSON body, return { json: <body>, method, url, headers }
|
|
* GET /get — return { args: <query params>, method, url, headers }
|
|
* * — 404 JSON error
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
const http = require('http');
|
|
const url = require('url');
|
|
|
|
const PORT = 8787;
|
|
const HOST = '0.0.0.0';
|
|
|
|
// ── Request-body reader ───────────────────────────────────────────────────────
|
|
|
|
function readBody(req) {
|
|
return new Promise((resolve, reject) => {
|
|
const chunks = [];
|
|
req.on('data', (chunk) => chunks.push(chunk));
|
|
req.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
|
|
req.on('error', reject);
|
|
});
|
|
}
|
|
|
|
// ── Response helpers ──────────────────────────────────────────────────────────
|
|
|
|
function sendJson(res, status, body) {
|
|
const payload = JSON.stringify(body, null, 2);
|
|
res.writeHead(status, {
|
|
'Content-Type': 'application/json',
|
|
'Content-Length': Buffer.byteLength(payload),
|
|
'Access-Control-Allow-Origin': '*',
|
|
});
|
|
res.end(payload);
|
|
}
|
|
|
|
// ── Route handlers ────────────────────────────────────────────────────────────
|
|
|
|
async function handleAnything(req, res) {
|
|
// Parse body (if any)
|
|
let parsedJson = null;
|
|
const rawBody = await readBody(req);
|
|
|
|
if (rawBody.trim()) {
|
|
try {
|
|
parsedJson = JSON.parse(rawBody);
|
|
} catch (e) {
|
|
sendJson(res, 400, {
|
|
error: 'invalid_json',
|
|
message: `Request body is not valid JSON: ${e.message}`,
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
|
|
const parsed = url.parse(req.url, true);
|
|
|
|
sendJson(res, 200, {
|
|
method: req.method,
|
|
url: req.url,
|
|
args: parsed.query,
|
|
headers: req.headers,
|
|
json: parsedJson,
|
|
data: rawBody || '',
|
|
});
|
|
}
|
|
|
|
function handleGet(req, res) {
|
|
const parsed = url.parse(req.url, true);
|
|
sendJson(res, 200, {
|
|
method: req.method,
|
|
url: req.url,
|
|
args: parsed.query,
|
|
headers: req.headers,
|
|
});
|
|
}
|
|
|
|
// ── Server ────────────────────────────────────────────────────────────────────
|
|
|
|
const server = http.createServer(async (req, res) => {
|
|
// Strip query string for routing
|
|
const pathname = url.parse(req.url).pathname;
|
|
|
|
// OPTIONS preflight (CORS)
|
|
if (req.method === 'OPTIONS') {
|
|
res.writeHead(204, { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': '*' });
|
|
res.end();
|
|
return;
|
|
}
|
|
|
|
try {
|
|
if (pathname === '/anything') {
|
|
await handleAnything(req, res);
|
|
} else if (pathname === '/get' && req.method === 'GET') {
|
|
handleGet(req, res);
|
|
} else {
|
|
sendJson(res, 404, {
|
|
error: 'not_found',
|
|
message: `No mock route for ${req.method} ${pathname}. ` +
|
|
`Available: POST /anything, GET /get`,
|
|
});
|
|
}
|
|
} catch (err) {
|
|
sendJson(res, 500, {
|
|
error: 'internal_error',
|
|
message: err instanceof Error ? err.message : String(err),
|
|
});
|
|
}
|
|
});
|
|
|
|
server.listen(PORT, HOST, () => {
|
|
console.log(`Conductor mock server listening on http://${HOST}:${PORT}`);
|
|
console.log(' POST /anything — echoes JSON body as { json: <body> }');
|
|
console.log(' GET /get — echoes query parameters as { args: <params> }');
|
|
console.log('Press Ctrl+C to stop.');
|
|
});
|