feat(gateway): register multi-platform gateway routes

This commit is contained in:
Anish Sarkar 2026-05-29 10:20:43 +05:30
parent 51bf2a8361
commit 185759de1f
3 changed files with 72 additions and 25 deletions

View file

@ -19,6 +19,8 @@ from .editor_routes import router as editor_router
from .export_routes import router as export_router
from .folders_routes import router as folders_router
from .gateway_webhook_routes import router as gateway_router
from .gateway_whatsapp_baileys_routes import router as gateway_whatsapp_baileys_router
from .gateway_whatsapp_webhook_routes import router as gateway_whatsapp_webhook_router
from .google_calendar_add_connector_route import (
router as google_calendar_add_connector_router,
)
@ -70,6 +72,8 @@ router.include_router(export_router)
router.include_router(documents_router)
router.include_router(folders_router)
router.include_router(gateway_router)
router.include_router(gateway_whatsapp_webhook_router)
router.include_router(gateway_whatsapp_baileys_router)
router.include_router(notes_router)
router.include_router(new_chat_router) # Chat with assistant-ui persistence
router.include_router(agent_revert_router) # POST /threads/{id}/revert/{action_id}

View file

@ -7,6 +7,7 @@ import logging
import uuid
from datetime import UTC, datetime
from typing import Any
from urllib.parse import quote
from fastapi import APIRouter, Depends, HTTPException, Request
from pydantic import BaseModel
@ -16,14 +17,17 @@ from starlette.responses import Response
from app.config import config
from app.db import (
ExternalChatBindingState,
ExternalChatBinding,
ExternalChatPlatform,
ExternalChatAccount,
ExternalChatBinding,
ExternalChatBindingState,
ExternalChatPlatform,
User,
get_async_session,
)
from app.gateway.accounts import get_or_create_system_telegram_account
from app.gateway.accounts import (
get_or_create_system_telegram_account,
get_or_create_system_whatsapp_account,
)
from app.gateway.bindings import resume_binding, revoke_binding
from app.gateway.inbox import persist_inbound_event, telegram_event_dedupe_key
from app.gateway.pairing import generate_pairing_code, pairing_expires_at
@ -131,11 +135,39 @@ async def start_binding(
user: User = Depends(current_active_user),
session: AsyncSession = Depends(get_async_session),
) -> StartBindingResponse:
if body.platform != ExternalChatPlatform.TELEGRAM:
raise HTTPException(status_code=400, detail="Only Telegram is supported in v1")
account = await get_or_create_system_telegram_account(session)
code = generate_pairing_code()
if body.platform == ExternalChatPlatform.TELEGRAM:
account = await get_or_create_system_telegram_account(session)
username = account.bot_username or config.TELEGRAM_SHARED_BOT_USERNAME
if not username:
raise HTTPException(
status_code=500,
detail="Telegram bot username is not configured",
)
deep_link = f"https://t.me/{username}?start={code}"
elif body.platform == ExternalChatPlatform.WHATSAPP:
if config.GATEWAY_WHATSAPP_INTAKE_MODE != "cloud":
raise HTTPException(
status_code=400,
detail="WhatsApp /start pairing requires GATEWAY_WHATSAPP_INTAKE_MODE=cloud",
)
account = await get_or_create_system_whatsapp_account(session)
phone = config.WHATSAPP_SHARED_DISPLAY_PHONE_NUMBER
if not phone:
raise HTTPException(
status_code=500,
detail="WHATSAPP_SHARED_DISPLAY_PHONE_NUMBER is not configured",
)
normalized_phone = "".join(ch for ch in phone if ch.isdigit())
if not normalized_phone:
raise HTTPException(
status_code=500,
detail="WHATSAPP_SHARED_DISPLAY_PHONE_NUMBER must contain digits",
)
deep_link = f"https://wa.me/{normalized_phone}?text={quote(f'/start {code}')}"
else:
raise HTTPException(status_code=400, detail="Unsupported platform")
expires_at = pairing_expires_at()
binding = ExternalChatBinding(
account_id=account.id,
@ -149,13 +181,10 @@ async def start_binding(
await session.commit()
await session.refresh(binding)
username = account.bot_username or config.TELEGRAM_SHARED_BOT_USERNAME
if not username:
raise HTTPException(status_code=500, detail="Telegram bot username is not configured")
return StartBindingResponse(
binding_id=binding.id,
code=code,
deep_link=f"https://t.me/{username}?start={code}",
deep_link=deep_link,
expires_at=expires_at,
)
@ -166,21 +195,21 @@ async def list_bindings(
session: AsyncSession = Depends(get_async_session),
) -> list[dict[str, Any]]:
result = await session.execute(
select(ExternalChatBinding).where(
ExternalChatBinding.user_id == user.id
)
select(ExternalChatBinding, ExternalChatAccount)
.join(ExternalChatAccount, ExternalChatBinding.account_id == ExternalChatAccount.id)
.where(ExternalChatBinding.user_id == user.id)
)
return [
{
"id": binding.id,
"platform": "telegram",
"platform": account.platform.value,
"state": binding.state.value,
"search_space_id": binding.search_space_id,
"external_display_name": binding.external_display_name,
"external_username": binding.external_username,
"suspended_reason": binding.suspended_reason,
}
for binding in result.scalars()
for binding, account in result.all()
]