94 lines
2.8 KiB
Python
94 lines
2.8 KiB
Python
from urllib.parse import quote
|
|
|
|
import httpx
|
|
from fastapi import APIRouter, Request
|
|
from fastapi.responses import Response, StreamingResponse
|
|
|
|
from config import settings
|
|
|
|
router = APIRouter(tags=["chatlog-proxy"])
|
|
|
|
HOP_BY_HOP_HEADERS = {
|
|
"connection",
|
|
"keep-alive",
|
|
"proxy-authenticate",
|
|
"proxy-authorization",
|
|
"te",
|
|
"trailers",
|
|
"transfer-encoding",
|
|
"upgrade",
|
|
}
|
|
|
|
|
|
def _copy_headers(headers: httpx.Headers) -> dict[str, str]:
|
|
copied: dict[str, str] = {}
|
|
for key, value in headers.items():
|
|
if key.lower() not in HOP_BY_HOP_HEADERS:
|
|
copied[key] = value
|
|
return copied
|
|
|
|
|
|
async def _proxy_chatlog(request: Request, upstream_path: str) -> Response:
|
|
query = request.url.query
|
|
target = f"{settings.chatlog_base_url}{upstream_path}"
|
|
if query:
|
|
target = f"{target}?{query}"
|
|
|
|
body = await request.body()
|
|
headers = {
|
|
key: value
|
|
for key, value in request.headers.items()
|
|
if key.lower() not in HOP_BY_HOP_HEADERS and key.lower() != "host"
|
|
}
|
|
|
|
async with httpx.AsyncClient(timeout=None, trust_env=False, follow_redirects=True) as client:
|
|
upstream = await client.request(
|
|
request.method,
|
|
target,
|
|
content=body if body else None,
|
|
headers=headers,
|
|
)
|
|
|
|
response_headers = _copy_headers(upstream.headers)
|
|
return StreamingResponse(
|
|
iter([upstream.content]),
|
|
status_code=upstream.status_code,
|
|
media_type=upstream.headers.get("content-type"),
|
|
headers=response_headers,
|
|
)
|
|
|
|
|
|
@router.api_route("/api/v1/{path:path}", methods=["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"])
|
|
async def proxy_api_v1(path: str, request: Request):
|
|
return await _proxy_chatlog(request, f"/api/v1/{path}")
|
|
|
|
|
|
async def _proxy_media(kind: str, path: str, request: Request):
|
|
safe_path = "/".join(quote(part, safe="") for part in path.split("/"))
|
|
return await _proxy_chatlog(request, f"/{kind}/{safe_path}")
|
|
|
|
|
|
@router.api_route("/image/{path:path}", methods=["GET", "POST", "OPTIONS"])
|
|
async def proxy_image(path: str, request: Request):
|
|
return await _proxy_media("image", path, request)
|
|
|
|
|
|
@router.api_route("/voice/{path:path}", methods=["GET", "POST", "OPTIONS"])
|
|
async def proxy_voice(path: str, request: Request):
|
|
return await _proxy_media("voice", path, request)
|
|
|
|
|
|
@router.api_route("/video/{path:path}", methods=["GET", "POST", "OPTIONS"])
|
|
async def proxy_video(path: str, request: Request):
|
|
return await _proxy_media("video", path, request)
|
|
|
|
|
|
@router.api_route("/file/{path:path}", methods=["GET", "POST", "OPTIONS"])
|
|
async def proxy_file(path: str, request: Request):
|
|
return await _proxy_media("file", path, request)
|
|
|
|
|
|
@router.api_route("/data/{path:path}", methods=["GET", "POST", "OPTIONS"])
|
|
async def proxy_data(path: str, request: Request):
|
|
return await _proxy_media("data", path, request)
|