complete proxy with instructions

This commit is contained in:
Chris Anderson 2024-12-03 21:43:29 +00:00
parent e489a66ae2
commit e3055b121f
4 changed files with 160 additions and 46 deletions

View File

@ -1,23 +1,112 @@
const { assert } = require("console");
const express = require("express"); const express = require("express");
const { createProxyMiddleware } = require("http-proxy-middleware"); const { createProxyMiddleware } = require("http-proxy-middleware");
const { inspect } = require("util");
const conf = {}; const conf = {};
conf.PORT = process.env.EXPLORER_PORT || 1234; conf.PORT = process.env.EXPLORER_PORT || 1234;
conf.AZURE_TENANT_ID = process.env.AZURE_TENANT_ID || "72f988bf-86f1-41af-91ab-2d7cd011db47"; conf.LOG_LEVEL = process.env.LOG_LEVEL || "info";
conf.EMULATOR_ENDPOINT = process.env.EMULATOR_ENDPOINT || "https://localhost:8081/"; conf.EMULATOR_ENDPOINT = process.env.EMULATOR_ENDPOINT || "http://127.0.0.1:8081";
conf.ENDPOINT_DISCOVERY_ENABLED = (process.env.ENDPOINT_DISCOVERY_ENABLED || "false").toLowerCase() === "true";
const LOG_NUM = levelToNumber(conf.LOG_LEVEL);
function log(level, msg, color) {
if (levelToNumber(level) >= LOG_NUM) {
console.log(`${colorToCode(color)}[${level || "debug"}]${msg}\x1b[0m`);
}
}
function debug(msg, color) {
log("debug", msg, color);
}
function info(msg, color) {
log("info", msg, color);
}
function warn(msg, color) {
log("warn", msg, color || "yellow");
}
function err(msg, color) {
log("error", msg, color || "red");
}
function levelToNumber(level) {
switch (level) {
case "debug":
return 0;
case "info":
return 1;
case "warn":
return 2;
case "error":
return 3;
default:
return 0;
}
}
function colorToCode(color) {
switch (color) {
case "red":
return "\x1b[31m";
case "green":
return "\x1b[32m";
case "blue":
return "\x1b[34m";
case "yellow":
return "\x1b[33m";
default:
return "\x1b[0m";
}
}
function statusToColor(status) {
if (status < 300) {
return "green";
} else if (status < 400) {
return "blue";
} else {
return "red";
}
}
const testEndpoint = () => {
fetch(conf.EMULATOR_ENDPOINT)
.then(async (res) => {
const body = await res.json();
info("[EMU] Emulator is accessible");
})
.catch((err) => {
err("[EMU] Emulator is not accessible");
err(`[EMU] ${inspect(err)}`);
});
};
testEndpoint();
const app = express(); const app = express();
app.all("*", (req, res, next) => { app.use((err, req, res, next) => {
const start = new Date(); err(`[APP] ${inspect(err)}`);
res.status(500).json({ error: err.message });
});
app.use((req, res, next) => {
req.startTime = new Date();
req.requestId = Math.random().toString(36).substring(7);
res.append("Access-Control-Allow-Origin", "*"); res.append("Access-Control-Allow-Origin", "*");
res.append("Access-Control-Allow-Credentials", "true"); res.append("Access-Control-Allow-Credentials", "true");
res.append("Access-Control-Max-Age", "3600"); res.append("Access-Control-Max-Age", "3600");
res.append("Access-Control-Allow-Headers", "*"); res.append("Access-Control-Allow-Headers", "*");
res.append("Access-Control-Allow-Methods", "*"); res.append("Access-Control-Allow-Methods", "*");
next(); next();
const ms = new Date() - start; const ms = new Date() - req.startTime;
console.log(`[${req.method}] ${req.url} -> ${res.statusCode} [${ms}ms]`); (res.statusCode < 400 ? debug : err)(
`[APP][${req.requestId}][${req.method}][${res.statusCode}][${ms}ms] ${req.url}`,
statusToColor(res.statusCode),
);
}); });
app.get("/_ready", (_, res) => { app.get("/_ready", (_, res) => {
@ -28,60 +117,44 @@ app.get("/_ready", (_, res) => {
} }
}); });
app.get("config.json", (_, res) => { const appConf = {
res.json({ EMULATOR_ENDPOINT: conf.EMULATOR_ENDPOINT }); PROXY_PATH: "/proxy",
}); EMULATOR_ENDPOINT: conf.EMULATOR_ENDPOINT,
const bypass = (req, res) => {
if (req.method === "OPTIONS") {
res.statusCode = 200;
res.send();
}
}; };
app.get("/config.json", (_, res) => {
const apiProxy = createProxyMiddleware({ res.status(200).json(appConf).end();
target: "https://main.documentdb.ext.azure.com",
changeOrigin: true,
logLevel: "debug",
}); });
app.use("/api", bypass, apiProxy);
const proxyProxy = createProxyMiddleware({ const proxyProxy = createProxyMiddleware({
target: "https://main.documentdb.ext.azure.com", target: "https://cdb-ms-mpac-pbe.cosmos.azure.com",
changeOrigin: true, changeOrigin: true,
secure: false, secure: false,
logLevel: "debug", logLevel: conf.LOG_LEVEL,
pathRewrite: { "^/proxy": "" }, pathRewrite: { "^/proxy": "" },
router: (req) => { router: (req) => {
if (conf.ENDPOINT_DISCOVERY_ENABLED) {
let newTarget = req.headers["x-ms-proxy-target"]; let newTarget = req.headers["x-ms-proxy-target"];
return newTarget; return newTarget;
} else {
return conf.EMULATOR_ENDPOINT;
}
}, },
}); });
app.use("/proxy", proxyProxy); app.use("/proxy", proxyProxy);
const explorerProxy = createProxyMiddleware({ const unsupported = (req, res) => {
target: conf.EMULATOR_ENDPOINT, res.status(404).send("Unexpected operation. Please create issue.");
changeOrigin: true, };
secure: false,
logLevel: "debug",
});
app.use("/_explorer", explorerProxy); // TODO: andersonc - I don't believe these are needed for emulator, should confirm and remove.
app.use("/explorerProxy", explorerProxy); app.use("/api", unsupported);
app.use("/_explorer", unsupported);
const tenantProxy = createProxyMiddleware({ app.use("/explorerProxy", unsupported);
target: "https://login.microsoftonline.com/", app.use(`/${conf.AZURE_TENANT_ID}`, unsupported);
changeOrigin: true,
secure: false,
logLevel: "debug",
});
app.use(`/${conf.AZURE_TENANT_ID}`, tenantProxy);
app.use(express.static("dist")); app.use(express.static("dist"));
console.log(`Expecting emulator on [${conf.EMULATOR_ENDPOINT}]`); info(`[EMU] Expecting emulator on [${conf.EMULATOR_ENDPOINT}]`);
console.log(`Listening on [${conf.PORT}]`); info(`[APP] Listening on [${conf.PORT}]`);
app.listen(conf.PORT); app.listen(conf.PORT);

