|
1 | 1 | import base64
|
2 | 2 | from cryptography.fernet import Fernet, InvalidToken
|
3 | 3 | from fastapi import HTTPException, Request
|
4 |
| -from fastapi.responses import RedirectResponse |
| 4 | +from fastapi.responses import JSONResponse, RedirectResponse, StreamingResponse |
| 5 | +import httpx |
5 | 6 | import jwt
|
| 7 | +from starlette.types import Scope, Receive, Send |
6 | 8 |
|
7 | 9 | from config import BACKEND_ADMIN_UIDS, BACKEND_JWT_SECRET
|
8 | 10 |
|
@@ -91,3 +93,44 @@ def hash_decrypt(reviewer_id: str):
|
91 | 93 | return base64.b64encode(Fernet(secure_key).decrypt(reviewer_id)).decode()
|
92 | 94 | except InvalidToken:
|
93 | 95 | return None
|
| 96 | + |
| 97 | + |
| 98 | +class ProxyFiles: |
| 99 | + """ |
| 100 | + This is custom API that serves as a replacement for StaticFiles function |
| 101 | + when a production build is not available and the frontend is running on its |
| 102 | + own server. |
| 103 | + Essentially this implements a basic HTTP proxy so that the backend can serve |
| 104 | + the frontend even in dev mode, like it does in production mode. |
| 105 | + """ |
| 106 | + |
| 107 | + def __init__(self, port: int, host: str = "127.0.0.1", http_scheme: str = "http"): |
| 108 | + self.http_scheme = http_scheme |
| 109 | + self.host = host |
| 110 | + self.port = port |
| 111 | + |
| 112 | + async def handle_http(self, scope: Scope, receive: Receive, send: Send): |
| 113 | + request = Request(scope, receive, send) |
| 114 | + async with httpx.AsyncClient() as client: |
| 115 | + request_response = await client.request( |
| 116 | + request.method.lower(), |
| 117 | + f"{self.http_scheme}://{self.host}:{self.port}{request.url.path}", |
| 118 | + headers=dict(request.headers), |
| 119 | + params=request.query_params, |
| 120 | + content=await request.body(), |
| 121 | + ) |
| 122 | + return StreamingResponse( |
| 123 | + request_response.aiter_bytes(), |
| 124 | + status_code=request_response.status_code, |
| 125 | + headers=dict(request_response.headers), |
| 126 | + ) |
| 127 | + |
| 128 | + async def __call__(self, scope: Scope, receive: Receive, send: Send): |
| 129 | + if scope["type"] == "http": |
| 130 | + response = await self.handle_http(scope, receive, send) |
| 131 | + else: |
| 132 | + response = JSONResponse( |
| 133 | + {"error": f"{scope['type']} connections are not supported"}, |
| 134 | + status_code=400, |
| 135 | + ) |
| 136 | + await response(scope, receive, send) |
0 commit comments