mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-06-04 20:05:16 +02:00
refactor(gateway): rename persistence models to external chat
This commit is contained in:
parent
f2d82234d4
commit
a57b741d5e
8 changed files with 274 additions and 323 deletions
|
|
@ -1,4 +1,4 @@
|
|||
"""Gateway account helpers."""
|
||||
"""External chat account helpers."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
|
@ -7,16 +7,16 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
|||
|
||||
from app.config import config
|
||||
from app.db import (
|
||||
GatewayAccountMode,
|
||||
GatewayHealthStatus,
|
||||
GatewayPlatform,
|
||||
GatewayPlatformAccount,
|
||||
ExternalChatAccountMode,
|
||||
ExternalChatHealthStatus,
|
||||
ExternalChatPlatform,
|
||||
ExternalChatAccount,
|
||||
)
|
||||
from app.utils.oauth_security import TokenEncryption
|
||||
|
||||
|
||||
def account_token(account: GatewayPlatformAccount) -> str | None:
|
||||
if account.is_system_account and account.platform == GatewayPlatform.TELEGRAM:
|
||||
def account_token(account: ExternalChatAccount) -> str | None:
|
||||
if account.is_system_account and account.platform == ExternalChatPlatform.TELEGRAM:
|
||||
return config.TELEGRAM_SHARED_BOT_TOKEN
|
||||
if not account.encrypted_credentials:
|
||||
return None
|
||||
|
|
@ -27,26 +27,24 @@ def account_token(account: GatewayPlatformAccount) -> str | None:
|
|||
|
||||
async def get_or_create_system_telegram_account(
|
||||
session: AsyncSession,
|
||||
) -> GatewayPlatformAccount:
|
||||
) -> ExternalChatAccount:
|
||||
result = await session.execute(
|
||||
select(GatewayPlatformAccount).where(
|
||||
GatewayPlatformAccount.platform == GatewayPlatform.TELEGRAM,
|
||||
GatewayPlatformAccount.is_system_account.is_(True),
|
||||
select(ExternalChatAccount).where(
|
||||
ExternalChatAccount.platform == ExternalChatPlatform.TELEGRAM,
|
||||
ExternalChatAccount.is_system_account.is_(True),
|
||||
)
|
||||
)
|
||||
account = result.scalars().first()
|
||||
if account is not None:
|
||||
return account
|
||||
account = GatewayPlatformAccount(
|
||||
platform=GatewayPlatform.TELEGRAM,
|
||||
mode=GatewayAccountMode.CLOUD_SHARED,
|
||||
account = ExternalChatAccount(
|
||||
platform=ExternalChatPlatform.TELEGRAM,
|
||||
mode=ExternalChatAccountMode.CLOUD_SHARED,
|
||||
is_system_account=True,
|
||||
account_metadata={
|
||||
"bot_username": config.TELEGRAM_SHARED_BOT_USERNAME,
|
||||
"webhook_secret": config.TELEGRAM_WEBHOOK_SECRET,
|
||||
},
|
||||
bot_username=config.TELEGRAM_SHARED_BOT_USERNAME,
|
||||
webhook_secret=config.TELEGRAM_WEBHOOK_SECRET,
|
||||
cursor_state={},
|
||||
health_status=GatewayHealthStatus.UNKNOWN,
|
||||
health_status=ExternalChatHealthStatus.UNKNOWN,
|
||||
)
|
||||
session.add(account)
|
||||
await session.flush()
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
"""Authorization invariants for gateway-routed turns."""
|
||||
"""Authorization invariants for external-chat-routed turns."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import HTTPException
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.db import GatewayConversationBinding, Permission, User
|
||||
from app.db import ExternalChatBinding, Permission, User
|
||||
from app.gateway.bindings import suspend_binding
|
||||
from app.observability.metrics import record_gateway_auth_invariant_failure
|
||||
from app.utils.rbac import check_permission, check_search_space_access
|
||||
|
|
@ -19,7 +19,7 @@ class GatewaySuspendedError(RuntimeError):
|
|||
|
||||
async def _fail(
|
||||
session: AsyncSession,
|
||||
binding: GatewayConversationBinding,
|
||||
binding: ExternalChatBinding,
|
||||
reason: str,
|
||||
) -> None:
|
||||
suspend_binding(binding, reason)
|
||||
|
|
@ -30,7 +30,7 @@ async def _fail(
|
|||
|
||||
async def assert_authorization_invariant(
|
||||
session: AsyncSession,
|
||||
binding: GatewayConversationBinding,
|
||||
binding: ExternalChatBinding,
|
||||
) -> User:
|
||||
if binding.state != "bound":
|
||||
await _fail(session, binding, "binding_not_bound")
|
||||
|
|
@ -46,7 +46,7 @@ async def assert_authorization_invariant(
|
|||
user,
|
||||
binding.search_space_id,
|
||||
Permission.CHATS_CREATE.value,
|
||||
"Gateway owner no longer has permission to chat in this search space",
|
||||
"External chat owner no longer has permission to chat in this search space",
|
||||
)
|
||||
except HTTPException as exc:
|
||||
await _fail(session, binding, f"rbac_{exc.status_code}")
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
"""Gateway binding helpers."""
|
||||
"""External chat binding helpers."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
|
@ -9,19 +9,19 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
|||
|
||||
from app.db import (
|
||||
ChatVisibility,
|
||||
GatewayBindingState,
|
||||
GatewayConversationBinding,
|
||||
ExternalChatBindingState,
|
||||
ExternalChatBinding,
|
||||
NewChatThread,
|
||||
)
|
||||
|
||||
|
||||
async def get_or_create_thread_for_binding(
|
||||
session: AsyncSession,
|
||||
binding: GatewayConversationBinding,
|
||||
binding: ExternalChatBinding,
|
||||
) -> NewChatThread:
|
||||
if binding.active_thread_id is not None:
|
||||
if binding.new_chat_thread_id is not None:
|
||||
result = await session.execute(
|
||||
select(NewChatThread).where(NewChatThread.id == binding.active_thread_id)
|
||||
select(NewChatThread).where(NewChatThread.id == binding.new_chat_thread_id)
|
||||
)
|
||||
thread = result.scalars().first()
|
||||
if thread is not None and not thread.archived:
|
||||
|
|
@ -33,30 +33,30 @@ async def get_or_create_thread_for_binding(
|
|||
created_by_id=binding.user_id,
|
||||
visibility=ChatVisibility.PRIVATE,
|
||||
source="telegram",
|
||||
binding_id=binding.id,
|
||||
external_chat_binding_id=binding.id,
|
||||
)
|
||||
session.add(thread)
|
||||
await session.flush()
|
||||
binding.active_thread_id = thread.id
|
||||
binding.new_chat_thread_id = thread.id
|
||||
return thread
|
||||
|
||||
|
||||
def suspend_binding(binding: GatewayConversationBinding, reason: str) -> None:
|
||||
def suspend_binding(binding: ExternalChatBinding, reason: str) -> None:
|
||||
now = datetime.now(UTC)
|
||||
binding.state = GatewayBindingState.SUSPENDED
|
||||
binding.state = ExternalChatBindingState.SUSPENDED
|
||||
binding.suspended_at = now
|
||||
binding.suspended_reason = reason
|
||||
|
||||
|
||||
def revoke_binding(binding: GatewayConversationBinding) -> None:
|
||||
def revoke_binding(binding: ExternalChatBinding) -> None:
|
||||
now = datetime.now(UTC)
|
||||
binding.state = GatewayBindingState.REVOKED
|
||||
binding.state = ExternalChatBindingState.REVOKED
|
||||
binding.revoked_at = now
|
||||
binding.active_thread_id = None
|
||||
binding.new_chat_thread_id = None
|
||||
|
||||
|
||||
def resume_binding(binding: GatewayConversationBinding) -> None:
|
||||
binding.state = GatewayBindingState.BOUND
|
||||
def resume_binding(binding: ExternalChatBinding) -> None:
|
||||
binding.state = ExternalChatBindingState.BOUND
|
||||
binding.suspended_at = None
|
||||
binding.suspended_reason = None
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from __future__ import annotations
|
|||
from sqlalchemy.dialects.postgresql import insert
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.db import GatewayInboundEvent, GatewayPlatform
|
||||
from app.db import ExternalChatInboundEvent, ExternalChatPlatform
|
||||
|
||||
|
||||
def telegram_event_dedupe_key(update_id: int | str) -> str:
|
||||
|
|
@ -16,15 +16,16 @@ async def persist_inbound_event(
|
|||
session: AsyncSession,
|
||||
*,
|
||||
account_id: int,
|
||||
platform: GatewayPlatform,
|
||||
platform: ExternalChatPlatform,
|
||||
event_dedupe_key: str,
|
||||
event_kind: str,
|
||||
raw_payload: dict,
|
||||
external_event_id: str | None = None,
|
||||
external_message_id: str | None = None,
|
||||
request_id: str | None = None,
|
||||
) -> int | None:
|
||||
stmt = (
|
||||
insert(GatewayInboundEvent)
|
||||
insert(ExternalChatInboundEvent)
|
||||
.values(
|
||||
account_id=account_id,
|
||||
platform=platform,
|
||||
|
|
@ -33,11 +34,12 @@ async def persist_inbound_event(
|
|||
external_message_id=external_message_id,
|
||||
event_kind=event_kind,
|
||||
raw_payload=raw_payload,
|
||||
request_id=request_id,
|
||||
)
|
||||
.on_conflict_do_nothing(
|
||||
index_elements=["account_id", "event_dedupe_key"],
|
||||
)
|
||||
.returning(GatewayInboundEvent.id)
|
||||
.returning(ExternalChatInboundEvent.id)
|
||||
)
|
||||
result = await session.execute(stmt)
|
||||
return result.scalar_one_or_none()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
"""Pairing code lifecycle for gateway bindings."""
|
||||
"""Pairing code lifecycle for external chat bindings."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
|
@ -8,7 +8,7 @@ from datetime import UTC, datetime, timedelta
|
|||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.db import GatewayBindingState, GatewayConversationBinding
|
||||
from app.db import ExternalChatBindingState, ExternalChatBinding
|
||||
|
||||
PAIRING_CODE_TTL = timedelta(minutes=10)
|
||||
|
||||
|
|
@ -30,19 +30,19 @@ async def redeem_pairing_code(
|
|||
external_display_name: str | None,
|
||||
external_username: str | None,
|
||||
external_metadata: dict | None = None,
|
||||
) -> GatewayConversationBinding | None:
|
||||
) -> ExternalChatBinding | None:
|
||||
result = await session.execute(
|
||||
select(GatewayConversationBinding).where(
|
||||
GatewayConversationBinding.pairing_code == code,
|
||||
GatewayConversationBinding.state == GatewayBindingState.PENDING,
|
||||
GatewayConversationBinding.pairing_code_expires_at > datetime.now(UTC),
|
||||
select(ExternalChatBinding).where(
|
||||
ExternalChatBinding.pairing_code == code,
|
||||
ExternalChatBinding.state == ExternalChatBindingState.PENDING,
|
||||
ExternalChatBinding.pairing_code_expires_at > datetime.now(UTC),
|
||||
)
|
||||
)
|
||||
binding = result.scalars().first()
|
||||
if binding is None:
|
||||
return None
|
||||
|
||||
binding.state = GatewayBindingState.BOUND
|
||||
binding.state = ExternalChatBindingState.BOUND
|
||||
binding.pairing_code = None
|
||||
binding.pairing_code_expires_at = None
|
||||
binding.external_peer_id = external_peer_id
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue