feat: add loopback host configuration for health checks
- Introduced LOOPBACK_HTTP_HOSTS in config.py to define valid loopback addresses for health-check URL and MINI_APP_SKIP_AUTH safety. - Updated run.py to utilize LOOPBACK_HTTP_HOSTS for determining the host in health check and authentication logic. - Enhanced test_app.py to skip tests if the required webapp output directory is not built, improving test reliability.
This commit is contained in:
@@ -22,6 +22,9 @@ logger = logging.getLogger(__name__)
|
|||||||
# Valid port range for HTTP_PORT.
|
# Valid port range for HTTP_PORT.
|
||||||
HTTP_PORT_MIN, HTTP_PORT_MAX = 1, 65535
|
HTTP_PORT_MIN, HTTP_PORT_MAX = 1, 65535
|
||||||
|
|
||||||
|
# Host values treated as loopback (for health-check URL and MINI_APP_SKIP_AUTH safety).
|
||||||
|
LOOPBACK_HTTP_HOSTS = ("127.0.0.1", "localhost", "::1", "")
|
||||||
|
|
||||||
# Project root (parent of duty_teller package). Used for webapp path, etc.
|
# Project root (parent of duty_teller package). Used for webapp path, etc.
|
||||||
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
||||||
|
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ def _run_uvicorn(web_app, port: int) -> None:
|
|||||||
def _wait_for_http_ready(port: int) -> bool:
|
def _wait_for_http_ready(port: int) -> bool:
|
||||||
"""Return True if /health responds successfully within _HTTP_STARTUP_WAIT_SEC."""
|
"""Return True if /health responds successfully within _HTTP_STARTUP_WAIT_SEC."""
|
||||||
host = config.HTTP_HOST
|
host = config.HTTP_HOST
|
||||||
if host == "0.0.0.0":
|
if host not in config.LOOPBACK_HTTP_HOSTS:
|
||||||
host = "127.0.0.1"
|
host = "127.0.0.1"
|
||||||
url = f"http://{host}:{port}/health"
|
url = f"http://{host}:{port}/health"
|
||||||
deadline = time.monotonic() + _HTTP_STARTUP_WAIT_SEC
|
deadline = time.monotonic() + _HTTP_STARTUP_WAIT_SEC
|
||||||
@@ -113,7 +113,7 @@ def main() -> None:
|
|||||||
logger.warning(
|
logger.warning(
|
||||||
"MINI_APP_SKIP_AUTH is set — API auth disabled (insecure); use only for dev"
|
"MINI_APP_SKIP_AUTH is set — API auth disabled (insecure); use only for dev"
|
||||||
)
|
)
|
||||||
if config.HTTP_HOST not in ("127.0.0.1", "localhost", ""):
|
if config.HTTP_HOST not in config.LOOPBACK_HTTP_HOSTS:
|
||||||
print(
|
print(
|
||||||
"ERROR: MINI_APP_SKIP_AUTH must not be used in production (non-localhost).",
|
"ERROR: MINI_APP_SKIP_AUTH must not be used in production (non-localhost).",
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"""Tests for FastAPI app /api/duties."""
|
"""Tests for FastAPI app /api/duties."""
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
from pathlib import Path
|
||||||
from unittest.mock import ANY, patch
|
from unittest.mock import ANY, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@@ -59,7 +60,15 @@ def test_app_static_has_no_store_and_vary(client):
|
|||||||
|
|
||||||
def test_app_js_has_no_store(client):
|
def test_app_js_has_no_store(client):
|
||||||
"""JS and all static under /app get Cache-Control: no-store."""
|
"""JS and all static under /app get Cache-Control: no-store."""
|
||||||
r = client.get("/app/js/main.js")
|
webapp_out = config.PROJECT_ROOT / "webapp-next" / "out"
|
||||||
|
if not webapp_out.is_dir():
|
||||||
|
pytest.skip("webapp-next/out not built")
|
||||||
|
# Next.js static export serves JS under _next/static/chunks/<hash>.js
|
||||||
|
js_files = list(webapp_out.glob("_next/static/chunks/*.js"))
|
||||||
|
if not js_files:
|
||||||
|
pytest.skip("no JS chunks in webapp-next/out")
|
||||||
|
rel = js_files[0].relative_to(webapp_out)
|
||||||
|
r = client.get(f"/app/{rel.as_posix()}")
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert r.headers.get("cache-control") == "no-store"
|
assert r.headers.get("cache-control") == "no-store"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user