mirror of
https://github.com/furyhawk/api_gateway.git
synced 2026-07-21 02:06:50 +00:00
feat: update gateway configuration to support dynamic gateway port and enhance Docker integration
This commit is contained in:
+2
-1
@@ -1,2 +1,3 @@
|
|||||||
DATAMALL_API_KEY=replace-with-your-lta-datamall-account-key
|
DATAMALL_API_KEY=replace-with-your-lta-datamall-account-key
|
||||||
PORT=8000
|
PORT=8067
|
||||||
|
GATEWAY_PORT=8067
|
||||||
|
|||||||
+1
-1
@@ -16,4 +16,4 @@ COPY openapi_json ./openapi_json
|
|||||||
|
|
||||||
EXPOSE 8000
|
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,6 +5,7 @@ IMAGE_NAME ?= openapi-api-gateway
|
|||||||
IMAGE_TAG ?= latest
|
IMAGE_TAG ?= latest
|
||||||
CONTAINER_NAME ?= openapi-api-gateway
|
CONTAINER_NAME ?= openapi-api-gateway
|
||||||
PORT ?= 8000
|
PORT ?= 8000
|
||||||
|
GATEWAY_PORT ?= $(PORT)
|
||||||
ENV_FILE ?= .env
|
ENV_FILE ?= .env
|
||||||
CONFIG_PROFILE ?= local
|
CONFIG_PROFILE ?= local
|
||||||
COMPOSE_FILE ?= docker-compose.dev.yml
|
COMPOSE_FILE ?= docker-compose.dev.yml
|
||||||
@@ -46,14 +47,15 @@ run:
|
|||||||
$(MAKE) stop >/dev/null 2>&1 || true
|
$(MAKE) stop >/dev/null 2>&1 || true
|
||||||
$(CONTAINER_ENGINE) run -d \
|
$(CONTAINER_ENGINE) run -d \
|
||||||
--name $(CONTAINER_NAME) \
|
--name $(CONTAINER_NAME) \
|
||||||
-p $(PORT):8000 \
|
-p $(PORT):$(GATEWAY_PORT) \
|
||||||
-e GATEWAY_CONFIG_PATH=$(GATEWAY_CONFIG_PATH) \
|
-e GATEWAY_CONFIG_PATH=$(GATEWAY_CONFIG_PATH) \
|
||||||
|
-e GATEWAY_PORT=$(GATEWAY_PORT) \
|
||||||
$(RUN_ENV) \
|
$(RUN_ENV) \
|
||||||
$(IMAGE_NAME):$(IMAGE_TAG)
|
$(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:
|
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:
|
stop:
|
||||||
-$(CONTAINER_ENGINE) rm -f $(CONTAINER_NAME)
|
-$(CONTAINER_ENGINE) rm -f $(CONTAINER_NAME)
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ uv sync
|
|||||||
|
|
||||||
3. Choose a gateway profile:
|
3. Choose a gateway profile:
|
||||||
- `config/gateway.yaml` targets a locally running companion backend on `127.0.0.1:8068`.
|
- `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:
|
4. Run the gateway with the managed environment:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -49,6 +49,12 @@ make run-local CONFIG_PROFILE=local
|
|||||||
5. Open the docs:
|
5. Open the docs:
|
||||||
- `http://127.0.0.1:8000/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
|
## Companion Backend Integration
|
||||||
This gateway is a good front door for the companion backend at `furyhawk/lta_datamall_api`.
|
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`.
|
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:
|
Notes:
|
||||||
- `make compose-up` starts the companion backend first, checks its live `/openapi.json`, then starts the gateway.
|
- `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.
|
- The compose workflow uses Docker Compose because the companion backend is built directly from its Git repository.
|
||||||
@@ -95,6 +103,7 @@ settings:
|
|||||||
title: string
|
title: string
|
||||||
version: string
|
version: string
|
||||||
description: string
|
description: string
|
||||||
|
gateway_port: 8067
|
||||||
external_openapi_file: path/to/openapi.json
|
external_openapi_file: path/to/openapi.json
|
||||||
cache_enabled: false
|
cache_enabled: false
|
||||||
cache_ttl_seconds: 30
|
cache_ttl_seconds: 30
|
||||||
@@ -125,6 +134,7 @@ Notes:
|
|||||||
- `upstream_path` overrides forwarded path.
|
- `upstream_path` overrides forwarded path.
|
||||||
- `strip_prefix` removes a leading path segment before forwarding.
|
- `strip_prefix` removes a leading path segment before forwarding.
|
||||||
- `port` overrides the upstream URL port without changing the host or scheme.
|
- `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
|
## Administrative Portal and Dashboard
|
||||||
- Admin UI: `GET /admin/portal`
|
- Admin UI: `GET /admin/portal`
|
||||||
@@ -203,7 +213,8 @@ make stop
|
|||||||
|
|
||||||
### Useful Variables
|
### Useful Variables
|
||||||
- `CONTAINER_ENGINE=docker` (or `podman`)
|
- `CONTAINER_ENGINE=docker` (or `podman`)
|
||||||
- `PORT=8080`
|
- `PORT=8067`
|
||||||
|
- `GATEWAY_PORT=8067`
|
||||||
- `IMAGE_NAME=openapi-api-gateway`
|
- `IMAGE_NAME=openapi-api-gateway`
|
||||||
- `IMAGE_TAG=latest`
|
- `IMAGE_TAG=latest`
|
||||||
- `ENV_FILE=.env`
|
- `ENV_FILE=.env`
|
||||||
@@ -211,7 +222,7 @@ make stop
|
|||||||
Example:
|
Example:
|
||||||
```bash
|
```bash
|
||||||
make build CONTAINER_ENGINE=docker IMAGE_TAG=dev
|
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)
|
## GitHub Packages (GHCR)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ settings:
|
|||||||
title: LTA DataMall API Gateway
|
title: LTA DataMall API Gateway
|
||||||
version: 0.2.1
|
version: 0.2.1
|
||||||
description: Configurable gateway for OpenAPI-based upstream services
|
description: Configurable gateway for OpenAPI-based upstream services
|
||||||
|
gateway_port: 8067
|
||||||
external_openapi_file: openapi_json/lta_datamall_openapi_v0-1-1.json
|
external_openapi_file: openapi_json/lta_datamall_openapi_v0-1-1.json
|
||||||
cache_enabled: true
|
cache_enabled: true
|
||||||
cache_ttl_seconds: 30
|
cache_ttl_seconds: 30
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ settings:
|
|||||||
title: LTA DataMall API Gateway
|
title: LTA DataMall API Gateway
|
||||||
version: 0.2.1
|
version: 0.2.1
|
||||||
description: Configurable gateway for a locally running LTA DataMall backend
|
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
|
external_openapi_file: openapi_json/lta_datamall_openapi_v0-1-1.json
|
||||||
cache_enabled: true
|
cache_enabled: true
|
||||||
cache_ttl_seconds: 30
|
cache_ttl_seconds: 30
|
||||||
|
|||||||
@@ -18,5 +18,6 @@ services:
|
|||||||
- lta-datamall-api
|
- lta-datamall-api
|
||||||
environment:
|
environment:
|
||||||
GATEWAY_CONFIG_PATH: config/gateway.container.yaml
|
GATEWAY_CONFIG_PATH: config/gateway.container.yaml
|
||||||
|
GATEWAY_PORT: ${PORT:-8000}
|
||||||
ports:
|
ports:
|
||||||
- "${PORT:-8000}:8000"
|
- "${PORT:-8000}:${PORT:-8000}"
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ class GatewaySettings(BaseModel):
|
|||||||
title: str = "OpenAPI API Gateway"
|
title: str = "OpenAPI API Gateway"
|
||||||
version: str = "0.1.0"
|
version: str = "0.1.0"
|
||||||
description: str = "Configurable API gateway"
|
description: str = "Configurable API gateway"
|
||||||
|
gateway_port: int = Field(default=8000, ge=1, le=65535)
|
||||||
external_openapi_file: str | None = None
|
external_openapi_file: str | None = None
|
||||||
cache_enabled: bool = False
|
cache_enabled: bool = False
|
||||||
cache_ttl_seconds: float = Field(default=30.0, ge=1.0, le=3600.0)
|
cache_ttl_seconds: float = Field(default=30.0, ge=1.0, le=3600.0)
|
||||||
|
|||||||
@@ -1,3 +1,35 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
import uvicorn
|
||||||
|
|
||||||
from .app import create_app
|
from .app import create_app
|
||||||
|
from .config import load_gateway_config
|
||||||
|
|
||||||
app = create_app()
|
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()
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ routes:
|
|||||||
assert "demo" in config.upstreams
|
assert "demo" in config.upstreams
|
||||||
assert config.routes[0].path == "/api/v1/demo"
|
assert config.routes[0].path == "/api/v1/demo"
|
||||||
assert config.upstreams["demo"].port == 8443
|
assert config.upstreams["demo"].port == 8443
|
||||||
|
assert config.settings.gateway_port == 8000
|
||||||
|
|
||||||
|
|
||||||
def test_load_gateway_config_unknown_upstream(tmp_path) -> None:
|
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} == {
|
assert {route.path for route in local_config.routes} == {
|
||||||
route.path for route in container_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
|
||||||
|
|||||||
@@ -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
|
||||||
Reference in New Issue
Block a user