submitForm now enqueues two kinds of notifications, gated per the design:
- form_submission_received: ONLY when the submitter is authenticated
(members or public_auth visibility). Recipient is the verified auth
email (user.email), never the typed body.email. Anonymous public
submissions skip the confirmation because confirming to a typed
address is a victim-spam vector.
- form_submission_admin: one row per member who holds forms.manage in
the space (mirrors the forms-guard / forms RLS gate). Dedupe by
(submission, admin) so a replay is a no-op.
Finding all permission holders without N round-trips needs a new SQL
helper. Migration 047 adds members_with_permission(sid, perm): the
inverted, set-returning form of user_has_permission, with the same
current/late status gate (046) and the same admin shortcut +
space_role_permissions / user_effective_roles fallback. Additive,
introduces no new tables or policies; existing callers unchanged.
Mirrored into schema.sql; types/database.ts updated.
renderFormEmail + formDedupeKey live in notifications-logic; 6 new
render/dedupe unit tests cover both types, fallback labels, escaping.
Suite 565, build clean.
38 lines
1.9 KiB
PL/PgSQL
38 lines
1.9 KiB
PL/PgSQL
-- =============================================================================
|
|
-- 047: members_with_permission helper (Product spine Phase 4 notifications)
|
|
-- =============================================================================
|
|
-- The dues outbox is member-keyed: one recipient per row. The new form-
|
|
-- submission "admin alert" fans out: one outbox row per recipient member who
|
|
-- holds a given permission (e.g. forms.manage). Today the only available
|
|
-- function is user_has_permission(uid, sid, perm): a per-user check. Iterating
|
|
-- it across every member of a space would be N round-trips for every form
|
|
-- submit -- bad latency, bad scaling.
|
|
--
|
|
-- members_with_permission(sid, perm) is the inverted, set-returning version of
|
|
-- user_has_permission. Same membership-status gate (current/late only, mirrors
|
|
-- migration 046), same admin-shortcut, same space_role_permissions +
|
|
-- user_effective_roles fallback. Additive: it adds nothing to RLS surface,
|
|
-- introduces no new tables/policies, and existing callers continue to use
|
|
-- user_has_permission unchanged.
|
|
--
|
|
-- Idempotent: CREATE OR REPLACE. Apply as-is (Supabase SQL editor / psql);
|
|
-- re-runs are no-ops.
|
|
-- =============================================================================
|
|
|
|
CREATE OR REPLACE FUNCTION public.members_with_permission(sid uuid, perm text)
|
|
RETURNS TABLE(member_id uuid) LANGUAGE sql STABLE SECURITY DEFINER SET search_path = public
|
|
AS $$
|
|
SELECT sm.id
|
|
FROM public.space_members sm
|
|
WHERE sm.space_id = sid
|
|
AND sm.status IN ('current','late') -- privilege-eligible only, mirrors 046
|
|
AND (
|
|
sm.role = 'admin' -- admins always pass (matches user_has_permission)
|
|
OR EXISTS (
|
|
SELECT 1 FROM public.space_role_permissions p
|
|
WHERE p.space_id = sid AND p.permission = perm
|
|
AND p.subject IN (SELECT public.user_effective_roles(sm.user_id, sid))
|
|
)
|
|
);
|
|
$$;
|