上一版按"webhook + URL 里带令牌"自己编了一套,用户当场指出微信必须先扫码配对。 现在照腾讯 iLink Bot 协议自己做客户端:取二维码 → 手机扫码确认 → 存加密登录态 → 后端长轮询收消息 → 用 context_token 回话。独立实现,不依赖 DSH 的运行环境。 后端 - wechat_ilink.py:get_bot_qrcode / get_qrcode_status / getupdates / sendmessage, bot_token 用 Fernet(密钥由 JWT_SECRET 派生)加密入库,日志与接口都不回明文 - wechat_intake.py:一句话 → 任务或知识草稿,前缀识别、#标签、首行标题 - wechat_bot.py:每条通道一个轮询任务,启动时按 status='active' 恢复; message_id 去重;errcode -14 转 expired 让用户重新扫 - api/integrations.py 重写成扫码三接口 + 停用/断开/最近消息 - 迁移 0013(登录态列)、0014(轮询入口按 app.bot_channel 主键的 RLS 口子) Web - 接入页改成扫码流程:页面里画出二维码、每 2 秒问一次状态、连上显示微信用户与最近消息 验证 - pytest 149 passed(新增 test_wechat_bot 9 例) - ruff check / ruff format --check 干净;tsc 0;vite build ✓ - web/e2e/integrations.mjs 7 项 ALL PASS(拿的是微信真二维码) 教训:接入第三方协议先去已有实现里找协议,别从直觉倒推。
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
"""轮询任务读通道需要一条 RLS 口子
|
||||
|
||||
Revision ID: 0014
|
||||
Revises: 0013
|
||||
Create Date: 2026-11-05
|
||||
|
||||
后台轮询是"从 channel_id 开始"的:它还没有工作区上下文,而 `bot_channel` 的策略要求
|
||||
`workspace_id = app_ws_id()`,直接 SELECT 会一行都看不到(先有鸡还是先有蛋)。
|
||||
|
||||
和 0012 那条 `token_hash = app_bot_token_hash()` 同理,这里再放一条**只按主键命中自己**
|
||||
的口子:任务在读任何东西之前先把 `app.bot_channel` 设成本次要处理的通道 id,
|
||||
策略允许它看见那一行,其余行照旧不可见。读到 workspace_id 之后立刻 `bind_tenant`,
|
||||
后面所有查询都回到正常的租户上下文里。
|
||||
|
||||
两条策略是 permissive 关系(PG 会 OR 起来),所以原来的隔离策略不用改。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
from alembic import op
|
||||
|
||||
from app.roles import APP_ROLE
|
||||
|
||||
revision: str = "0014"
|
||||
down_revision: str = "0013"
|
||||
branch_labels: Sequence[str] | None = None
|
||||
depends_on: Sequence[str] | None = None
|
||||
|
||||
CREATE_POLICY = """
|
||||
CREATE POLICY bot_channel_worker ON bot_channel
|
||||
FOR ALL
|
||||
USING (id = current_setting('app.bot_channel', true)::uuid)
|
||||
WITH CHECK (id = current_setting('app.bot_channel', true)::uuid)
|
||||
"""
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.execute("DROP POLICY IF EXISTS bot_channel_worker ON bot_channel")
|
||||
op.execute(CREATE_POLICY)
|
||||
op.execute(f"GRANT SELECT, INSERT, UPDATE, DELETE ON bot_channel TO {APP_ROLE}")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.execute("DROP POLICY IF EXISTS bot_channel_worker ON bot_channel")
|
||||
Reference in New Issue
Block a user