Files
height_manager/client/metro.config.js

122 lines
3.7 KiB
JavaScript
Raw Normal View History

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;