01 - Supabase Configuração

Segue abaixo a configuração incial para um Projeto de Frontend para gerenciar arquivos Markdown online.

SQLs

Tabelas Principais do Projeto

-- Tabela de grupos (ex: Financeiro, Suporte, Dev)
create table if not exists public.app_groups (
  id uuid primary key default gen_random_uuid(),
  name text not null unique,
  slug text not null unique -- ex: 'financeiro', 'suporte'
);

-- Relação usuário ↔ grupo
create table if not exists public.app_group_users (
  group_id uuid not null references public.app_groups(id) on delete cascade,
  user_id uuid not null references auth.users(id) on delete cascade,
  primary key (group_id, user_id)
);

-- Tabela de documentos (metadados do markdown)
create table if not exists public.app_docs (
  id uuid primary key default gen_random_uuid(),
  slug text not null unique,    -- ex: 'financeiro/relatorio-mensal'
  title text not null,          -- título amigável
  bucket text not null default 'docs',
  storage_path text not null    -- ex: 'financeiro/relatorio-mensal.md'
);

-- Relação grupo ↔ doc (quem pode ver o quê)
create table if not exists public.app_group_docs (
  group_id uuid not null references public.app_groups(id) on delete cascade,
  doc_id uuid not null references public.app_docs(id) on delete cascade,
  primary key (group_id, doc_id)
);

-- Relação grupo x pastas
-- Dica: sempre termine com `/` pra ficar mais claro (`suporte/`).
create table if not exists public.app_group_folders (
  id uuid primary key default gen_random_uuid(),
  group_id uuid not null references public.app_groups(id) on delete cascade,
  folder_prefix text not null -- ex: 'suporte/' ou 'suporte/guias/'
);

Caso precise dropar

-- Relação grupo x pastas
drop table public.app_group_folders;

-- Relação grupo ↔ doc (quem pode ver o quê)
drop table public.app_group_docs;

-- Tabela de documentos (metadados do markdown)
drop table public.app_docs;

-- Relação usuário ↔ grupo
drop table public.app_group_users;

-- Tabela de grupos (ex: Financeiro, Suporte, Dev)
drop table public.app_groups;

RLS

alter table public.app_docs enable row level security;
alter table public.app_group_users enable row level security;
alter table public.app_group_docs enable row level security;

-- para as pastas
alter table public.app_group_folders enable row level security;
create policy "docs_por_grupo" on public.app_docs
for select
using (
  -- 1) permissão por doc específico
  exists (
    select 1
    from public.app_group_docs gd
    join public.app_group_users gu
      on gu.group_id = gd.group_id
    where gd.doc_id = app_docs.id
      and gu.user_id = auth.uid()
  )
  OR
  -- 2) permissão por pasta (prefixo do slug)
  exists (
    select 1
    from public.app_group_folders gf
    join public.app_group_users gu
      on gu.group_id = gf.group_id
    where gu.user_id = auth.uid()
      and app_docs.slug like gf.folder_prefix || '%'
  )
);

Cadastrar Alguns grupos, docs e permissões

Ainda no SQL Editor:

insert into public.app_groups (name, slug)
values ('CRMFK', 'crmfk'),
	   ('SESMT', 'sesmt'),       
       ('Desenvolvimento', 'desenvolvimento'),
       ('Suporte', 'suporte'),
       ('SESMT-CLIENTE', 'sesmt-cliente'),
       ('CRMFK-CLIENTE', 'crmfk-cliente')
on conflict (slug) do nothing;

Criar docs (batendo com os arquivos do Storage)

insert into public.app_docs (slug, title, bucket, storage_path)
values 
-- sesmt
  ('sesmt/readme', 'Página Inicial', 'docs', 'SESMT/README.md'),  
  ('sesmt/01-projeto-sesmt', '01 - Projeto SESMT', 'docs', 'SESMT/01 - Projeto SESMT.md'),
    ('sesmt/02-requisitos-funcionais-e-nao-funcionais', '02 - Requisitos Funcionais e Não Funcionais', 'docs', 'SESMT/02 - Requisitos Funcionais e Não Funcionais.md'),  

-- sesmt-cliente  
  ('sesmt-cliente/readme', 'Página Inicial', 'docs', 'SESMT-CLIENTE/README.md'),  
  ('sesmt-cliente/01-projeto-sesmt', '01 - Projeto SESMT', 'docs', 'SESMT-CLIENTE/01 - Projeto SESMT.md'),
  ('sesmt-cliente/02-requisitos-funcionais-e-nao-funcionais', '02 - Requisitos Funcionais e Não Funcionais', 'docs', 'SESMT-CLIENTE/02 - Requisitos Funcionais e Não Funcionais.md'),
  
