Files
v2/dev_proxy.py
2026-01-26 21:41:28 +08:00

126 lines
4.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 开发服务器用于在不构建vite应用的情况下仍然像生产环境中那样使用相对路径调用api兼容Vite HMR
# 如果出现Bug请注意先检查是不是proxy导致的问题再检查是不是代码写错了
import asyncio
import aiohttp
from aiohttp import web
import logging
# 全局变量配置
VITE_ADD = "http://localhost:5173"
PY_ADD = "http://127.0.0.1:5000"
PORT = 8080
MAX_BODY_MB = 50
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("dev_proxy")
async def proxy_handler(request):
path = request.path
query = request.query_string
method = request.method
headers = dict(request.headers)
# 判定转发目标
if path.startswith('/api/'):
target_url = f"{PY_ADD}{path}"
else:
target_url = f"{VITE_ADD}{path}"
if query:
target_url += f"?{query}"
# 处理 WebSocket 升级请求 (Vite HMR)
if request.headers.get('Upgrade', '').lower() == 'websocket':
return await ws_proxy_handler(request, target_url)
try:
async with aiohttp.ClientSession() as session:
# 读取请求体
data = await request.read()
async with session.request(
method=method,
url=target_url,
headers=headers,
data=data,
allow_redirects=False
) as resp:
# 构建响应头
resp_headers = dict(resp.headers)
# 读取响应体
body = await resp.read()
return web.Response(
body=body,
status=resp.status,
headers=resp_headers
)
except Exception as e:
logger.error(f"Error proxying {method} {path}: {e}")
return web.Response(text=str(e), status=502)
async def ws_proxy_handler(request, target_url):
"""处理 WebSocket 转发,主要用于 Vite HMR"""
# 将 http:// 转换为 ws://
ws_target_url = target_url.replace('http://', 'ws://').replace('https://', 'wss://')
# 获取并转发 WebSocket 子协议
protocol = request.headers.get('Sec-WebSocket-Protocol', '')
protocols = [p.strip() for p in protocol.split(',')] if protocol else []
ws_server = web.WebSocketResponse(protocols=protocols)
await ws_server.prepare(request)
logger.info(f"WebSocket connection upgraded: {request.path} (Protocols: {protocols})")
async with aiohttp.ClientSession() as session:
async with session.ws_connect(ws_target_url, protocols=protocols) as ws_client:
async def forward(src, dst):
async for msg in src:
if msg.type == aiohttp.WSMsgType.TEXT:
await dst.send_str(msg.data)
elif msg.type == aiohttp.WSMsgType.BINARY:
await dst.send_bytes(msg.data)
elif msg.type == aiohttp.WSMsgType.CLOSE:
await dst.close()
break
elif msg.type == aiohttp.WSMsgType.ERROR:
break
# 双向转发
await asyncio.gather(
forward(ws_server, ws_client),
forward(ws_client, ws_server)
)
return ws_server
async def main():
app = web.Application(client_max_size=MAX_BODY_MB * 1024 * 1024)
# 捕获所有路径
app.router.add_route('*', '/{path:.*}', proxy_handler)
runner = web.AppRunner(app)
await runner.setup()
site = web.TCPSite(runner, '127.0.0.1', PORT)
logger.info(f"Dev Proxy started at http://127.0.0.1:{PORT}")
logger.info(f"Routing /api/* to {PY_ADD}")
logger.info(f"Routing others to {VITE_ADD}")
await site.start()
# 保持运行
try:
while True:
await asyncio.sleep(3600)
except KeyboardInterrupt:
pass
finally:
await runner.cleanup()
if __name__ == "__main__":
asyncio.run(main())