feat(deploy-control): 添加直接IP绑定功能支持
新增ipaddress模块导入以支持IP地址处理, 添加DEPLOY_DIRECT_PUBLIC_HOST_BIND_IP环境变量配置, 实现IP地址验证、直接URL构建和端口分配功能, 当基础域名是IP地址时自动使用直接绑定模式, 支持IPv4和IPv6地址格式并添加相应参数传递
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import ipaddress
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
@ -56,6 +57,7 @@ PUBLIC_SCHEME = os.environ.get("DEPLOY_PUBLIC_SCHEME", "http").strip() or "http"
|
||||
PUBLIC_BASE_DOMAIN = os.environ.get("DEPLOY_PUBLIC_BASE_DOMAIN", "localhost").strip()
|
||||
PUBLIC_HOST_TEMPLATE = os.environ.get("DEPLOY_PUBLIC_HOST_TEMPLATE", "{slug}.{base_domain}").strip()
|
||||
PUBLIC_PORT = int(os.environ.get("DEPLOY_PUBLIC_PORT", "8088").strip() or "8088")
|
||||
DIRECT_PUBLIC_HOST_BIND_IP = os.environ.get("DEPLOY_DIRECT_PUBLIC_HOST_BIND_IP", "0.0.0.0").strip() or "0.0.0.0"
|
||||
AUTO_START_PROXY = os.environ.get("DEPLOY_AUTO_START_PROXY", "1").strip() not in {"0", "false", "False"}
|
||||
HEALTH_TIMEOUT_SECONDS = float(os.environ.get("DEPLOY_HEALTH_TIMEOUT_SECONDS", "60").strip() or "60")
|
||||
HEALTH_INTERVAL_SECONDS = float(os.environ.get("DEPLOY_HEALTH_INTERVAL_SECONDS", "1").strip() or "1")
|
||||
@ -195,6 +197,39 @@ def build_public_url(host: str) -> str:
|
||||
return f"{PUBLIC_SCHEME}://{netloc}"
|
||||
|
||||
|
||||
def public_base_domain_ip() -> ipaddress.IPv4Address | ipaddress.IPv6Address | None:
|
||||
value = PUBLIC_BASE_DOMAIN.strip().strip("[]")
|
||||
try:
|
||||
return ipaddress.ip_address(value)
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
|
||||
def build_direct_public_url(host: ipaddress.IPv4Address | ipaddress.IPv6Address, host_port: int) -> str:
|
||||
host_value = f"[{host}]" if host.version == 6 else str(host)
|
||||
return f"http://{host_value}:{host_port}"
|
||||
|
||||
|
||||
def pick_instance_host_port(instance_id: str) -> int:
|
||||
args = [
|
||||
str(REGISTRY_TOOL),
|
||||
"--registry",
|
||||
str(REGISTRY_PATH),
|
||||
"next-port",
|
||||
"--start",
|
||||
"20000",
|
||||
"--end",
|
||||
"29999",
|
||||
]
|
||||
if instance_id:
|
||||
args.extend(["--exclude-instance-id", instance_id])
|
||||
output = run_command(args)
|
||||
try:
|
||||
return int(output.strip())
|
||||
except ValueError as exc:
|
||||
raise ApiError(HTTPStatus.BAD_GATEWAY, f"invalid registry port response: {output}") from exc
|
||||
|
||||
|
||||
def build_internal_api_base_url(record: dict[str, Any]) -> str:
|
||||
container_name = str(record.get("container_name", "") or "").strip()
|
||||
if container_name:
|
||||
@ -247,7 +282,13 @@ def create_or_get_instance(payload: dict[str, Any]) -> dict[str, Any]:
|
||||
if existing is None:
|
||||
ensure_network()
|
||||
public_host = build_public_host(slug=slug, instance_id=instance_id, username=username)
|
||||
public_url = build_public_url(public_host)
|
||||
direct_public_host = public_base_domain_ip()
|
||||
host_port: int | None = None
|
||||
if direct_public_host is not None:
|
||||
host_port = pick_instance_host_port(instance_id)
|
||||
public_url = build_direct_public_url(direct_public_host, host_port)
|
||||
else:
|
||||
public_url = build_public_url(public_host)
|
||||
authz_base_url = str(payload.get("authz_base_url", "") or DEFAULT_AUTHZ_BASE_URL).strip()
|
||||
authz_outlook_mcp_url = str(
|
||||
payload.get("authz_outlook_mcp_url", "") or DEFAULT_AUTHZ_OUTLOOK_MCP_URL
|
||||
@ -279,6 +320,9 @@ def create_or_get_instance(payload: dict[str, Any]) -> dict[str, Any]:
|
||||
"--network",
|
||||
INSTANCE_NETWORK_NAME,
|
||||
]
|
||||
if host_port is not None:
|
||||
command.extend(["--host-port", str(host_port)])
|
||||
command.extend(["--host-bind-ip", DIRECT_PUBLIC_HOST_BIND_IP])
|
||||
if authz_base_url:
|
||||
command.extend(["--authz-base-url", authz_base_url])
|
||||
if DEFAULT_AUTHZ_INTERNAL_TOKEN:
|
||||
|
||||
Reference in New Issue
Block a user