View File

@ -0,0 +1 @@
require('./index.js');

View File

@ -3,7 +3,8 @@
"version": "1.0.0", "version": "1.0.0",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"start": "node index.js" "start": "node main.js",
"pack": "cd ../.. && npm run build:proxy && cd utils/local-proxy"
}, },
"keywords": [], "keywords": [],
"author": "", "author": "",

View File

@ -0,0 +1,39 @@
# local-proxy
Lightweight host for Cosmos Explorer
## Quickstart
1. Pre-req - install packages for root project (`cd ../.. && npm ci && cd utils/local-proxy`)
2. Install - install packages for local-proxy (`npm ci`)
3. Pack - `npm run pack` - builds and packs Cosmos Explorer and copies files into project
4. Start - `npm start` - starts the proxy
```bash
cd ../..
npm ci
cd utils/local-proxy
npm ci
npm run pack
npm start
```
## Config
All config is current set via environment variables
| Name | Options (Default) | Description |
| ---------------------------- | ----------------------------------------- | ------------------------------------------------------------ |
| `PORT` | number (`1234`) | The port on which the proxy runs. |
| `LOG_LEVEL` | `debug`, `info`, `warn`, `error` (`info`) | The logging level for the proxy. |
| `EMULATOR_ENDPOINT` | string (`http://localhost:8081`) | The endpoint for the emulator which will be proxied. |
| `ENDPOINT_DISCOVERY_ENABLED` | boolean (`false`) | Determine whether the proxy will rewrite the endpoint or not |
## Dependenies
Node.js v20+
npm (optional)
## Deployment
Copy the entire local-proxy directory to wherever you'd like. If you have npm, you can use `npm start`, else `node main.js`