mirror of
https://github.com/Luchexx/wacrm.git
synced 2026-11-02 09:19:51 +00:00
fix(ai): grant EXECUTE on claim_ai_reply_slot so auto-reply works (#345)
Migration 029 created claim_ai_reply_slot(uuid, integer) as SECURITY
DEFINER but never GRANTed EXECUTE — the only function in the schema
missing its grant (007/012/017/018/019/025/030 all grant theirs).
SECURITY DEFINER sets the privileges a function runs *with*, not who may
*call* it: the caller still needs EXECUTE. On instances where the default
PUBLIC execute privilege on public-schema functions has been revoked
(hardened / self-hosted Supabase), the service_role that PostgREST uses
cannot invoke it. The AI auto-reply path runs entirely under the
service-role client (the inbound webhook has no auth.uid()), so
db.rpc('claim_ai_reply_slot') fails permission-denied, the dispatch bails
before sending, and the bot silently never answers ANY inbound message.
The Playground works because it never claims a slot — matching the report.
- 031_ai_reply_slot_grant.sql: new migration granting EXECUTE to
service_role (029 is already applied on existing installs, so it needs
its own migration to reach them). service_role only — the sole caller —
matching the 007/012 increment-counter precedent.
- 029_ai_reply.sql: add the same grant next to the definition so fresh
installs are correct.
- auto-reply.ts: log a real claim RPC error instead of returning
silently, so this class of deploy failure is diagnosable.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
5dbc786fdc
commit
95faf58e75
@ -122,7 +122,15 @@ export async function dispatchInboundToAiReply(
|
||||
max_replies: config.autoReplyMaxPerConversation,
|
||||
},
|
||||
)
|
||||
if (claimErr || claimed !== true) return
|
||||
if (claimErr) {
|
||||
// A real error here (vs. losing the cap race) is almost always a
|
||||
// deploy issue — e.g. `claim_ai_reply_slot` not EXECUTE-able by the
|
||||
// service role, or the migration not applied. Log it loudly: a
|
||||
// silent return makes "auto-reply never fires" undiagnosable.
|
||||
console.error('[ai auto-reply] claim_ai_reply_slot failed:', claimErr)
|
||||
return
|
||||
}
|
||||
if (claimed !== true) return // lost the per-conversation cap race
|
||||
|
||||
await engineSendText({
|
||||
accountId,
|
||||
|
||||
@ -129,3 +129,13 @@ RETURNS boolean AS $$
|
||||
)
|
||||
SELECT EXISTS (SELECT 1 FROM claimed);
|
||||
$$ LANGUAGE sql SECURITY DEFINER SET search_path = public;
|
||||
|
||||
-- The auto-reply bot claims slots under the service-role client (the
|
||||
-- inbound webhook has no auth.uid()), so it needs EXECUTE. SECURITY
|
||||
-- DEFINER alone is not enough — it sets the privileges the function runs
|
||||
-- *with*, not who may call it. Without this grant the RPC fails with
|
||||
-- permission-denied on instances where the default PUBLIC execute
|
||||
-- privilege has been revoked (hardened / self-hosted Supabase), and the
|
||||
-- bot silently never replies. Only the service role claims slots, so we
|
||||
-- grant to it alone (mirrors 007 / 012). See migration 031 / issue #345.
|
||||
GRANT EXECUTE ON FUNCTION public.claim_ai_reply_slot(uuid, integer) TO service_role;
|
||||
|
||||
27
supabase/migrations/031_ai_reply_slot_grant.sql
Normal file
27
supabase/migrations/031_ai_reply_slot_grant.sql
Normal file
@ -0,0 +1,27 @@
|
||||
-- ============================================================
|
||||
-- 031_ai_reply_slot_grant.sql — fix: AI auto-reply never fires
|
||||
--
|
||||
-- Migration 029 created `claim_ai_reply_slot(uuid, integer)` as a
|
||||
-- SECURITY DEFINER function but never GRANTed EXECUTE on it — the only
|
||||
-- function in the schema missing its grant (cf. 007, 012, 018, 019,
|
||||
-- 025, 030, which all grant EXECUTE explicitly).
|
||||
--
|
||||
-- SECURITY DEFINER changes the privileges a function runs *with*, not
|
||||
-- who may *call* it: the caller still needs EXECUTE. On Postgres
|
||||
-- instances where the default PUBLIC execute privilege on public-schema
|
||||
-- functions has been revoked (standard on hardened / self-hosted
|
||||
-- Supabase), `service_role` therefore cannot invoke it. The AI
|
||||
-- auto-reply path runs entirely under the service-role client (the
|
||||
-- inbound webhook has no auth.uid()), so `db.rpc('claim_ai_reply_slot')`
|
||||
-- fails with permission-denied, the caller bails before sending, and the
|
||||
-- bot silently never answers ANY inbound message — while the Playground
|
||||
-- (which never claims a slot) keeps working. See issue #345.
|
||||
--
|
||||
-- Only the service role ever claims a slot, so we grant to it alone —
|
||||
-- matching the increment-counter precedent in 007 / 012, and never
|
||||
-- exposing a counter-mutating function to end users.
|
||||
--
|
||||
-- Idempotent — GRANT is a no-op when the privilege already exists.
|
||||
-- ============================================================
|
||||
|
||||
GRANT EXECUTE ON FUNCTION public.claim_ai_reply_slot(uuid, integer) TO service_role;
|
||||
Loading…
Reference in New Issue
Block a user