connected zen

This commit is contained in:
2026-02-03 00:36:22 +01:00
parent 9281c0e02a
commit 05aa70c4af
10 changed files with 582 additions and 4 deletions

View File

@@ -0,0 +1,241 @@
import os
import re
import time
from urllib.parse import urlparse
from fastapi import APIRouter, HTTPException, Request, Response
import httpx
from session_manager import session_manager
from http_pool import make_http_request
from logging_config import (
RequestContext,
log_request,
log_session_operation,
log_security_event,
)
router = APIRouter(tags=["proxy"])
ALL_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"]
def get_session_from_cookie(request: Request) -> str:
session_id = request.cookies.get("lovdata_session")
if not session_id:
raise HTTPException(
status_code=400,
detail="No active session - please access via /session/{id}/ first",
)
return session_id
@router.api_route("/global/{path:path}", methods=ALL_METHODS)
async def proxy_global_to_session(request: Request, path: str):
session_id = get_session_from_cookie(request)
return await proxy_to_session(request, session_id, f"global/{path}")
@router.api_route("/assets/{path:path}", methods=ALL_METHODS)
async def proxy_assets_to_session(request: Request, path: str):
session_id = get_session_from_cookie(request)
return await proxy_to_session(request, session_id, f"assets/{path}")
@router.api_route("/provider/{path:path}", methods=ALL_METHODS)
async def proxy_provider_path_to_session(request: Request, path: str):
session_id = get_session_from_cookie(request)
return await proxy_to_session(request, session_id, f"provider/{path}")
@router.api_route("/provider", methods=ALL_METHODS)
async def proxy_provider_to_session(request: Request):
session_id = get_session_from_cookie(request)
return await proxy_to_session(request, session_id, "provider")
@router.api_route("/project", methods=ALL_METHODS)
async def proxy_project_to_session(request: Request):
session_id = get_session_from_cookie(request)
return await proxy_to_session(request, session_id, "project")
@router.api_route("/path", methods=ALL_METHODS)
async def proxy_path_to_session(request: Request):
session_id = get_session_from_cookie(request)
return await proxy_to_session(request, session_id, "path")
@router.api_route("/find/{path:path}", methods=ALL_METHODS)
async def proxy_find_to_session(request: Request, path: str):
session_id = get_session_from_cookie(request)
return await proxy_to_session(request, session_id, f"find/{path}")
@router.api_route("/file", methods=ALL_METHODS)
async def proxy_file_to_session(request: Request):
session_id = get_session_from_cookie(request)
return await proxy_to_session(request, session_id, "file")
@router.api_route("/file/{path:path}", methods=ALL_METHODS)
async def proxy_file_path_to_session(request: Request, path: str):
session_id = get_session_from_cookie(request)
return await proxy_to_session(request, session_id, f"file/{path}")
@router.api_route("/session/{session_id}/{path:path}", methods=ALL_METHODS)
async def proxy_to_session(request: Request, session_id: str, path: str):
start_time = time.time()
with RequestContext():
log_request(
request.method,
f"/session/{session_id}/{path}",
200,
0,
operation="proxy_start",
session_id=session_id,
)
session = await session_manager.get_session(session_id)
if not session or session.status != "running":
duration_ms = (time.time() - start_time) * 1000
log_request(
request.method,
f"/session/{session_id}/{path}",
404,
duration_ms,
session_id=session_id,
error="Session not found or not running",
)
raise HTTPException(
status_code=404, detail="Session not found or not running"
)
docker_host = os.getenv("DOCKER_HOST", "http://docker-daemon:2375")
parsed = urlparse(docker_host)
container_host = parsed.hostname or "docker-daemon"
container_url = f"http://{container_host}:{session.port}"
url = f"{container_url}/{path}"
if request.url.query:
url += f"?{request.url.query}"
body = await request.body()
headers = dict(request.headers)
headers.pop("host", None)
if session.auth_token:
headers["Authorization"] = f"Bearer {session.auth_token}"
headers["X-Session-Token"] = session.auth_token
headers["X-Session-ID"] = session.session_id
try:
log_session_operation(
session_id, "proxy_request", method=request.method, path=path
)
response = await make_http_request(
method=request.method,
url=url,
headers=headers,
content=body,
)
duration_ms = (time.time() - start_time) * 1000
log_request(
request.method,
f"/session/{session_id}/{path}",
response.status_code,
duration_ms,
session_id=session_id,
operation="proxy_complete",
)
log_security_event(
"proxy_access",
"info",
session_id=session_id,
method=request.method,
path=path,
status_code=response.status_code,
)
content = response.content
response_headers = dict(response.headers)
content_type = response.headers.get("content-type", "")
if "text/html" in content_type:
try:
html = content.decode("utf-8")
session_prefix = f"/session/{session_id}"
html = re.sub(r'src="/', f'src="{session_prefix}/', html)
html = re.sub(r'href="/', f'href="{session_prefix}/', html)
html = re.sub(r'content="/', f'content="{session_prefix}/', html)
content = html.encode("utf-8")
response_headers["content-length"] = str(len(content))
except UnicodeDecodeError:
pass
resp = Response(
content=content,
status_code=response.status_code,
headers=response_headers,
)
resp.set_cookie(
key="lovdata_session",
value=session_id,
httponly=True,
samesite="lax",
max_age=86400,
)
return resp
except httpx.TimeoutException as e:
duration_ms = (time.time() - start_time) * 1000
log_request(
request.method,
f"/session/{session_id}/{path}",
504,
duration_ms,
session_id=session_id,
error="timeout",
)
log_security_event(
"proxy_timeout",
"warning",
session_id=session_id,
method=request.method,
path=path,
error=str(e),
)
raise HTTPException(
status_code=504, detail="Request to session container timed out"
)
except httpx.RequestError as e:
duration_ms = (time.time() - start_time) * 1000
log_request(
request.method,
f"/session/{session_id}/{path}",
502,
duration_ms,
session_id=session_id,
error=str(e),
)
log_security_event(
"proxy_connection_error",
"error",
session_id=session_id,
method=request.method,
path=path,
error=str(e),
)
raise HTTPException(
status_code=502,
detail=f"Failed to connect to session container: {str(e)}",
)