mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-06-18 13:46:02 +00:00
2b301e8211
* fix(channels): harden runtime credential management APIs * fix(channels): address review feedback on credential hardening Follow-up to the runtime credential-hardening pass, resolving five review findings: - WeChat auth persistence now writes through a 0o600 NamedTemporaryFile + Path.replace instead of write_text-then-chmod, so the iLink bot_token is never briefly readable at umask defaults (mirrors ChannelRuntimeConfigStore). - The post-write chmod is split into its own try/except: a chmod failure on a filesystem without POSIX perms now logs at debug instead of masquerading as a "failed to persist" warning. - Extracted the three near-identical _require_admin_user helpers (mcp, channel_connections, channels) into a single require_admin_user(request, *, detail) in app/gateway/deps.py; each router supplies its own detail string. - Strengthened the runtime-config-store chmod coverage: a new test injects a temp-file chmod failure and asserts it is logged at debug while the destination is still owner-only (mutation-verified to fail if the chmod is dropped), plus a loose-pre-existing-file case. - Removed the unused _FakeRepo from the blocking-io test: its isinstance gate routes through the repo-less 503 path, so neither stub was ever invoked. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
59 lines
1.9 KiB
Python
59 lines
1.9 KiB
Python
"""Gateway router for IM channel management."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
|
|
from fastapi import APIRouter, HTTPException, Request
|
|
from pydantic import BaseModel
|
|
|
|
from app.gateway.deps import require_admin_user
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter(prefix="/api/channels", tags=["channels"])
|
|
|
|
_ADMIN_REQUIRED_DETAIL = "Admin privileges required to manage channel runtime workers."
|
|
|
|
|
|
class ChannelStatusResponse(BaseModel):
|
|
service_running: bool
|
|
channels: dict[str, dict]
|
|
|
|
|
|
class ChannelRestartResponse(BaseModel):
|
|
success: bool
|
|
message: str
|
|
|
|
|
|
@router.get("/", response_model=ChannelStatusResponse)
|
|
async def get_channels_status() -> ChannelStatusResponse:
|
|
"""Get the status of all IM channels."""
|
|
from app.channels.service import get_channel_service
|
|
|
|
service = get_channel_service()
|
|
if service is None:
|
|
return ChannelStatusResponse(service_running=False, channels={})
|
|
status = service.get_status()
|
|
return ChannelStatusResponse(**status)
|
|
|
|
|
|
@router.post("/{name}/restart", response_model=ChannelRestartResponse)
|
|
async def restart_channel(name: str, request: Request) -> ChannelRestartResponse:
|
|
"""Restart a specific IM channel."""
|
|
await require_admin_user(request, detail=_ADMIN_REQUIRED_DETAIL)
|
|
|
|
from app.channels.service import get_channel_service
|
|
|
|
service = get_channel_service()
|
|
if service is None:
|
|
raise HTTPException(status_code=503, detail="Channel service is not running")
|
|
|
|
success = await service.restart_channel(name)
|
|
if success:
|
|
logger.info("Channel %s restarted successfully", name)
|
|
return ChannelRestartResponse(success=True, message=f"Channel {name} restarted successfully")
|
|
else:
|
|
logger.warning("Failed to restart channel %s", name)
|
|
return ChannelRestartResponse(success=False, message=f"Failed to restart channel {name}")
|