Proxy backend requests on '/' to the frontend
If the optional extra `proxy` is installed, then the backend can handle all requests by proxy unrecognized routes to the frontend nextjs server.
This commit is contained in:
parent
1e9ccecea0
commit
7512afa949
@ -63,6 +63,7 @@ setuptools = ">=69.1.1,<70.0"
|
||||
httpx = ">=0.25.1,<1.0"
|
||||
twine = ">=4.0.0,<6.0"
|
||||
tomlkit = ">=0.12.4,<1.0"
|
||||
asgiproxy = { version = "==0.1.1", optional = true }
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
pytest = ">=7.1.2,<8.0"
|
||||
@ -90,6 +91,9 @@ pytest-benchmark = ">=4.0.0,<5.0"
|
||||
[tool.poetry.scripts]
|
||||
reflex = "reflex.reflex:cli"
|
||||
|
||||
[tool.poetry.extras]
|
||||
proxy = ["asgiproxy"]
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.5.1"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
@ -13,6 +13,7 @@ import os
|
||||
import platform
|
||||
from typing import (
|
||||
Any,
|
||||
AsyncGenerator,
|
||||
AsyncIterator,
|
||||
Callable,
|
||||
Coroutine,
|
||||
@ -94,6 +95,23 @@ def default_overlay_component() -> Component:
|
||||
return Fragment.create(connection_pulser(), connection_modal())
|
||||
|
||||
|
||||
@contextlib.asynccontextmanager
|
||||
async def lifespan(api: FastAPI) -> AsyncGenerator[None, None]:
|
||||
"""Context manager to handle the lifespan of the app.
|
||||
|
||||
Args:
|
||||
api: The FastAPI instance.
|
||||
|
||||
Yields:
|
||||
None
|
||||
"""
|
||||
# try to set up proxying if its enabled
|
||||
from .proxy import proxy_middleware
|
||||
|
||||
async with proxy_middleware(api):
|
||||
yield
|
||||
|
||||
|
||||
class OverlayFragment(Fragment):
|
||||
"""Alias for Fragment, used to wrap the overlay_component."""
|
||||
|
||||
@ -203,7 +221,7 @@ class App(Base):
|
||||
self.middleware.append(HydrateMiddleware())
|
||||
|
||||
# Set up the API.
|
||||
self.api = FastAPI()
|
||||
self.api = FastAPI(lifespan=lifespan)
|
||||
self._add_cors()
|
||||
self._add_default_endpoints()
|
||||
|
||||
|
75
reflex/proxy.py
Normal file
75
reflex/proxy.py
Normal file
@ -0,0 +1,75 @@
|
||||
"""Handle proxying frontend requests from the backend server."""
|
||||
from __future__ import annotations
|
||||
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import AsyncGenerator
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from fastapi import FastAPI
|
||||
from starlette.types import ASGIApp
|
||||
|
||||
from .config import get_config
|
||||
from .utils import console
|
||||
|
||||
try:
|
||||
from asgiproxy.config import BaseURLProxyConfigMixin, ProxyConfig
|
||||
from asgiproxy.context import ProxyContext
|
||||
from asgiproxy.simple_proxy import make_simple_proxy_app
|
||||
except ImportError:
|
||||
|
||||
@asynccontextmanager
|
||||
async def proxy_middleware(*args, **kwargs) -> AsyncGenerator[None, None]:
|
||||
"""A no-op proxy middleware for when asgiproxy is not installed.
|
||||
|
||||
Args:
|
||||
*args: The positional arguments.
|
||||
**kwargs: The keyword arguments.
|
||||
|
||||
Yields:
|
||||
None
|
||||
"""
|
||||
yield
|
||||
else:
|
||||
|
||||
def _get_proxy_app_with_context(frontend_host: str) -> tuple[ProxyContext, ASGIApp]:
|
||||
"""Get the proxy app with the given frontend host.
|
||||
|
||||
Args:
|
||||
frontend_host: The frontend host to proxy requests to.
|
||||
|
||||
Returns:
|
||||
The proxy context and app.
|
||||
"""
|
||||
|
||||
class LocalProxyConfig(BaseURLProxyConfigMixin, ProxyConfig):
|
||||
upstream_base_url = frontend_host
|
||||
rewrite_host_header = urlparse(upstream_base_url).netloc
|
||||
|
||||
proxy_context = ProxyContext(LocalProxyConfig())
|
||||
proxy_app = make_simple_proxy_app(proxy_context)
|
||||
return proxy_context, proxy_app
|
||||
|
||||
@asynccontextmanager
|
||||
async def proxy_middleware( # pyright: ignore[reportGeneralTypeIssues]
|
||||
api: FastAPI,
|
||||
) -> AsyncGenerator[None, None]:
|
||||
"""A middleware to proxy requests to the separate frontend server.
|
||||
|
||||
The proxy is installed on the / endpoint of the FastAPI instance.
|
||||
|
||||
Args:
|
||||
api: The FastAPI instance.
|
||||
|
||||
Yields:
|
||||
None
|
||||
"""
|
||||
config = get_config()
|
||||
backend_port = config.backend_port
|
||||
frontend_host = f"http://localhost:{config.frontend_port}"
|
||||
proxy_context, proxy_app = _get_proxy_app_with_context(frontend_host)
|
||||
api.mount("/", proxy_app)
|
||||
console.debug(
|
||||
f"Proxying '/' requests on port {backend_port} to {frontend_host}"
|
||||
)
|
||||
async with proxy_context:
|
||||
yield
|
Loading…
Reference in New Issue
Block a user