import * as express from 'express' import * as http from 'http' import prependHttp from 'prepend-http' import WebpackDevServer, { Configuration, Middleware } from 'webpack-dev-server' import { createProxyMiddleware, RequestHandler } from 'http-proxy-middleware' import { parseEnv } from '../utils/parse' /** url 解析 */ const lax = (url: string, options?: { https: boolean }) => { if (typeof url !== 'string') { throw new TypeError(`Expected \`url\` to be of type \`string\`, got \`${typeof url}\` instead.`) } const finalUrl = prependHttp(url.replace(/^\/\//, ''), options) return new URL(finalUrl) } /** url 补全 */ const urlFix = (url: string, https: boolean = false) => { const finalUrl = prependHttp(url.replace(/^\/\//, ''), { https }) return finalUrl } /** 重写 cookie domain */ const rewriteCookieDomain = (headers: { [key: string]: any }, req: express.Request) => { const cookies = headers['set-cookie'] if (!cookies) return const referer = new URL(req.headers.referer ?? '') const domain = referer.hostname const replaceDomain = (cookie: string, domain: string) => { const [key, value] = cookie.trim().split('=') if (key === 'Domain') return `Domain=${domain}` else return `${key}=${value}` } // 修改cookie Path headers['set-cookie'] = cookies.map((cookie: any) => { return cookie .split(';') .map((item: string) => replaceDomain(item, domain)) .join('; ') }) } /** 设置项目启动后, 自动打开浏览器策略 */ const setAutoOpenType = () => { const { VUE_DEV_OPEN_CHROME } = parseEnv() switch (VUE_DEV_OPEN_CHROME) { case 2: return { app: [ 'Google Chrome', '--incognito', '-n /Applications/Google Chrome.app/ --args --disable-web-security --user-data-dir=/Users/admin/Appliactions' ] } case 1: return true default: return false } } const proxyPool: { [key: string]: RequestHandler } = {} const loadProxy = (target: string, from?: string) => { target = urlFix(target, lax(target.toString()).protocol === 'https:') if (proxyPool[target]) return proxyPool[target] let headers = {} if (from) { const finalUrl = urlFix(from ?? '', lax(target.toString()).protocol === 'https:') headers = { origin: finalUrl, host: finalUrl, referer: finalUrl } } // 动态创建 http 代理服务 const handler = createProxyMiddleware({ autoRewrite: true, secure: false, // 关闭安全校验(https校验) xfwd: false, // 关闭 http proxy 标记 changeOrigin: true, // 替换origin参数 pathRewrite: { '^/proxy': '' }, // 重写路径 logLevel: 'info', target: target.toString(), timeout: 5 * 60 * 1000, headers, onProxyReq(proxyReq: http.ClientRequest) { proxyReq.removeHeader('from') proxyReq.removeHeader('target') }, onProxyRes(proxyRes: http.IncomingMessage, req: any) { // 重写 domain rewriteCookieDomain(proxyRes.headers, req) } }) proxyPool[target] = handler return handler } const createHttpProxyMiddle = (app: express.Application) => { app.use('/proxy', async (req, res, next) => { const headers = req.headers const { from, target } = headers if (!target) return next() const handler = loadProxy(target?.toString(), from?.toString()) handler(req, res, next) }) } /** 创建 dev server 代理配置 */ export const createWebpackDevServer = (): Configuration => { const { VUE_DEV_SERVER_PORT } = parseEnv() return { allowedHosts: 'all', /** 启用 Gzip 压缩访问 */ compress: true, /** 启动时注入: 启动端口号 */ port: VUE_DEV_SERVER_PORT, /** 启动完成后, 自动打开chrome浏览器 */ // open: setAutoOpenType(), host: '0.0.0.0', /** 启动后, 创建动态代理 */ setupMiddlewares(middlewares: Array, devServer: WebpackDevServer): Array { if (devServer.app) createHttpProxyMiddle(devServer.app) return middlewares } } }