122 lines
3.7 KiB
JavaScript
122 lines
3.7 KiB
JavaScript
|
|
const { getDefaultConfig } = require('expo/metro-config');
|
||
|
|
const { createProxyMiddleware } = require('http-proxy-middleware');
|
||
|
|
const connect = require('connect');
|
||
|
|
|
||
|
|
const config = getDefaultConfig(__dirname);
|
||
|
|
|
||
|
|
// 安全地获取 Expo 的默认排除列表
|
||
|
|
const existingBlockList = [].concat(config.resolver.blockList || []);
|
||
|
|
|
||
|
|
config.resolver.blockList = [
|
||
|
|
...existingBlockList,
|
||
|
|
/.*\/\.expo\/.*/, // Expo 的缓存和构建产物目录
|
||
|
|
|
||
|
|
// 1. 原生代码 (Java/C++/Objective-C)
|
||
|
|
/.*\/react-native\/ReactAndroid\/.*/,
|
||
|
|
/.*\/react-native\/ReactCommon\/.*/,
|
||
|
|
|
||
|
|
// 2. 纯开发和调试工具
|
||
|
|
// 这些工具只在开发电脑上运行,不会被打包到应用中
|
||
|
|
/.*\/@typescript-eslint\/eslint-plugin\/.*/,
|
||
|
|
|
||
|
|
// 3. 构建时数据
|
||
|
|
// 这个数据库只在打包过程中使用,应用运行时不需要
|
||
|
|
/.*\/caniuse-lite\/data\/.*/,
|
||
|
|
|
||
|
|
// 4. 通用规则
|
||
|
|
/.*\/__tests__\/.*/, // 排除所有测试目录
|
||
|
|
/.*\.git\/.*/, // 排除 Git 目录
|
||
|
|
];
|
||
|
|
|
||
|
|
const BACKEND_TARGET = 'http://localhost:9091';
|
||
|
|
|
||
|
|
const apiProxy = createProxyMiddleware({
|
||
|
|
target: BACKEND_TARGET,
|
||
|
|
changeOrigin: true,
|
||
|
|
logLevel: 'debug',
|
||
|
|
proxyTimeout: 86400000,
|
||
|
|
onProxyReq: (proxyReq, req) => {
|
||
|
|
const accept = req.headers.accept || '';
|
||
|
|
if (accept.includes('text/event-stream')) {
|
||
|
|
proxyReq.setHeader('accept-encoding', 'identity');
|
||
|
|
}
|
||
|
|
},
|
||
|
|
onProxyRes: (proxyRes, req, res) => {
|
||
|
|
const contentType = proxyRes.headers['content-type'] || '';
|
||
|
|
if (contentType.includes('text/event-stream') || contentType.includes('application/stream')) {
|
||
|
|
res.setHeader('Cache-Control', 'no-cache');
|
||
|
|
res.setHeader('Connection', 'keep-alive');
|
||
|
|
res.setHeader('X-Accel-Buffering', 'no');
|
||
|
|
if (typeof res.flushHeaders === 'function') {
|
||
|
|
try { res.flushHeaders(); } catch {}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
const streamProxy = createProxyMiddleware({
|
||
|
|
target: BACKEND_TARGET,
|
||
|
|
changeOrigin: true,
|
||
|
|
logLevel: 'debug',
|
||
|
|
ws: true,
|
||
|
|
proxyTimeout: 86400000,
|
||
|
|
onProxyReq: (proxyReq, req) => {
|
||
|
|
const upgrade = req.headers.upgrade;
|
||
|
|
const accept = req.headers.accept || '';
|
||
|
|
if (upgrade && upgrade.toLowerCase() === 'websocket') {
|
||
|
|
proxyReq.setHeader('Connection', 'upgrade');
|
||
|
|
proxyReq.setHeader('Upgrade', req.headers.upgrade);
|
||
|
|
} else if (accept.includes('text/event-stream')) {
|
||
|
|
proxyReq.setHeader('accept-encoding', 'identity');
|
||
|
|
proxyReq.setHeader('Connection', 'keep-alive');
|
||
|
|
}
|
||
|
|
},
|
||
|
|
onProxyRes: (proxyRes, req, res) => {
|
||
|
|
const contentType = proxyRes.headers['content-type'] || '';
|
||
|
|
if (contentType.includes('text/event-stream') || contentType.includes('application/stream')) {
|
||
|
|
res.setHeader('Cache-Control', 'no-cache');
|
||
|
|
res.setHeader('Connection', 'keep-alive');
|
||
|
|
res.setHeader('X-Accel-Buffering', 'no');
|
||
|
|
if (typeof res.flushHeaders === 'function') {
|
||
|
|
try { res.flushHeaders(); } catch {}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
const shouldProxyToBackend = (url) => {
|
||
|
|
if (!url) return false;
|
||
|
|
if (/^\/api\/v\d+\//.test(url)) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
};
|
||
|
|
|
||
|
|
const isWebSocketRequest = (req) =>
|
||
|
|
!!(req.headers.upgrade && req.headers.upgrade.toLowerCase() === 'websocket');
|
||
|
|
const isSSERequest = (req) => {
|
||
|
|
const accept = req.headers.accept || '';
|
||
|
|
return accept.includes('text/event-stream');
|
||
|
|
};
|
||
|
|
|
||
|
|
config.server = {
|
||
|
|
...config.server,
|
||
|
|
enhanceMiddleware: (metroMiddleware, metroServer) => {
|
||
|
|
return connect()
|
||
|
|
.use((req, res, next) => {
|
||
|
|
if (shouldProxyToBackend(req.url)) {
|
||
|
|
console.log(`[Metro Proxy] Forwarding ${req.method} ${req.url}`);
|
||
|
|
|
||
|
|
if (isWebSocketRequest(req) || isSSERequest(req)) {
|
||
|
|
return streamProxy(req, res, next);
|
||
|
|
}
|
||
|
|
return apiProxy(req, res, next);
|
||
|
|
}
|
||
|
|
next();
|
||
|
|
})
|
||
|
|
.use(metroMiddleware);
|
||
|
|
},
|
||
|
|
};
|
||
|
|
|
||
|
|
module.exports = config;
|