env-manage-plugin
Version:
A dev env plugin that integrates an Express server with request proxying capabilities.
203 lines (202 loc) • 7.08 kB
JavaScript
import express from "express";
import { createProxyMiddleware } from "http-proxy-middleware";
import setCookieParser from "set-cookie-parser";
import * as libCookie from "cookie";
import { getConfig } from "../utils/ResolveConfig.js";
import { devServerLogger } from "../utils/logger.js";
class PreProxyServer {
/**
* 判断该端口是否已经有服务存在
* @param port
* @returns
*/
static getAppInsByPort(port) {
return this.appMap[port];
}
static async create(envId, envRepo, devServerRepo) {
if (this.appMap[envId]) {
devServerLogger.info(`环境 ${envId} 已经启动`);
return null;
}
const preProxyServer = new PreProxyServer(envId, envRepo, devServerRepo);
await preProxyServer.startServer();
PreProxyServer.appMap[envId] = preProxyServer;
return preProxyServer;
}
constructor(envId, envRepo, devServerRepo, app = express()) {
this.envId = envId;
this.envRepo = envRepo;
this.devServerRepo = devServerRepo;
this.app = app;
app.use(this.createPreProxyMiddleware());
}
/**
*
* @returns 获取绑定的环境信息
*/
getEnvItem() {
const envItem = this.envRepo.findOneById(this.envId);
return envItem;
}
/**
* 获取绑定的服务器详情
*/
getDevServer() { }
/**
* 当前 代理的 cookie 后缀
*/
get cookieSuffix() {
const envItem = this.getEnvItem();
return `-${envItem?.port}-${PreProxyServer.configCookieSuffix}`;
}
/**
* 配置的 cookie 后缀
*/
static get configCookieSuffix() {
return `${getConfig().cookieSuffix}`;
}
/**
* 生成代理中间件
* @returns
*/
createPreProxyMiddleware() {
// 前置转发:将请求转发到 开发服务器
return createProxyMiddleware({
ws: true,
changeOrigin: true,
router: () => {
// 查询环境信息
const envItem = this.getEnvItem();
// 根据环境信息查询绑定的 devServer 地址
const devServerConfig = this.devServerRepo.findOneById({
id: envItem?.devServerId ?? "",
});
// 转发到 devServer
return `${devServerConfig?.devServerUrl}`;
},
on: {
proxyReq: (proxyReq, req) => {
const envItem = this.getEnvItem();
const target = `${envItem?.apiBaseUrl}`;
proxyReq.setHeader("x-api-server", `${target}`);
this._rewrieCookieOnProxyReq(proxyReq, req);
},
proxyRes: (proxyRes) => {
this._rewriteSetCookieOnProxyRes(proxyRes);
},
proxyReqWs: (proxyReq) => {
const envItem = this.getEnvItem();
const target = `${envItem?.apiBaseUrl}`;
proxyReq.setHeader("x-api-server", `${target}`);
},
},
});
}
/**
* 代理的时候如果收到了 setcookie 请求
* 给 每一个 setcookie 追加保存一个 代理cookie
* @param proxyRes
*/
_rewriteSetCookieOnProxyRes(proxyRes) {
const envItem = this.getEnvItem();
const setCookie = proxyRes.headers["set-cookie"];
if (envItem && setCookie) {
const setCookies = setCookieParser.parse(setCookie);
const proxyCookie = setCookies.map((item) => {
const cookie = {
...item,
name: `${item.name}${this.cookieSuffix}`,
};
return libCookie.serialize(cookie.name, cookie.value, cookie);
});
setCookie.push(...proxyCookie);
}
}
/**
* 代理 转发的时候,将对应端口的cookie 重写回去
*/
_rewrieCookieOnProxyReq(proxyReq, req) {
const envItem = this.getEnvItem();
if (envItem && req.headers.cookie) {
const cookie = libCookie.parse(req.headers.cookie || "");
const newCookies = [];
Object.keys(cookie).forEach((item) => {
if (item.endsWith(PreProxyServer.configCookieSuffix)) {
return;
}
let cookieName = `${item}${this.cookieSuffix}`;
if (!cookie[cookieName]) {
cookieName = item;
}
newCookies.push(libCookie.serialize(item, cookie[cookieName] || ""));
});
proxyReq.setHeader("cookie", newCookies.join(";"));
}
}
/**
* 启动服务
* @param envItem
* @returns
*/
async startServer() {
const { port } = this.getEnvItem();
if (PreProxyServer.appMap[this.envId]) {
devServerLogger.info(`环境 ${this.envId} 已经启动`);
return;
}
this.server = await new Promise((resolve, reject) => {
const server = this.app.listen(port, () => {
devServerLogger.info(`Server is running on http://localhost:${port}`);
// 更新状态
resolve(server);
});
server.on("error", (err) => {
console.error(`端口 ${port} 启动失败:`, err.message);
reject(err);
});
});
// 保存所有活动的 socket 连接
this.sockets = new Set();
this.server.on("connection", (socket) => {
this.sockets.add(socket); // 保存 socket
socket.setTimeout(300000); // 设置超时时间为 5 分钟
socket.on("timeout", () => {
socket.destroy();
this.sockets.delete(socket);
});
// 监听 socket 关闭事件
socket.on("close", () => {
this.sockets.delete(socket); // 从集合中移除已关闭的 socket
});
});
}
static stopServer(id) {
return new Promise((resolve) => {
if (!this.getAppInsByPort(id)) {
devServerLogger.info(`端口 【${id}】 未启动,无需停止!`);
resolve(1);
return;
}
for (const socket of this.appMap[id].sockets) {
socket.destroy();
}
this.appMap[id].server.close((err) => {
// 停止服务更新状态
if (err) {
console.error("服务器关闭失败:", err);
resolve(0);
}
else {
delete this.appMap[id];
devServerLogger.info({ id }, `Server on port ${id} 已关闭`);
resolve(1);
}
});
});
}
}
/**
* 保存启动的环境实例
*/
PreProxyServer.appMap = {};
export default PreProxyServer;