chore: update external connector deployment flow

This commit is contained in:
2026-06-05 17:27:05 +08:00
parent 2c5205b06e
commit e0bc6c55b0
12 changed files with 318 additions and 93 deletions

View File

@ -83,6 +83,7 @@ class FeishuBotProvider:
metadata = {
"eventCallbackPath": "/feishu/events",
"eventCallbackUrl": f"{self.public_base_url}/feishu/events",
"bridgeBaseUrl": str(payload.get("callbackBaseUrl") or self.bridge_base_url),
}
app_id = str(options.get("appId") or options.get("app_id") or "").strip()
app_secret = str(options.get("appSecret") or options.get("app_secret") or "").strip()
@ -212,7 +213,7 @@ class FeishuBotProvider:
if bridge_event is None:
return {"ok": True, "ignored": "empty_or_oversized"}
self.bridge_post(
f"{self.bridge_base_url}/api/channel-connector-bridge/events",
f"{_bridge_base_url(session, self.bridge_base_url)}/api/channel-connector-bridge/events",
bridge_event,
{"Authorization": f"Bearer {self.bridge_token}"},
)
@ -452,13 +453,17 @@ class FeishuBotProvider:
"FEISHU_TEXT_BATCH_DELAY_MS": str(_positive_int(metadata.get("textBatchDelayMs"), default=0)),
"FEISHU_TEXT_BATCH_MAX_MESSAGES": str(_positive_int(metadata.get("textBatchMaxMessages"), default=10)),
"FEISHU_TEXT_BATCH_MAX_CHARS": str(_positive_int(metadata.get("textBatchMaxChars"), default=20000)),
"BEAVER_BRIDGE_BASE_URL": self.bridge_base_url,
"BEAVER_BRIDGE_BASE_URL": _bridge_base_url(session, self.bridge_base_url),
"BEAVER_BRIDGE_TOKEN": self.bridge_token,
}
)
return subprocess.Popen(["node", str(script)], env=env, cwd=str(script.parent))
def _bridge_base_url(session: ConnectorSessionState, fallback: str) -> str:
return str(session.metadata.get("bridgeBaseUrl") or fallback).rstrip("/")
def _bridge_event_from_feishu(session: ConnectorSessionState, header: dict[str, Any], event: dict[str, Any]) -> dict[str, Any] | None:
message = dict(event.get("message") or {})
sender = dict(event.get("sender") or {})

View File

@ -246,7 +246,7 @@ class WeixinIlinkProvider:
flush=True,
)
self.bridge_post(
f"{self.bridge_base_url}/api/channel-connector-bridge/events",
f"{_bridge_base_url(session, self.bridge_base_url)}/api/channel-connector-bridge/events",
event,
{"Authorization": f"Bearer {self.bridge_token}"},
)
@ -384,6 +384,10 @@ def _url(base_url: str, endpoint: str) -> str:
return f"{base_url.rstrip('/')}/{endpoint.lstrip('/')}"
def _bridge_base_url(session: ConnectorSessionState, fallback: str) -> str:
return str(session.metadata.get("bridgeBaseUrl") or fallback).rstrip("/")
def _base_info() -> dict[str, str]:
return {"channel_version": "2.4.3", "bot_agent": "Beaver/1.0"}

View File

@ -384,6 +384,44 @@ def test_feishu_event_route_forwards_message_to_bridge(tmp_path) -> None:
assert bridge_posts[0][1]["peerId"] == "ou_user"
def test_feishu_event_route_uses_session_callback_base_url(tmp_path) -> None:
bridge_posts: list[tuple[str, dict[str, object], dict[str, str]]] = []
provider = _provider(tmp_path, bridge_posts=bridge_posts)
provider.start_session(
{
"kind": "feishu",
"connectionId": "conn_1",
"channelId": "feishu-main",
"displayName": "Feishu Main",
"callbackBaseUrl": "http://app-instance-jaychen:8080",
"options": {"appId": "cli_xxx", "appSecret": "secret", "verificationToken": "verify-token"},
}
)
app = create_app(provider=provider, api_token="sidecar-token")
with TestClient(app) as client:
response = client.post(
"/feishu/events",
json={
"schema": "2.0",
"header": {"event_id": "evt_1", "token": "verify-token", "app_id": "cli_xxx"},
"event": {
"sender": {"sender_id": {"open_id": "ou_user"}},
"message": {
"message_id": "om_1",
"chat_id": "oc_chat",
"chat_type": "p2p",
"message_type": "text",
"content": "{\"text\":\"hello feishu\"}",
},
},
},
)
assert response.status_code == 200
assert bridge_posts[0][0] == "http://app-instance-jaychen:8080/api/channel-connector-bridge/events"
def test_feishu_event_route_ignores_bot_sender_and_platform_commands(tmp_path) -> None:
bridge_posts: list[tuple[str, dict[str, object], dict[str, str]]] = []
provider = _provider(tmp_path, bridge_posts=bridge_posts)

View File

@ -438,3 +438,35 @@ def test_weixin_ilink_provider_poll_once_forwards_bridge_event(tmp_path) -> None
assert bridge_posts[0][1]["eventId"] == "weixin-main:42"
assert bridge_posts[0][1]["content"] == "hello"
assert bridge_posts[0][1]["peerId"] == "wx-user"
def test_weixin_ilink_provider_poll_once_uses_session_callback_base_url(tmp_path) -> None:
http = FakeHttpClient()
bridge_posts: list[tuple[str, dict[str, object], dict[str, str]]] = []
def bridge_post(url: str, payload: dict[str, object], headers: dict[str, str]) -> None:
bridge_posts.append((url, payload, headers))
provider = WeixinIlinkProvider(
store=SidecarStateStore(tmp_path / "state.json"),
http_client=http,
bridge_base_url="http://global-beaver:8080",
bridge_token="bridge-token",
bridge_post=bridge_post,
start_receivers=False,
)
session = provider.start_session(
{
"kind": "weixin",
"connectionId": "conn_1",
"channelId": "weixin-main",
"displayName": "Weixin Main",
"callbackBaseUrl": "http://app-instance-jaychen:8080",
"options": {},
}
)
provider.get_session(session["sessionId"])
provider.poll_once("conn_1")
assert bridge_posts[0][0] == "http://app-instance-jaychen:8080/api/channel-connector-bridge/events"