-- crmfk  
  ('crmfk/readme', 'Página Inicial', 'docs', 'CRMFK/README.md'),  
  ('crmfk/checklist-do-projeto', 'Checklist do Projeto', 'docs', 'CRMFK/Checklist do projeto.md'),    

-- crmfk-cliente
  ('crmfk-cliente/readme', 'Página Inicial', 'docs', 'CRMFK-CLIENTE/README.md'),  
  ('crmfk-cliente/checklist-do-projeto', 'Checklist do Projeto', 'docs', 'CRMFK-CLIENTE/Checklist do projeto.md'),    

-- desenvolvimento  
  ('desenvolvimento/faq', 'FAQ Desenvolvimento', 'docs', 'Desenvolvimento/faq.md'),
  
-- suporte  
  ('suporte/faq', 'FAQ Suporte', 'docs', 'Suporte/faq.md')

on conflict (slug) do nothing;

Com isso, qualquer consulta select * from app_docs feita via Supabase Client já vem filtrada para o usuário logado.

💡 Exemplo prático:

O like gf.folder_prefix || '%' vira:

app_docs.slug like 'desenvolvimento/' || '%' 
-- equivalente a 'financeiro/%'

Como você cadastra a permissão da pasta

Suponha que você já tenha o grupo desenvolvimento em app_groups:

insert into public.app_group_folders (group_id, folder_prefix)
select id, 'desenvolvimento/'
from public.app_groups
where slug = 'desenvolvimento';

Relacionar grupo vs doc

-- SESMT pode ver o README.md
insert into public.app_group_docs (group_id, doc_id)
select g.id, d.id
from app_groups g, app_docs d
where g.slug = 'sesmt'
  and d.slug = 'sesmt/readme'
on conflict do nothing;

insert into public.app_group_docs (group_id, doc_id)
select g.id, d.id
from app_groups g, app_docs d
where g.slug = 'sesmt'
  and d.slug = 'sesmt/readme'
on conflict do nothing;

-- Dando permissão para o grupo Desenvolvimento ter acesso a tudo
insert into public.app_group_folders (group_id, folder_prefix)
select id, 'Desenvolvimento/'
from public.app_groups
where slug = 'desenvolvimento';

-- Dando permissão para o grupo Suporte ter acesso a tudo
insert into public.app_group_folders (group_id, folder_prefix)
select id, 'Suporte/'
from public.app_groups
where slug = 'suporte';

-- Dando permissão para o grupo SESMT ter acesso a tudo
insert into public.app_group_folders (group_id, folder_prefix)
select id, 'SESMT/'
from public.app_groups
where slug = 'sesmt';

-- Dando permissão para o grupo SESMT-CLIENTE ter acesso a tudo
insert into public.app_group_folders (group_id, folder_prefix)
select id, 'SESMT-CLIENTE/'
from public.app_groups
where slug = 'sesmt-cliente';

-- Dando permissão para o grupo CRMFK ter acesso a tudo
insert into public.app_group_folders (group_id, folder_prefix)
select id, 'CRMFK/'
from public.app_groups
where slug = 'crmfk';

-- Dando permissão para o grupo CRMFK-CLIENTE ter acesso a tudo
insert into public.app_group_folders (group_id, folder_prefix)
select id, 'CRMFK-CLIENTE/'
from public.app_groups
where slug = 'crmfk-cliente';

Dar Privilégios via SQL

Dar acesso à pasta inteira para um grupo

Ex: grupo suporte tem acesso a tudo que comece com suporte/:

insert into public.app_group_folders (group_id, folder_prefix)
select id, 'suporte/'
from public.app_groups
where slug = 'suporte'
on conflict do nothing;

(Opcional) Dar acesso pontual a um doc específico

Ex: doc suporte/faq também pode ser visto pelo grupo financeiro:

insert into public.app_group_docs (group_id, doc_id)
select g.id, d.id
from public.app_groups g, public.app_docs d
where g.slug = 'financeiro'
  and d.slug = 'suporte/faq'
on conflict do nothing;

Vincular usuários aos grupos

Aqui você precisa do UUID do usuário em auth.users.

-- Exemplo: colocar o usuário no grupo "financeiro"
insert into public.app_group_users (group_id, user_id)
select g.id, '<UUID_DO_USUARIO>'
from public.app_groups g
where g.slug = 'financeiro'
on conflict do nothing;

-- Outro exemplo: mesmo usuário também no grupo "suporte"
insert into public.app_group_users (group_id, user_id)
select g.id, '<UUID_DO_USUARIO>'
from public.app_groups g
where g.slug = 'suporte'
on conflict do nothing;

OBS: Substitua '<UUID_DO_USUARIO>' pelo ID real (por ex: d5d8c1c8-...).