- 引入 DirectAnnouncementCallback 类型用于处理直连模式下的公告 - 在 DelegationManager 中添加 _direct_announcement_callback 属性和设置方法 - 实现 _notify_direct_announcement 方法用于在非总线模式下将公告回写到本地会话 - 在委托取消、完成和分组完成时添加对直连公告的通知逻辑 feat(web): 增加 WebSocket 广播器支持实时会话更新通知 - 创建 WebSocketBroadcaster 类用于跟踪认证的 WebSocket 连接并广播 JSON 事件 - 在应用启动时初始化 websocket_broadcaster 实例 - 实现连接注册、注销和消息广播功能 - 添加过期连接清理机制 feat(agent): 新增系统公告处理方法支持本地处理 - 在 AgentLoop 中添加 process_system_announcement 方法用于在无常驻 run() 场景下处理系统公告 - 创建 InboundMessage 并通过 _process_message 进行处理 feat(cron): 改进定时任务的会话路由解析和实时更新 - 添加 _resolve_cron_session_key 和 _infer_cron_route_from_session_key 辅助函数 - 在 cron 任务执行完成后通过 WebSocket 广播会话更新事件 - 在添加定时任务时自动推断目标会话的渠道和聊天 ID refactor: 项目名称从 Boardware Genius 统一改为 Boardware Agent Sandbox - 更新前端页面标题和描述文本中的产品名称 - 添加新的品牌 Logo 图片资源 - 在前端布局中使用新的 Logo 显示 - 更新授权门户中的品牌信息和 Logo 显示 feat(frontend): 添加会话更新事件监听实现消息自动刷新 - 定义 SessionUpdatedEvent 类型接口 - 在 ChatPage 中添加会话更新事件的处理逻辑 - 当收到会话更新事件时自动重新加载会话列表和当前会话消息 feat(api): 扩展定时任务 API 支持会话键参数 - 在 addCronJob API 参数中添加 session_key 字段 - 更新前端 Cron 页面的表单处理以传递当前会话键
113 lines
3.4 KiB
Python
Executable File
113 lines
3.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import subprocess
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
|
|
DEFAULT_REGISTRY = (
|
|
Path(__file__).resolve().parents[1]
|
|
/ "app-instance"
|
|
/ "runtime"
|
|
/ "registry"
|
|
/ "instances.json"
|
|
)
|
|
DEFAULT_OUTPUT = Path(__file__).resolve().parent / "runtime" / "conf.d" / "instances.conf"
|
|
|
|
|
|
def load_instances(path: Path) -> list[dict[str, Any]]:
|
|
if not path.exists():
|
|
return []
|
|
try:
|
|
data = json.loads(path.read_text(encoding="utf-8"))
|
|
except json.JSONDecodeError:
|
|
return []
|
|
items = data.get("instances", [])
|
|
return [item for item in items if isinstance(item, dict)]
|
|
|
|
|
|
def is_container_running(container_name: str) -> bool:
|
|
name = container_name.strip()
|
|
if not name:
|
|
return False
|
|
try:
|
|
result = subprocess.run(
|
|
["docker", "inspect", "-f", "{{.State.Running}}", name],
|
|
check=False,
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
except OSError:
|
|
return False
|
|
return result.returncode == 0 and result.stdout.strip().lower() == "true"
|
|
|
|
|
|
def render_server(instance_host: str, container_name: str, upstream_port: int) -> str:
|
|
return f"""server {{
|
|
listen 80;
|
|
server_name {instance_host};
|
|
|
|
location / {{
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
proxy_set_header X-Forwarded-Host $host;
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
proxy_set_header Connection $connection_upgrade;
|
|
proxy_read_timeout 3600s;
|
|
proxy_send_timeout 3600s;
|
|
proxy_pass http://{container_name}:{upstream_port};
|
|
}}
|
|
}}
|
|
"""
|
|
|
|
|
|
def render(instances: list[dict[str, Any]], upstream_port: int) -> str:
|
|
blocks = [
|
|
"server {\n"
|
|
" listen 80 default_server;\n"
|
|
" server_name _;\n"
|
|
" return 404;\n"
|
|
"}\n"
|
|
]
|
|
seen_hosts: set[str] = set()
|
|
for item in sorted(instances, key=lambda value: str(value.get("instance_host", ""))):
|
|
instance_host = str(item.get("instance_host", "") or "").strip()
|
|
container_name = str(item.get("container_name", "") or "").strip()
|
|
if (
|
|
not instance_host
|
|
or not container_name
|
|
or instance_host in seen_hosts
|
|
or not is_container_running(container_name)
|
|
):
|
|
continue
|
|
seen_hosts.add(instance_host)
|
|
blocks.append(render_server(instance_host, container_name, upstream_port))
|
|
return "\n".join(blocks)
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description="Render nginx routes for app instances.")
|
|
parser.add_argument("--registry", default=str(DEFAULT_REGISTRY), help="Registry JSON path.")
|
|
parser.add_argument("--output", default=str(DEFAULT_OUTPUT), help="Nginx output config path.")
|
|
parser.add_argument("--upstream-port", default=8080, type=int, help="App container upstream port.")
|
|
args = parser.parse_args()
|
|
|
|
registry_path = Path(args.registry).expanduser()
|
|
output_path = Path(args.output).expanduser()
|
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
contents = render(load_instances(registry_path), args.upstream_port)
|
|
output_path.write_text(contents, encoding="utf-8")
|
|
print(output_path)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|