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 .export_routes import router as export_router
|
||||||
from .folders_routes import router as folders_router
|
from .folders_routes import router as folders_router
|
||||||
from .gateway_webhook_routes import router as gateway_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 (
|
from .google_calendar_add_connector_route import (
|
||||||
router as google_calendar_add_connector_router,
|
router as google_calendar_add_connector_router,
|
||||||
)
|
)
|
||||||
|
|
@ -70,6 +72,8 @@ router.include_router(export_router)
|
||||||
router.include_router(documents_router)
|
router.include_router(documents_router)
|
||||||
router.include_router(folders_router)
|
router.include_router(folders_router)
|
||||||
router.include_router(gateway_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(notes_router)
|
||||||
router.include_router(new_chat_router) # Chat with assistant-ui persistence
|
router.include_router(new_chat_router) # Chat with assistant-ui persistence
|
||||||
router.include_router(agent_revert_router) # POST /threads/{id}/revert/{action_id}
|
router.include_router(agent_revert_router) # POST /threads/{id}/revert/{action_id}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import logging
|
||||||
import uuid
|
import uuid
|
||||||
from datetime import UTC, datetime
|
from datetime import UTC, datetime
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
from urllib.parse import quote
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Request
|
from fastapi import APIRouter, Depends, HTTPException, Request
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
@ -16,14 +17,17 @@ from starlette.responses import Response
|
||||||
|
|
||||||
from app.config import config
|
from app.config import config
|
||||||
from app.db import (
|
from app.db import (
|
||||||
ExternalChatBindingState,
|
|
||||||
ExternalChatBinding,
|
|
||||||
ExternalChatPlatform,
|
|
||||||
ExternalChatAccount,
|
ExternalChatAccount,
|
||||||
|
ExternalChatBinding,
|
||||||
|
ExternalChatBindingState,
|
||||||
|
ExternalChatPlatform,
|
||||||
User,
|
User,
|
||||||
get_async_session,
|
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.bindings import resume_binding, revoke_binding
|
||||||
from app.gateway.inbox import persist_inbound_event, telegram_event_dedupe_key
|
from app.gateway.inbox import persist_inbound_event, telegram_event_dedupe_key
|
||||||
from app.gateway.pairing import generate_pairing_code, pairing_expires_at
|
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),
|
user: User = Depends(current_active_user),
|
||||||
session: AsyncSession = Depends(get_async_session),
|
session: AsyncSession = Depends(get_async_session),
|
||||||
) -> StartBindingResponse:
|
) -> 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()
|
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()
|
expires_at = pairing_expires_at()
|
||||||
binding = ExternalChatBinding(
|
binding = ExternalChatBinding(
|
||||||
account_id=account.id,
|
account_id=account.id,
|
||||||
|
|
@ -149,13 +181,10 @@ async def start_binding(
|
||||||
await session.commit()
|
await session.commit()
|
||||||
await session.refresh(binding)
|
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(
|
return StartBindingResponse(
|
||||||
binding_id=binding.id,
|
binding_id=binding.id,
|
||||||
code=code,
|
code=code,
|
||||||
deep_link=f"https://t.me/{username}?start={code}",
|
deep_link=deep_link,
|
||||||
expires_at=expires_at,
|
expires_at=expires_at,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -166,21 +195,21 @@ async def list_bindings(
|
||||||
session: AsyncSession = Depends(get_async_session),
|
session: AsyncSession = Depends(get_async_session),
|
||||||
) -> list[dict[str, Any]]:
|
) -> list[dict[str, Any]]:
|
||||||
result = await session.execute(
|
result = await session.execute(
|
||||||
select(ExternalChatBinding).where(
|
select(ExternalChatBinding, ExternalChatAccount)
|
||||||
ExternalChatBinding.user_id == user.id
|
.join(ExternalChatAccount, ExternalChatBinding.account_id == ExternalChatAccount.id)
|
||||||
)
|
.where(ExternalChatBinding.user_id == user.id)
|
||||||
)
|
)
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
"id": binding.id,
|
"id": binding.id,
|
||||||
"platform": "telegram",
|
"platform": account.platform.value,
|
||||||
"state": binding.state.value,
|
"state": binding.state.value,
|
||||||
"search_space_id": binding.search_space_id,
|
"search_space_id": binding.search_space_id,
|
||||||
"external_display_name": binding.external_display_name,
|
"external_display_name": binding.external_display_name,
|
||||||
"external_username": binding.external_username,
|
"external_username": binding.external_username,
|
||||||
"suspended_reason": binding.suspended_reason,
|
"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.celery_app import celery_app
|
||||||
from app.db import (
|
from app.db import (
|
||||||
|
ExternalChatAccount,
|
||||||
ExternalChatEventStatus,
|
ExternalChatEventStatus,
|
||||||
ExternalChatHealthStatus,
|
ExternalChatHealthStatus,
|
||||||
ExternalChatInboundEvent,
|
ExternalChatInboundEvent,
|
||||||
ExternalChatPlatform,
|
ExternalChatPlatform,
|
||||||
ExternalChatAccount,
|
|
||||||
)
|
)
|
||||||
from app.gateway.accounts import account_token
|
|
||||||
from app.gateway.inbox import persist_inbound_event, telegram_event_dedupe_key
|
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.gateway.telegram.adapter import TelegramAdapter
|
||||||
from app.observability.metrics import (
|
from app.observability.metrics import (
|
||||||
record_gateway_health_check_failure,
|
record_gateway_health_check_failure,
|
||||||
|
|
@ -69,15 +69,29 @@ def gateway_health_check_task() -> None:
|
||||||
result = await session.execute(select(ExternalChatAccount))
|
result = await session.execute(select(ExternalChatAccount))
|
||||||
accounts = list(result.scalars())
|
accounts = list(result.scalars())
|
||||||
for account in accounts:
|
for account in accounts:
|
||||||
token = account_token(account)
|
|
||||||
if not token or account.platform != ExternalChatPlatform.TELEGRAM:
|
|
||||||
continue
|
|
||||||
try:
|
try:
|
||||||
metadata = await TelegramAdapter(token).validate_credentials()
|
bundle = resolve_platform_bundle(account)
|
||||||
|
metadata = await bundle.adapter.validate_credentials()
|
||||||
account.health_status = ExternalChatHealthStatus.OK
|
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:
|
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
|
account.health_status = ExternalChatHealthStatus.FAILING
|
||||||
record_gateway_health_check_failure(platform=account.platform.value)
|
record_gateway_health_check_failure(platform=account.platform.value)
|
||||||
account.last_health_check_at = datetime.now(UTC)
|
account.last_health_check_at = datetime.now(UTC)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue