diff --git a/api/main.py b/api/main.py index 3ca0cbe..810508a 100644 --- a/api/main.py +++ b/api/main.py @@ -37,8 +37,10 @@ def load_version() -> str: _EXPOSE_STATS = os.getenv("FILESYNC_EXPOSE_STATS") == "1" # Init FastAPI. The interactive docs and OpenAPI schema are off: the HTTP surface is just -# /health, and the schema would only map the attack surface for a scanner. -app = FastAPI(title='FileSync API', version=load_version(), root_path="/api", +# /api/health, and the schema would only map the attack surface for a scanner. +# nginx forwards /api/ unchanged, so routes carry the prefix themselves (FastAPI's +# root_path parameter is deprecated). +app = FastAPI(title='FileSync API', version=load_version(), docs_url=None, redoc_url=None, openapi_url=None) # The signaling socket signs TURN credentials and reports the version on 'registered'. @@ -58,7 +60,7 @@ def load_version() -> str: ) # Add health check route: liveness, plus signaling capacity when FILESYNC_EXPOSE_STATS=1. -@app.get("/health") +@app.get("/api/health") async def health_check(): if _EXPOSE_STATS: return {"status": "ok", **registry_stats()} diff --git a/api/requirements.txt b/api/requirements.txt index d93d0ef..0e715a5 100644 --- a/api/requirements.txt +++ b/api/requirements.txt @@ -1,3 +1,3 @@ # Pinned for reproducible builds; not fastapi[standard] (unneeded extras). -fastapi==0.143.3 +fastapi==0.146.0 uvicorn[standard]==0.54.0 diff --git a/tests/test_main.py b/tests/test_main.py index 68c0cb9..82981a3 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -31,23 +31,23 @@ def main(monkeypatch): class TestHealth: def test_health_is_minimal_by_default(self, main): - r = TestClient(main.app).get("/health") + r = TestClient(main.app).get("/api/health") assert r.status_code == 200 assert r.json() == {"status": "ok"} def test_health_stats_are_opt_in(self, monkeypatch): main = _load_main(monkeypatch, FILESYNC_EXPOSE_STATS="1") - body = TestClient(main.app).get("/health").json() + body = TestClient(main.app).get("/api/health").json() assert body["status"] == "ok" assert set(body) == {"status", "registered_peers", "claims", "connections", "max_connections"} def test_other_values_do_not_expose_stats(self, monkeypatch): main = _load_main(monkeypatch, FILESYNC_EXPOSE_STATS="true") - assert TestClient(main.app).get("/health").json() == {"status": "ok"} + assert TestClient(main.app).get("/api/health").json() == {"status": "ok"} class TestRemovedSurface: - @pytest.mark.parametrize("path", ["/", "/uuid", "/credentials"]) + @pytest.mark.parametrize("path", ["/", "/api/uuid", "/api/credentials"]) def test_removed_routes_404(self, main, path): assert TestClient(main.app).get(path).status_code == 404 @@ -109,12 +109,12 @@ def test_startup_configures_signaling(self, main): class TestCors: def test_cors_off_by_default(self, main): - r = TestClient(main.app).get("/health", headers={"Origin": "http://localhost:5173"}) + r = TestClient(main.app).get("/api/health", headers={"Origin": "http://localhost:5173"}) assert "access-control-allow-origin" not in r.headers def test_cors_origins(self, monkeypatch): main = _load_main(monkeypatch, CORS_ORIGINS="http://localhost:5173, ") - r = TestClient(main.app).get("/health", headers={"Origin": "http://localhost:5173"}) + r = TestClient(main.app).get("/api/health", headers={"Origin": "http://localhost:5173"}) assert r.headers["access-control-allow-origin"] == "http://localhost:5173"