Skip to content

Commit d05fa22

Browse files
authored
Merge pull request #78 from TukaTek/feature/middleware/TUK-112
Feature/middleware/tuk 112
2 parents cca9d9a + ce7620e commit d05fa22

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+5536
-251
lines changed

backend/alembic/versions/0a98909f2757_enable_encrypted_fields.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
Create Date: 2024-05-05 19:30:34.317972
66
77
"""
8-
98
from alembic import op
109
import sqlalchemy as sa
1110
from sqlalchemy.sql import table

backend/middleware/Dockerfile

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Use an official Python runtime as a parent image
2+
FROM python:3.11-slim
3+
4+
# Set working directory inside the container
5+
WORKDIR /app
6+
7+
# Copy requirements file and install dependencies
8+
RUN pip install fastapi uvicorn httpx
9+
10+
# Copy the application code
11+
COPY . /app
12+
13+
# Expose the port the app runs on
14+
EXPOSE 8000
15+
16+
# Start the FastAPI app with uvicorn
17+
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

backend/middleware/main.py

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# import logging
2+
# from fastapi import FastAPI, Request, Response, HTTPException
3+
# from fastapi.responses import RedirectResponse, JSONResponse
4+
# import os
5+
6+
# # Configure logging
7+
# logging.basicConfig(level=logging.INFO)
8+
# logger = logging.getLogger(__name__)
9+
10+
# app = FastAPI()
11+
12+
# MIDDLEWARE_BACKEND_URL = os.getenv("MIDDLEWARE_BACKEND_URL") # e.g. http://middleware_endpoint_handler:5000
13+
# API_SERVER_URL = os.getenv("API_SERVER_URL") # e.g. http://api_server_gpt_5:8080
14+
15+
# @app.get("/health")
16+
# async def health_check():
17+
# return {"status": "healthy", "service": "middleware_route_filter"}
18+
19+
# @app.middleware("http")
20+
# async def route_redirect(request: Request, call_next):
21+
# try:
22+
# path = request.url.path
23+
# query = request.url.query
24+
25+
# # Block docs endpoints to avoid exposing backend docs
26+
# if path in ["/docs", "/openapi.json", "/api/docs", "/api/openapi.json"]:
27+
# raise HTTPException(status_code=404, detail="Not found")
28+
29+
# # Allow health endpoint locally
30+
# if path == "/health":
31+
# return await call_next(request)
32+
33+
# logger.info(f"Raw incoming path: {path}")
34+
35+
36+
# # Decide target backend URL based on path
37+
# if path.startswith("/middleware"):
38+
# target_url = f"{MIDDLEWARE_BACKEND_URL}{path[len('/middleware'):] or '/'}"
39+
# elif path.startswith("/api"):
40+
# target_url = f"{API_SERVER_URL}{path}"
41+
# else:
42+
# target_url = f"{API_SERVER_URL}{path}"
43+
44+
# # Append query parameters if present
45+
# if query:
46+
# target_url = f"{target_url}?{query}"
47+
48+
# logger.info(f"Redirecting {path} to {target_url}")
49+
50+
# # Redirect client to backend server URL
51+
# return RedirectResponse(url=target_url)
52+
53+
# except HTTPException as http_exc:
54+
# logger.warning(f"HTTPException: {http_exc.detail} for path {request.url.path}")
55+
# return JSONResponse(status_code=http_exc.status_code, content={"detail": http_exc.detail})
56+
57+
# except Exception as exc:
58+
# logger.error(f"Unhandled error: {exc}")
59+
# return JSONResponse(status_code=500, content={"detail": "Internal server error"})
60+
61+
import os
62+
import logging
63+
from fastapi import FastAPI, Request, HTTPException
64+
from fastapi.responses import JSONResponse, Response
65+
import httpx
66+
67+
# Configure logging
68+
logging.basicConfig(level=logging.INFO)
69+
logger = logging.getLogger(__name__)
70+
71+
app = FastAPI()
72+
73+
# Set these as ENV vars in your docker-compose for the middleware container!
74+
MIDDLEWARE_BACKEND_URL = os.getenv("MIDDLEWARE_BACKEND_URL", "http://middleware_endpoint_handler:5000")
75+
API_SERVER_URL = os.getenv("API_SERVER_URL", "http://api_server_gpt_5:8080")
76+
77+
@app.get("/health")
78+
async def health_check():
79+
return {"status": "healthy", "service": "middleware_route_filter"}
80+
81+
@app.middleware("http")
82+
async def proxy_middleware(request: Request, call_next):
83+
try:
84+
path = request.url.path
85+
query = request.url.query
86+
87+
# Block docs endpoints to avoid exposing backend docs
88+
if path in ["/docs", "/openapi.json", "/api/docs", "/api/openapi.json"]:
89+
raise HTTPException(status_code=404, detail="Not found")
90+
91+
if path == "/health":
92+
return await call_next(request)
93+
94+
logger.info(f"Raw incoming path: {path}")
95+
96+
# Decide target backend URL based on path
97+
if path.startswith("/middleware"):
98+
target_url = f"{MIDDLEWARE_BACKEND_URL}{path[len('/middleware'):] or '/'}"
99+
else:
100+
target_url = f"{API_SERVER_URL}{path}"
101+
102+
if query:
103+
target_url = f"{target_url}?{query}"
104+
105+
logger.info(f"Proxying {path} to {target_url}")
106+
107+
# Prepare headers (remove host header to avoid conflicts)
108+
headers = dict(request.headers)
109+
headers.pop("host", None)
110+
111+
# Proxy the request using httpx
112+
async with httpx.AsyncClient() as client:
113+
proxied_response = await client.request(
114+
method=request.method,
115+
url=target_url,
116+
headers=headers,
117+
content=await request.body(),
118+
timeout=None # Consider setting a reasonable timeout
119+
)
120+
121+
# Copy headers except for transfer-encoding, content-encoding, connection
122+
excluded_headers = {"content-encoding", "transfer-encoding", "connection"}
123+
response_headers = {k: v for k, v in proxied_response.headers.items() if k.lower() not in excluded_headers}
124+
125+
return Response(
126+
content=proxied_response.content,
127+
status_code=proxied_response.status_code,
128+
headers=response_headers,
129+
media_type=proxied_response.headers.get("content-type")
130+
)
131+
132+
except HTTPException as http_exc:
133+
logger.warning(f"HTTPException: {http_exc.detail} for path {request.url.path}")
134+
return JSONResponse(status_code=http_exc.status_code, content={"detail": http_exc.detail})
135+
except Exception as exc:
136+
logger.error(f"Unhandled error: {exc}")
137+
return JSONResponse(status_code=500, content={"detail": "Internal server error"})

0 commit comments

Comments
 (0)