do $$ declare cname text; begin select constraint_name into cname from information_schema.table_constraints where table_schema = 'public' and table_name = 'profiles' and constraint_type = 'CHECK' and constraint_name = 'profiles_role_check'; if cname is not null then execute 'alter table public.profiles drop constraint ' || quote_ident(cname); end if; end; $$; alter table public.profiles add constraint profiles_role_check check (role in ('team', 'client', 'external')); create table if not exists public.project_members ( id uuid default gen_random_uuid() primary key, project_id uuid references public.projects(id) on delete cascade not null, profile_id uuid references public.profiles(id) on delete cascade not null, created_at timestamptz default now() not null, unique(project_id, profile_id) ); alter table public.project_members enable row level security; create or replace function public.is_external() returns boolean as $$ select get_my_role() = 'external'; $$ language sql security definer stable; drop policy if exists "Team all project_members" on public.project_members; create policy "Team all project_members" on public.project_members for all using (get_my_role() = 'team'); drop policy if exists "External reads own memberships" on public.project_members; create policy "External reads own memberships" on public.project_members for select using (profile_id = auth.uid()); drop policy if exists "External reads assigned projects" on public.projects; create policy "External reads assigned projects" on public.projects for select using ( get_my_role() = 'external' and id in (select project_id from public.project_members where profile_id = auth.uid()) ); drop policy if exists "External reads assigned tasks" on public.tasks; create policy "External reads assigned tasks" on public.tasks for select using ( get_my_role() = 'external' and project_id in (select project_id from public.project_members where profile_id = auth.uid()) ); drop policy if exists "External updates assigned tasks" on public.tasks; create policy "External updates assigned tasks" on public.tasks for update using ( get_my_role() = 'external' and project_id in (select project_id from public.project_members where profile_id = auth.uid()) ); drop policy if exists "External reads assigned submissions" on public.submissions; create policy "External reads assigned submissions" on public.submissions for select using ( get_my_role() = 'external' and task_id in ( select t.id from public.tasks t join public.project_members pm on pm.project_id = t.project_id where pm.profile_id = auth.uid() ) ); drop policy if exists "External inserts submissions" on public.submissions; create policy "External inserts submissions" on public.submissions for insert with check ( get_my_role() = 'external' and submitted_by = auth.uid() ); drop policy if exists "External reads assigned submission_files" on public.submission_files; create policy "External reads assigned submission_files" on public.submission_files for select using ( get_my_role() = 'external' and submission_id in ( select s.id from public.submissions s join public.tasks t on t.id = s.task_id join public.project_members pm on pm.project_id = t.project_id where pm.profile_id = auth.uid() ) ); drop policy if exists "External inserts submission_files" on public.submission_files; create policy "External inserts submission_files" on public.submission_files for insert with check (get_my_role() = 'external'); drop policy if exists "External reads assigned deliveries" on public.deliveries; create policy "External reads assigned deliveries" on public.deliveries for select using ( get_my_role() = 'external' and submission_id in ( select s.id from public.submissions s join public.tasks t on t.id = s.task_id join public.project_members pm on pm.project_id = t.project_id where pm.profile_id = auth.uid() ) ); drop policy if exists "External reads assigned delivery_files" on public.delivery_files; create policy "External reads assigned delivery_files" on public.delivery_files for select using ( get_my_role() = 'external' and delivery_id in ( select d.id from public.deliveries d join public.submissions s on s.id = d.submission_id join public.tasks t on t.id = s.task_id join public.project_members pm on pm.project_id = t.project_id where pm.profile_id = auth.uid() ) );