Files
boltage/peo/web.py
T
Bart BroereandClaude Opus 5.5 f0fb9ddc74 Use Starlette, uvicorn and websockets; rename Docker files to container ones
Serve neo4j-browser with Starlette and uvicorn (the optional browser extra)
instead of a hand-written HTTP server, and handle Bolt over WebSocket with the
websockets library's Sans-I/O protocol. Rename Dockerfile, .dockerignore and
docker/ to Containerfile, .containerignore and container/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 11:27:25 +02:00

55 lines
2.0 KiB
Python

# SPDX-FileCopyrightText: 2026 Bart Broere
# SPDX-License-Identifier: GPL-3.0-or-later
"""HTTP server for the neo4j-browser static files and Neo4j discovery.
Needs the ``browser`` extra: ``pip install 'peo[browser]'``.
"""
from starlette.applications import Starlette
from starlette.datastructures import MutableHeaders
from starlette.middleware import Middleware
from starlette.responses import RedirectResponse, Response
from starlette.routing import Mount, Route
from starlette.staticfiles import StaticFiles
from .discovery import discovery_document
class BrowserFiles(StaticFiles):
"""neo4j-browser's build: index.html must be revalidated, the hashed assets can be cached."""
def file_response(self, full_path, *args, **kwargs):
response = super().file_response(full_path, *args, **kwargs)
response.headers["Cache-Control"] = ("no-cache" if str(full_path).endswith(".html")
else "public, max-age=86400")
return response
def nosniff(app):
async def wrapped(scope, receive, send):
async def send_with_header(message):
if message["type"] == "http.response.start":
MutableHeaders(scope=message)["X-Content-Type-Options"] = "nosniff"
await send(message)
await app(scope, receive, send_with_header)
return wrapped
def create_app(browser_dir: str, bolt_port: int) -> Starlette:
async def root(request):
if "text/html" in request.headers.get("accept", ""):
return RedirectResponse("/browser/", status_code=302)
return Response(discovery_document(request.headers, bolt_port), media_type="application/json")
async def browser(request):
return RedirectResponse("/browser/", status_code=301)
return Starlette(
routes=[
Route("/", root),
Route("/browser", browser),
Mount("/browser", BrowserFiles(directory=browser_dir, html=True)),
],
middleware=[Middleware(nosniff)],
)