"""轮询任务读通道需要一条 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")