UNPKG

@mindconnect/mindconnect-nodejs

Version:

NodeJS Library for Siemens Insights Hub Connectivity - TypeScript SDK for Insights Hub and Industrial IoT - Command Line Interface - Insights Hub Development Proxy (Siemens Insights Hub was formerly known as MindSphere)

266 lines 16.4 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const console_1 = require("console"); const cross_fetch_1 = require("cross-fetch"); const http = require("http"); const https = require("https"); const https_proxy_agent_1 = require("https-proxy-agent"); const url = require("url"); const util = require("util"); const sdk_1 = require("../../api/sdk"); const utils_1 = require("../../api/utils"); const command_utils_1 = require("./command-utils"); const chalk = require("chalk"); const streamPipeline = util.promisify(require("stream").pipeline); const magenta = (0, command_utils_1.getColor)("magenta"); const red = (0, command_utils_1.getColor)("red"); const yellow = (0, command_utils_1.getColor)("yellow"); const green = (0, command_utils_1.getColor)("green"); let color = yellow; const headers = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "*", "Access-Control-Allow-Headers": "*", "Access-Control-Max-Age": 2592000, // 30 days "cache-control": "no-cache", "x-proxied-by": "mindsphere development proxy", }; exports.default = (program) => { program .command("dev-proxy") .alias("px") .option("-m, --mode [credentials|session]", "service/app credentials authentication of session authentication", "session") .option("-o, --port <port>", "port for web server", "7707") .option("-r, --norewrite", "don't rewrite hal+json urls") .option("-w, --nowarn", "don't warn for missing headers") .option("-d, --dontkeepalive", "don't keep the session alive") .option("-v, --verbose", "verbose output") .option("-s, --session <session>", "borrowed SESSION cookie from brower") .option("-x, --xsrftoken <xsrftoken>", "borrowed XSRF-TOKEN cookie from browser") .option("-h, --host <host>", "the address where SESSION and XSRF-TOKEN have been borrowed from") .option("-t, --timeout <timeout>", "keep alive timeout in seconds", "60") .option("-k, --passkey <passkey>", "passkey") .description(color(`starts mindsphere development proxy & ${magenta("(optional passkey) *")}`)) .action((options) => { (() => __awaiter(void 0, void 0, void 0, function* () { try { color = options.mode === "credentials" ? magenta : yellow; (0, command_utils_1.homeDirLog)(options.verbose, color); (0, command_utils_1.proxyLog)(options.verbose, color); if (options.mode === "credentials") { options.passkey = options.passkey || process.env.MDSP_PASSKEY; } if (options.mode === "session") { options.session = options.session || process.env.MDSP_SESSION; options.xsrftoken = options.xsrftoken || process.env.MDSP_XSRF_TOKEN; options.host = options.host || process.env.MDSP_HOST; (0, command_utils_1.verboseLog)(`${color("cookies:")} ${options.session}, ${options.xsrftoken}, ${options.host}`, options.verbose); } checkRequiredParamaters(options); console.log(`\nMode ${color(options.mode)}`); console.log(`\nCORS support ${green("on")}`); options.mode === "session" && !options.dontkeepalive && console.log(`\nKeep alive session ${color(options.session)} on ${color(options.host)} every ${color(options.timeout)} seconds: ${green("on")} `); console.log(`Rewrite hal+json support ${options.host || (0, utils_1.loadAuth)().gateway} -> ${"http://localhost:" + options.port} ${options.norewrite ? red("off") : green("on")}`); console.log(`warn on missing x-xsrf-token ${options.nowarn ? red("off") : green("on")}\n`); yield serve({ configPort: options.port, options: options }); } catch (err) { (0, command_utils_1.errorLog)(err, options.verbose); } }))(); }) .on("--help", () => { (0, console_1.log)("\n Examples:\n"); (0, console_1.log)(` mdsp dev-proxy \t\t\t\t runs on default port (7707) using ${yellow("cookies")}`); (0, console_1.log)(` mdsp dev-proxy --mode credentials --port 7777 --passkey $MDSP_PASSKEY \t runs on port 7777 using ${magenta("app/service credentials")}`); (0, console_1.log)("\n Configuration:\n"); (0, console_1.log)(` \t- create environment variables: ${color("MDSP_HOST")}, ${color("MDSP_SESSION")} and ${color("MDSP_XSRF_TOKEN")} using borrowed cookies `); (0, console_1.log)(`\n see more documentation at ${color("https://developer.siemens.com/industrial-iot-open-source/mindconnect-nodejs/development-proxy.html")}\n`); }); }; function serve(_a) { return __awaiter(this, arguments, void 0, function* ({ configPort, options }) { const proxy = process.env.http_proxy || process.env.HTTP_PROXY; const proxyHttpAgent = proxy ? new https_proxy_agent_1.HttpsProxyAgent(proxy) : undefined; const server = http.createServer(); const port = configPort || 7707; let sdk; let auth; if (options.mode === "credentials") { auth = (0, utils_1.loadAuth)(); sdk = new sdk_1.MindSphereSdk(Object.assign(Object.assign({}, auth), { basicAuth: (0, utils_1.decrypt)(auth, options.passkey) })); } keepAliveIfConfigured(options, proxyHttpAgent); server.on("error", (err) => { console.log(`[${red(new Date().toISOString())}] ${red(err)}`); }); let region; server.on("request", (req, res) => __awaiter(this, void 0, void 0, function* () { try { const hostname = options.host ? options.host : url.parse(auth.gateway).host; const requestOptions = { hostname: hostname, port: 443, path: req.url, method: req.method, headers: req.headers, agent: proxyHttpAgent, }; !options.nowarn && addWarning(req); requestOptions.headers.host = hostname; if (requestOptions.headers.origin) { requestOptions.headers["X-DevProxy-For"] = requestOptions.headers.origin; options.verbose && console.log(`[${green(new Date().toISOString())}] Setting X-Dev-Proxy-For to ${requestOptions.headers.origin}}`); } if (options.mode === "credentials") { requestOptions.headers["Authorization"] = `Bearer ${yield sdk.GetToken()}`; options.verbose && console.log(`[${green(new Date().toISOString())}] Setting authorization to Bearer ${requestOptions.headers["Authorization"]}`); } else { if (requestOptions.headers.origin) { requestOptions.headers.origin = `https://${options.host}`; options.verbose && console.log(`[${green(new Date().toISOString())}] Setting origin to ${requestOptions.headers.origin}}`); } let newCookie = `SESSION=${options.session}; XSRF-TOKEN=${options.xsrftoken}`; if (region && region !== "") { newCookie += `;REGION-SESSION=${region}`; options.verbose && console.log(`[${green(new Date().toISOString())}] Adding REGION-SESSION cookie ${newCookie}}`); } requestOptions.headers["cookie"] = newCookie; options.verbose && console.log(`[${green(new Date().toISOString())}] Setting mindsphere request cookies to ${newCookie}`); requestOptions.headers["x-xsrf-token"] = options.xsrftoken; options.verbose && console.log(`[${green(new Date().toISOString())}] Setting mindsphere request x-xsrf-token to ${options.xsrftoken}`); } const proxy = https.request(requestOptions, function (proxyres) { const allHeaders = Object.assign(Object.assign({}, headers), proxyres.headers); const logColor = res.statusCode >= 200 && res.statusCode < 400 ? green : red; if (req.method === "OPTIONS") { res.writeHead(204, allHeaders); console.log(`[${color(new Date().toISOString())}] ${logColor(res.statusCode)} ${requestOptions.method} ${requestOptions.path}`); res.end(); return; } let body = ""; proxyres.on("data", (data) => { if (data) { body += data; } }); proxyres.on("end", (data) => { var _a; if (data) { body += data; } let replaced = body; const responseHeaders = Object.assign(Object.assign({}, proxyres.headers), headers); if (!options.norewrite) { const regex = new RegExp(`https://${requestOptions.hostname}`, "g"); const target = `http://localhost:${port}`; replaced = body.replace(regex, target); options.verbose && console.log(`[${green(new Date().toISOString())}] adjusting body (replacing ${requestOptions.hostname} with ${target})`); } if (responseHeaders["transfer-encoding"] === "chunked") { delete responseHeaders["content-length"]; options.verbose && console.log(`[${green(new Date().toISOString())}] deleted content-length (transfer-encoding was chunked)`); } if (options.mode === "session") { const cookies = []; (_a = responseHeaders["set-cookie"]) === null || _a === void 0 ? void 0 : _a.forEach((element) => { const elements = element.split("="); if (elements[0] === "REGION-SESSION") { region = elements[1].split(";")[0]; } }); if (responseHeaders["set-cookie"] && responseHeaders["set-cookie"].length > 0 && region) { cookies.push(`REGION-SESSION=${region}; Path=/;`); } if (responseHeaders["set-cookie"] && responseHeaders["set-cookie"].length > 0) { cookies.push(`SESSION=${options.session}; Path=/;`); cookies.push(`XSRF-TOKEN=${options.xsrftoken}; Path=/;`); options.verbose && console.log(`[${green(new Date().toISOString())}] changing cookies from \n ${JSON.stringify(responseHeaders["set-cookie"], null, 2)}`); responseHeaders["set-cookie"] = cookies; options.verbose && console.log(`[${green(new Date().toISOString())}] rewriting response cookies to \n ${JSON.stringify(cookies)})}`); } } res.writeHead(proxyres.statusCode || 500, responseHeaders); res.statusCode = proxyres.statusCode || 500; res.end(replaced); const okColor = res.statusCode >= 200 && res.statusCode <= 399 ? green : red; const finalLogColor = res.statusCode >= 300 && res.statusCode <= 399 ? chalk.grey : okColor; console.log(`[${color(new Date().toISOString())}] ${finalLogColor(res.statusCode)} ${requestOptions.method} ${requestOptions.path}`); }); }); yield streamPipeline(req, proxy); } catch (error) { console.log(`[${red(new Date().toISOString())}] ${error.message}`); res.writeHead(500, "Internal server error"); res.end(JSON.stringify({ error: error.message })); } })); server.listen(port); console.log(`proxy is available at ${color("http://localhost:" + port)}`); console.log(`example api call (list of assets): ${color("http://localhost:" + port + "/api/assetmanagement/v3/assets")}`); console.log(`API documentation: ${color("https://developer.siemens.com/insights-hub/overview.html")}`); console.log(`press ${color("CTRL + C")} to exit`); }); } function keepAliveIfConfigured(options, proxyHttpAgent) { options.mode === "session" && !options.dontkeepalive && setInterval(() => __awaiter(this, void 0, void 0, function* () { const host = `https://${options.host}`; const newCookie = `SESSION=${options.session}; XSRF-TOKEN=${options.xsrftoken}`; const keepAlive = yield (0, cross_fetch_1.default)(host, { method: "GET", headers: { cookie: newCookie }, agent: proxyHttpAgent, }); const okColor = keepAlive.status >= 200 && keepAlive.status <= 399 ? green : red; const finalLogColor = keepAlive.status >= 300 && keepAlive.status <= 499 ? chalk.gray : okColor; console.log(`[${finalLogColor(new Date().toISOString())}] ${finalLogColor(keepAlive.status)} keep alive ${options.host} `); }), options.timeout * 1000); } function addWarning(req) { if (!req.headers["x-xsrf-token"] && !req.headers["X-XSRF-TOKEN"] && !(req.method === "GET")) { console.log(`[${color(new Date().toISOString())}] ${yellow("WARN: x-xsrf-token is missing. the app will not work after deployment if you use frontend authentication")}`); console.log(`[${color(new Date().toISOString())}] ${yellow("WARN: see")}: ${yellow("https://developer.mindsphere.io/concepts/concept-authentication.html#calling-apis-from-frontend")}`); } } function checkRequiredParamaters(options) { ["credentials", "session"].indexOf(options.mode) < 0 && (0, utils_1.throwError)("the mode must be either credentials or session"); options.mode === "credentials" && !options.passkey && (0, utils_1.throwError)("you have to specify passkey for credentials mode"); options.mode === "credentials" && (options.session || options.xsrftoken || options.host) && (0, utils_1.throwError)("session, xsrftoken and host are invalid options more credentials mode"); options.mode === "session" && options.passkey && (0, utils_1.throwError)("you don't have to specify passkey for session mode"); options.mode === "session" && (!options.session || !options.xsrftoken || !options.host) && (0, utils_1.throwError)("you have to specify session, xsrftoken and host for session mode"); } //# sourceMappingURL=mc-proxy.js.map