本次提交新增了完整的多渠道消息网关系统,包括: 1. 支持飞书、钉钉、Web、Hook 四种渠道的适配器与配置 2. 领域模型层:消息、会话、绑定、出箱等核心实体 3. 应用服务层:管道、中间件、DTO 与业务逻辑 4. 基础设施层:持久化、过滤器、队列等端口实现 5. 接口层:REST API、SSE、WebSocket 通信端点 6. 前端页面与路由配置,添加渠道管理菜单 7. 新增相关依赖包与 docker-compose 部署配置
99 lines
2.9 KiB
Python
99 lines
2.9 KiB
Python
"""Channel worker entrypoint with embedded health endpoint."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import os
|
|
import signal
|
|
import sys
|
|
|
|
if sys.platform == "win32":
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
|
|
|
from aiohttp import web
|
|
|
|
from yuxi.channel.infrastructure.agent.agent_adapter import AgentAdapter
|
|
from yuxi.channel.startup import init_channel, shutdown_channel
|
|
from yuxi.storage.postgres.manager import pg_manager
|
|
from yuxi.utils.logging_config import logger
|
|
|
|
_HEALTH_PORT = int(os.getenv("CHANNEL_HEALTH_PORT", "5051"))
|
|
|
|
|
|
async def _health_handler(request: web.Request) -> web.Response:
|
|
checks: dict[str, str] = {"status": "ok"}
|
|
container = request.app["container"]
|
|
|
|
try:
|
|
redis = container.cache_port
|
|
await redis.ping()
|
|
checks["redis"] = "ok"
|
|
except Exception as e:
|
|
checks["redis"] = f"error: {e}"
|
|
checks["status"] = "degraded"
|
|
|
|
worker_pool = getattr(container, "worker_pool", None)
|
|
if worker_pool and getattr(worker_pool, "is_running", False):
|
|
checks["worker_pool"] = "ok"
|
|
else:
|
|
checks["worker_pool"] = "not_running"
|
|
checks["status"] = "degraded"
|
|
|
|
status_code = 200 if checks["status"] == "ok" else 503
|
|
return web.json_response(checks, status=status_code)
|
|
|
|
|
|
async def _start_health_server(container: object) -> web.AppRunner:
|
|
app = web.Application()
|
|
app["container"] = container
|
|
app.router.add_get("/healthz", _health_handler)
|
|
|
|
runner = web.AppRunner(app)
|
|
await runner.setup()
|
|
site = web.TCPSite(runner, "0.0.0.0", _HEALTH_PORT)
|
|
await site.start()
|
|
logger.info("health endpoint started on port %d", _HEALTH_PORT)
|
|
return runner
|
|
|
|
|
|
async def main():
|
|
redis_url = os.getenv("REDIS_URL", "redis://redis:6379/0")
|
|
config_path = os.getenv("CHANNEL_CONFIG_PATH", "channel_config.yaml")
|
|
|
|
logger.info("starting channel worker, redis=%s", redis_url)
|
|
|
|
container = await init_channel(
|
|
redis_url=redis_url,
|
|
agent_port=AgentAdapter(pg_manager.get_async_session_context),
|
|
config_yaml_path=config_path,
|
|
)
|
|
|
|
health_runner = await _start_health_server(container)
|
|
|
|
stop_event = asyncio.Event()
|
|
|
|
def _signal_handler():
|
|
stop_event.set()
|
|
|
|
loop = asyncio.get_running_loop()
|
|
if sys.platform == "win32":
|
|
for sig in (signal.SIGINT, signal.SIGTERM):
|
|
signal.signal(sig, lambda *_: _signal_handler())
|
|
else:
|
|
for sig in (signal.SIGINT, signal.SIGTERM):
|
|
loop.add_signal_handler(sig, _signal_handler)
|
|
|
|
logger.info("channel worker running, waiting for stop signal")
|
|
await stop_event.wait()
|
|
|
|
await health_runner.cleanup()
|
|
logger.info("health endpoint stopped")
|
|
|
|
await shutdown_channel(container)
|
|
logger.info("channel worker stopped")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(main())
|