fix(api): stop advertising a /docs URL that production disables

The FastAPI app deliberately turns off docs/redoc/openapi in production:

    docs_url="/docs" if not settings.is_production else None

but the root endpoint returned `"docs": "/docs"` unconditionally. So the
public API root promised a URL that 404s — api.bootno.de/ says
{"name":"Bootnode API","version":"2.0.0","docs":"/docs","status":"operational"}
while /docs, /redoc and /openapi.json all return {"detail":"Not Found"}.

Anyone reading the root to find the schema followed a dead link, and the
disabled-in-prod decision was correct, so the answer is to tell the truth
rather than re-enable them.

root() now mirrors app.docs_url and omits the key entirely when docs are off,
so the two cannot drift again — there is no second place to remember when the
setting flips. Verified both ways: with docs_url None the key is absent, with
"/docs" it is present.
This commit is contained in:
2026-07-26 10:03:31 -07:00
parent d13ebaa314
commit b06f09f6c2
+13 -3
View File
@@ -146,10 +146,20 @@ async def health_check() -> dict:
@app.get("/") @app.get("/")
async def root() -> dict: async def root() -> dict:
"""Root endpoint.""" """Root endpoint.
return {
`docs` mirrors app.docs_url rather than hardcoding "/docs". The FastAPI app
above disables docs/redoc/openapi in production on purpose, so advertising
"/docs" unconditionally pointed every caller of the public API root at a URL
that 404s — api.bootno.de says `"docs": "/docs"` and /docs returns
{"detail":"Not Found"}. Reading the real value keeps the two in step however
the setting is flipped, instead of leaving a second place to remember.
"""
body = {
"name": "Bootnode API", "name": "Bootnode API",
"version": "2.0.0", "version": "2.0.0",
"docs": "/docs",
"status": "operational", "status": "operational",
} }
if app.docs_url:
body["docs"] = app.docs_url
return body