feat(agent): add test frontend and Docker stack

This commit is contained in:
2026-09-16 21:42:05 +02:00
parent aa0bee340f
commit fc656188cf
16 changed files with 1065 additions and 71 deletions
+32
View File
@@ -12,9 +12,11 @@ import secrets
import threading
import uuid
from contextlib import asynccontextmanager
from pathlib import Path
from typing import Literal
from fastapi import Depends, FastAPI, HTTPException, Request
from fastapi.responses import FileResponse
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from pydantic import BaseModel, ConfigDict, Field
@@ -27,6 +29,8 @@ from .retrieve import Retriever
API_VERSION = "v1"
DATA_SCOPE = "knowledge_base_only"
_REQUEST_ID_RE = re.compile(r"^[A-Za-z0-9._:-]{1,128}$")
WEB_DIR = Path(__file__).resolve().parent.parent / "web"
_WEB_ASSETS = {"app.js", "styles.css"}
logger = logging.getLogger(__name__)
bearer = HTTPBearer(auto_error=False)
bearer_credentials = Depends(bearer)
@@ -170,9 +174,37 @@ async def add_request_id(request: Request, call_next):
request.state.request_id = request_id
response = await call_next(request)
response.headers["X-Request-ID"] = request_id
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["X-Frame-Options"] = "DENY"
response.headers["Referrer-Policy"] = "no-referrer"
if request.url.path == "/" or request.url.path.startswith("/assets/"):
response.headers["Content-Security-Policy"] = (
"default-src 'self'; base-uri 'none'; form-action 'self'; "
"frame-ancestors 'none'; img-src 'self' data:; "
"script-src 'self'; style-src 'self'; connect-src 'self'"
)
return response
@app.get("/", include_in_schema=False, response_class=FileResponse)
def frontend() -> FileResponse:
return FileResponse(
WEB_DIR / "index.html",
media_type="text/html",
headers={"Cache-Control": "no-store"},
)
@app.get("/assets/{asset_name}", include_in_schema=False, response_class=FileResponse)
def frontend_asset(asset_name: str) -> FileResponse:
if asset_name not in _WEB_ASSETS:
raise HTTPException(status_code=404, detail="Asset nicht gefunden.")
return FileResponse(
WEB_DIR / asset_name,
headers={"Cache-Control": "public, max-age=3600"},
)
def _request_id(request: Request) -> str:
return getattr(request.state, "request_id", uuid.uuid4().hex)