feat: update gateway configuration to support dynamic gateway port and enhance Docker integration

This commit is contained in:
2026-06-01 11:26:46 +08:00
parent c0968b2fd1
commit fc947437c4
11 changed files with 95 additions and 10 deletions
+2 -1
View File
@@ -1,2 +1,3 @@
DATAMALL_API_KEY=replace-with-your-lta-datamall-account-key
PORT=8000
PORT=8067
GATEWAY_PORT=8067
+1 -1
View File
@@ -16,4 +16,4 @@ COPY openapi_json ./openapi_json
EXPOSE 8000
CMD ["uv", "run", "uvicorn", "gateway_framework.main:app", "--host", "0.0.0.0", "--port", "8000", "--app-dir", "src"]
CMD ["uv", "run", "python", "-m", "gateway_framework.main"]
+5 -3
View File
@@ -5,6 +5,7 @@ IMAGE_NAME ?= openapi-api-gateway
IMAGE_TAG ?= latest
CONTAINER_NAME ?= openapi-api-gateway
PORT ?= 8000
GATEWAY_PORT ?= $(PORT)
ENV_FILE ?= .env
CONFIG_PROFILE ?= local
COMPOSE_FILE ?= docker-compose.dev.yml
@@ -46,14 +47,15 @@ run:
$(MAKE) stop >/dev/null 2>&1 || true
$(CONTAINER_ENGINE) run -d \
--name $(CONTAINER_NAME) \
-p $(PORT):8000 \
-p $(PORT):$(GATEWAY_PORT) \
-e GATEWAY_CONFIG_PATH=$(GATEWAY_CONFIG_PATH) \
-e GATEWAY_PORT=$(GATEWAY_PORT) \
$(RUN_ENV) \
$(IMAGE_NAME):$(IMAGE_TAG)
@echo "Gateway is running on http://127.0.0.1:$(PORT) using $(GATEWAY_CONFIG_PATH)"
@echo "Gateway is running on http://127.0.0.1:$(PORT) using $(GATEWAY_CONFIG_PATH) (bind port $(GATEWAY_PORT))"
run-local:
GATEWAY_CONFIG_PATH=$(GATEWAY_CONFIG_PATH) uv run uvicorn gateway_framework.main:app --host 0.0.0.0 --port $(PORT) --reload --app-dir src
GATEWAY_CONFIG_PATH=$(GATEWAY_CONFIG_PATH) GATEWAY_PORT=$(GATEWAY_PORT) uv run python -m gateway_framework.main
stop:
-$(CONTAINER_ENGINE) rm -f $(CONTAINER_NAME)
+14 -3
View File
@@ -39,7 +39,7 @@ uv sync
3. Choose a gateway profile:
- `config/gateway.yaml` targets a locally running companion backend on `127.0.0.1:8068`.
- `config/gateway.container.yaml` targets the companion backend inside the bundled compose network.
- `config/gateway.container.yaml` targets the companion backend inside the bundled compose network, with default `gateway_port: 8067`.
4. Run the gateway with the managed environment:
```bash
@@ -49,6 +49,12 @@ make run-local CONFIG_PROFILE=local
5. Open the docs:
- `http://127.0.0.1:8000/docs`
To run on a different gateway port, pass `GATEWAY_PORT`:
```bash
make run-local CONFIG_PROFILE=local GATEWAY_PORT=8088
```
## Companion Backend Integration
This gateway is a good front door for the companion backend at `furyhawk/lta_datamall_api`.
@@ -82,6 +88,8 @@ make compose-up
3. Open the gateway at `http://127.0.0.1:8000`.
If you use the repository defaults from `.env.example`, the gateway is exposed on port `8067`.
Notes:
- `make compose-up` starts the companion backend first, checks its live `/openapi.json`, then starts the gateway.
- The compose workflow uses Docker Compose because the companion backend is built directly from its Git repository.
@@ -95,6 +103,7 @@ settings:
title: string
version: string
description: string
gateway_port: 8067
external_openapi_file: path/to/openapi.json
cache_enabled: false
cache_ttl_seconds: 30
@@ -125,6 +134,7 @@ Notes:
- `upstream_path` overrides forwarded path.
- `strip_prefix` removes a leading path segment before forwarding.
- `port` overrides the upstream URL port without changing the host or scheme.
- `settings.gateway_port` controls the gateway listen port unless `GATEWAY_PORT` (or `PORT`) is set.
## Administrative Portal and Dashboard
- Admin UI: `GET /admin/portal`
@@ -203,7 +213,8 @@ make stop
### Useful Variables
- `CONTAINER_ENGINE=docker` (or `podman`)
- `PORT=8080`
- `PORT=8067`
- `GATEWAY_PORT=8067`
- `IMAGE_NAME=openapi-api-gateway`
- `IMAGE_TAG=latest`
- `ENV_FILE=.env`
@@ -211,7 +222,7 @@ make stop
Example:
```bash
make build CONTAINER_ENGINE=docker IMAGE_TAG=dev
make run CONTAINER_ENGINE=docker PORT=8080 ENV_FILE=.env CONFIG_PROFILE=local
make run CONTAINER_ENGINE=docker PORT=8080 GATEWAY_PORT=8080 ENV_FILE=.env CONFIG_PROFILE=local
```
## GitHub Packages (GHCR)
+1
View File
@@ -2,6 +2,7 @@ settings:
title: LTA DataMall API Gateway
version: 0.2.1
description: Configurable gateway for OpenAPI-based upstream services
gateway_port: 8067
external_openapi_file: openapi_json/lta_datamall_openapi_v0-1-1.json
cache_enabled: true
cache_ttl_seconds: 30
+1
View File
@@ -2,6 +2,7 @@ settings:
title: LTA DataMall API Gateway
version: 0.2.1
description: Configurable gateway for a locally running LTA DataMall backend
gateway_port: 8067
external_openapi_file: openapi_json/lta_datamall_openapi_v0-1-1.json
cache_enabled: true
cache_ttl_seconds: 30
+2 -1
View File
@@ -18,5 +18,6 @@ services:
- lta-datamall-api
environment:
GATEWAY_CONFIG_PATH: config/gateway.container.yaml
GATEWAY_PORT: ${PORT:-8000}
ports:
- "${PORT:-8000}:8000"
- "${PORT:-8000}:${PORT:-8000}"
+1
View File
@@ -12,6 +12,7 @@ class GatewaySettings(BaseModel):
title: str = "OpenAPI API Gateway"
version: str = "0.1.0"
description: str = "Configurable API gateway"
gateway_port: int = Field(default=8000, ge=1, le=65535)
external_openapi_file: str | None = None
cache_enabled: bool = False
cache_ttl_seconds: float = Field(default=30.0, ge=1.0, le=3600.0)
+32
View File
@@ -1,3 +1,35 @@
from __future__ import annotations
import os
import uvicorn
from .app import create_app
from .config import load_gateway_config
app = create_app()
def _resolve_runtime_port() -> int:
override = os.getenv("GATEWAY_PORT") or os.getenv("PORT")
if override:
try:
port = int(override)
except ValueError as exc:
raise ValueError("GATEWAY_PORT must be an integer") from exc
if not 1 <= port <= 65535:
raise ValueError("GATEWAY_PORT must be between 1 and 65535")
return port
config_path = os.getenv("GATEWAY_CONFIG_PATH", "config/gateway.yaml")
return load_gateway_config(config_path).settings.gateway_port
def run() -> None:
host = os.getenv("GATEWAY_HOST", "0.0.0.0")
port = _resolve_runtime_port()
uvicorn.run("gateway_framework.main:app", host=host, port=port)
if __name__ == "__main__":
run()
+3 -1
View File
@@ -31,6 +31,7 @@ routes:
assert "demo" in config.upstreams
assert config.routes[0].path == "/api/v1/demo"
assert config.upstreams["demo"].port == 8443
assert config.settings.gateway_port == 8000
def test_load_gateway_config_unknown_upstream(tmp_path) -> None:
@@ -94,4 +95,5 @@ def test_container_gateway_config_matches_local_route_set() -> None:
assert {route.path for route in local_config.routes} == {
route.path for route in container_config.routes
}
assert container_config.upstreams["lta_datamall"].resolved_base_url() == "http://lta-datamall-api:8000/"
assert local_config.settings.gateway_port == container_config.settings.gateway_port
assert container_config.upstreams["lta_datamall"].port is not None
+33
View File
@@ -0,0 +1,33 @@
from __future__ import annotations
from gateway_framework.main import _resolve_runtime_port
def test_resolve_runtime_port_from_env(monkeypatch) -> None:
monkeypatch.setenv("GATEWAY_PORT", "18080")
assert _resolve_runtime_port() == 18080
def test_resolve_runtime_port_from_config_when_env_absent(tmp_path, monkeypatch) -> None:
monkeypatch.delenv("GATEWAY_PORT", raising=False)
monkeypatch.delenv("PORT", raising=False)
config_file = tmp_path / "gateway.yaml"
config_file.write_text(
"""
settings:
gateway_port: 19090
upstreams:
demo:
base_url: https://example.com/
routes:
- path: /api/v1/demo
methods: [GET]
upstream: demo
""".strip(),
encoding="utf-8",
)
monkeypatch.setenv("GATEWAY_CONFIG_PATH", str(config_file))
assert _resolve_runtime_port() == 19090