mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-06-02 19:55:18 +02:00
feat(gateway): register multi-platform gateway routes
This commit is contained in:
parent
51bf2a8361
commit
185759de1f
3 changed files with 72 additions and 25 deletions
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
]
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -9,14 +9,14 @@ from sqlalchemy import select, update
|
|||
|
||||
from app.celery_app import celery_app
|
||||
from app.db import (
|
||||
ExternalChatAccount,
|
||||
ExternalChatEventStatus,
|
||||
ExternalChatHealthStatus,
|
||||
ExternalChatInboundEvent,
|
||||
ExternalChatPlatform,
|
||||
ExternalChatAccount,
|
||||
)
|
||||
from app.gateway.accounts import account_token
|
||||
from app.gateway.inbox import persist_inbound_event, telegram_event_dedupe_key
|
||||
from app.gateway.registry import resolve_platform_bundle
|
||||
from app.gateway.telegram.adapter import TelegramAdapter
|
||||
from app.observability.metrics import (
|
||||
record_gateway_health_check_failure,
|
||||
|
|
@ -69,15 +69,29 @@ def gateway_health_check_task() -> None:
|
|||
result = await session.execute(select(ExternalChatAccount))
|
||||
accounts = list(result.scalars())
|
||||
for account in accounts:
|
||||
token = account_token(account)
|
||||
if not token or account.platform != ExternalChatPlatform.TELEGRAM:
|
||||
continue
|
||||
try:
|
||||
metadata = await TelegramAdapter(token).validate_credentials()
|
||||
bundle = resolve_platform_bundle(account)
|
||||
metadata = await bundle.adapter.validate_credentials()
|
||||
account.health_status = ExternalChatHealthStatus.OK
|
||||
account.bot_username = metadata.get("username")
|
||||
if account.platform == ExternalChatPlatform.TELEGRAM:
|
||||
account.bot_username = metadata.get("username")
|
||||
elif account.platform == ExternalChatPlatform.WHATSAPP:
|
||||
cursor_state = dict(account.cursor_state or {})
|
||||
for key in (
|
||||
"quality_rating",
|
||||
"account_review_status",
|
||||
"status",
|
||||
):
|
||||
if key in metadata:
|
||||
cursor_state[key] = metadata[key]
|
||||
account.cursor_state = cursor_state
|
||||
except Exception:
|
||||
logger.warning("External chat Telegram health check failed", exc_info=True)
|
||||
logger.warning(
|
||||
"External chat health check failed platform=%s account_id=%s",
|
||||
account.platform.value,
|
||||
account.id,
|
||||
exc_info=True,
|
||||
)
|
||||
account.health_status = ExternalChatHealthStatus.FAILING
|
||||
record_gateway_health_check_failure(platform=account.platform.value)
|
||||
account.last_health_check_at = datetime.now(UTC)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue