Primeiro commit

This commit is contained in:
joao.herculano
2026-08-06 11:13:33 -03:00
commit 4e1ebe861c
110 changed files with 18052 additions and 0 deletions
@@ -0,0 +1,20 @@
CREATE TABLE public.coletas (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
usuario TEXT NOT NULL,
codigo_barras TEXT NOT NULL,
inicio_coleta TIMESTAMPTZ NOT NULL DEFAULT now(),
fim_coleta TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
GRANT SELECT, INSERT, UPDATE ON public.coletas TO anon;
GRANT SELECT, INSERT, UPDATE, DELETE ON public.coletas TO authenticated;
GRANT ALL ON public.coletas TO service_role;
ALTER TABLE public.coletas ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Coletas sao visiveis publicamente" ON public.coletas FOR SELECT TO anon, authenticated USING (true);
CREATE POLICY "Qualquer um pode criar coleta" ON public.coletas FOR INSERT TO anon, authenticated WITH CHECK (true);
CREATE POLICY "Qualquer um pode atualizar coleta" ON public.coletas FOR UPDATE TO anon, authenticated USING (true) WITH CHECK (true);
CREATE INDEX idx_coletas_usuario_abertas ON public.coletas (usuario) WHERE fim_coleta IS NULL;
@@ -0,0 +1,141 @@
-- Perfis
CREATE TABLE public.perfis (
id uuid PRIMARY KEY,
email text NOT NULL,
aprovado boolean NOT NULL DEFAULT false,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
GRANT SELECT, INSERT, UPDATE ON public.perfis TO authenticated;
GRANT ALL ON public.perfis TO service_role;
ALTER TABLE public.perfis ENABLE ROW LEVEL SECURITY;
-- Papeis
CREATE TYPE public.app_role AS ENUM ('admin', 'user');
CREATE TABLE public.papeis_usuario (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
user_id uuid NOT NULL,
role public.app_role NOT NULL,
created_at timestamptz NOT NULL DEFAULT now(),
UNIQUE (user_id, role)
);
GRANT SELECT ON public.papeis_usuario TO authenticated;
GRANT ALL ON public.papeis_usuario TO service_role;
ALTER TABLE public.papeis_usuario ENABLE ROW LEVEL SECURITY;
CREATE OR REPLACE FUNCTION public.has_role(_user_id uuid, _role public.app_role)
RETURNS boolean
LANGUAGE sql
STABLE
SECURITY DEFINER
SET search_path = public
AS $$
SELECT EXISTS (
SELECT 1 FROM public.papeis_usuario WHERE user_id = _user_id AND role = _role
)
$$;
CREATE OR REPLACE FUNCTION public.is_aprovado(_user_id uuid)
RETURNS boolean
LANGUAGE sql
STABLE
SECURITY DEFINER
SET search_path = public
AS $$
SELECT EXISTS (
SELECT 1 FROM public.perfis WHERE id = _user_id AND aprovado = true
)
$$;
-- Policies perfis
CREATE POLICY "Ver proprio perfil" ON public.perfis
FOR SELECT TO authenticated USING (id = auth.uid());
CREATE POLICY "Admins veem todos os perfis" ON public.perfis
FOR SELECT TO authenticated USING (public.has_role(auth.uid(), 'admin'));
CREATE POLICY "Criar proprio perfil" ON public.perfis
FOR INSERT TO authenticated WITH CHECK (id = auth.uid());
CREATE POLICY "Admins atualizam perfis" ON public.perfis
FOR UPDATE TO authenticated
USING (public.has_role(auth.uid(), 'admin'))
WITH CHECK (public.has_role(auth.uid(), 'admin'));
-- Policies papeis
CREATE POLICY "Ver proprios papeis" ON public.papeis_usuario
FOR SELECT TO authenticated USING (user_id = auth.uid());
CREATE POLICY "Admins veem todos os papeis" ON public.papeis_usuario
FOR SELECT TO authenticated USING (public.has_role(auth.uid(), 'admin'));
-- Registro de perfil: primeiro usuario vira admin aprovado
CREATE OR REPLACE FUNCTION public.registrar_perfil()
RETURNS public.perfis
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
v_uid uuid := auth.uid();
v_email text;
v_primeiro boolean;
v_perfil public.perfis;
BEGIN
IF v_uid IS NULL THEN
RAISE EXCEPTION 'Nao autenticado';
END IF;
SELECT * INTO v_perfil FROM public.perfis WHERE id = v_uid;
IF FOUND THEN
RETURN v_perfil;
END IF;
SELECT email INTO v_email FROM auth.users WHERE id = v_uid;
SELECT NOT EXISTS (SELECT 1 FROM public.perfis) INTO v_primeiro;
INSERT INTO public.perfis (id, email, aprovado)
VALUES (v_uid, coalesce(v_email, ''), v_primeiro)
RETURNING * INTO v_perfil;
IF v_primeiro THEN
INSERT INTO public.papeis_usuario (user_id, role) VALUES (v_uid, 'admin')
ON CONFLICT DO NOTHING;
END IF;
RETURN v_perfil;
END;
$$;
GRANT EXECUTE ON FUNCTION public.registrar_perfil() TO authenticated;
CREATE OR REPLACE FUNCTION public.set_updated_at()
RETURNS TRIGGER
LANGUAGE plpgsql
SET search_path = public
AS $$
BEGIN
NEW.updated_at = now();
RETURN NEW;
END;
$$;
CREATE TRIGGER perfis_updated_at BEFORE UPDATE ON public.perfis
FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
-- Coletas: apenas usuarios logados e aprovados
DROP POLICY IF EXISTS "Coletas sao visiveis publicamente" ON public.coletas;
DROP POLICY IF EXISTS "Qualquer um pode atualizar coleta" ON public.coletas;
DROP POLICY IF EXISTS "Qualquer um pode criar coleta" ON public.coletas;
REVOKE ALL ON public.coletas FROM anon;
GRANT SELECT, INSERT, UPDATE ON public.coletas TO authenticated;
GRANT ALL ON public.coletas TO service_role;
CREATE POLICY "Aprovados veem coletas" ON public.coletas
FOR SELECT TO authenticated USING (public.is_aprovado(auth.uid()));
CREATE POLICY "Aprovados criam coletas" ON public.coletas
FOR INSERT TO authenticated WITH CHECK (public.is_aprovado(auth.uid()));
CREATE POLICY "Aprovados atualizam coletas" ON public.coletas
FOR UPDATE TO authenticated
USING (public.is_aprovado(auth.uid()))
WITH CHECK (public.is_aprovado(auth.uid()));
@@ -0,0 +1,8 @@
REVOKE ALL ON FUNCTION public.has_role(uuid, public.app_role) FROM PUBLIC, anon;
REVOKE ALL ON FUNCTION public.is_aprovado(uuid) FROM PUBLIC, anon;
REVOKE ALL ON FUNCTION public.registrar_perfil() FROM PUBLIC, anon;
REVOKE ALL ON FUNCTION public.set_updated_at() FROM PUBLIC, anon, authenticated;
GRANT EXECUTE ON FUNCTION public.has_role(uuid, public.app_role) TO authenticated;
GRANT EXECUTE ON FUNCTION public.is_aprovado(uuid) TO authenticated;
GRANT EXECUTE ON FUNCTION public.registrar_perfil() TO authenticated;
@@ -0,0 +1,46 @@
ALTER TABLE public.perfis ADD COLUMN IF NOT EXISTS nome text;
UPDATE public.perfis SET nome = split_part(email, '@', 1) WHERE nome IS NULL;
ALTER TABLE public.perfis ALTER COLUMN nome SET NOT NULL;
CREATE UNIQUE INDEX IF NOT EXISTS perfis_nome_unico ON public.perfis (lower(nome));
CREATE OR REPLACE FUNCTION public.registrar_perfil()
RETURNS perfis
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path TO 'public'
AS $function$
DECLARE
v_uid uuid := auth.uid();
v_email text;
v_nome text;
v_primeiro boolean;
v_perfil public.perfis;
BEGIN
IF v_uid IS NULL THEN
RAISE EXCEPTION 'Nao autenticado';
END IF;
SELECT * INTO v_perfil FROM public.perfis WHERE id = v_uid;
IF FOUND THEN
RETURN v_perfil;
END IF;
SELECT email INTO v_email FROM auth.users WHERE id = v_uid;
v_nome := split_part(coalesce(v_email, ''), '@', 1);
SELECT NOT EXISTS (SELECT 1 FROM public.perfis) INTO v_primeiro;
INSERT INTO public.perfis (id, email, nome, aprovado)
VALUES (v_uid, coalesce(v_email, ''), v_nome, v_primeiro)
RETURNING * INTO v_perfil;
IF v_primeiro THEN
INSERT INTO public.papeis_usuario (user_id, role) VALUES (v_uid, 'admin')
ON CONFLICT DO NOTHING;
END IF;
RETURN v_perfil;
END;
$function$;
@@ -0,0 +1,4 @@
REVOKE ALL ON FUNCTION public.has_role(uuid, public.app_role) FROM PUBLIC, anon, authenticated;
REVOKE ALL ON FUNCTION public.is_aprovado(uuid) FROM PUBLIC, anon, authenticated;
REVOKE ALL ON FUNCTION public.registrar_perfil() FROM PUBLIC, anon;
GRANT EXECUTE ON FUNCTION public.registrar_perfil() TO authenticated;
@@ -0,0 +1,58 @@
CREATE SCHEMA IF NOT EXISTS private;
REVOKE ALL ON SCHEMA private FROM PUBLIC, anon, authenticated;
GRANT USAGE ON SCHEMA private TO postgres, service_role;
CREATE OR REPLACE FUNCTION private.has_role(_user_id uuid, _role public.app_role)
RETURNS boolean LANGUAGE sql STABLE SECURITY DEFINER SET search_path TO 'public'
AS $$ SELECT EXISTS (SELECT 1 FROM public.papeis_usuario WHERE user_id = _user_id AND role = _role) $$;
CREATE OR REPLACE FUNCTION private.is_aprovado(_user_id uuid)
RETURNS boolean LANGUAGE sql STABLE SECURITY DEFINER SET search_path TO 'public'
AS $$ SELECT EXISTS (SELECT 1 FROM public.perfis WHERE id = _user_id AND aprovado = true) $$;
DROP POLICY "Admins veem todos os papeis" ON public.papeis_usuario;
CREATE POLICY "Admins veem todos os papeis" ON public.papeis_usuario FOR SELECT TO authenticated USING (private.has_role(auth.uid(), 'admin'::public.app_role));
DROP POLICY "Admins atualizam perfis" ON public.perfis;
CREATE POLICY "Admins atualizam perfis" ON public.perfis FOR UPDATE TO authenticated USING (private.has_role(auth.uid(), 'admin'::public.app_role)) WITH CHECK (private.has_role(auth.uid(), 'admin'::public.app_role));
DROP POLICY "Admins veem todos os perfis" ON public.perfis;
CREATE POLICY "Admins veem todos os perfis" ON public.perfis FOR SELECT TO authenticated USING (private.has_role(auth.uid(), 'admin'::public.app_role));
DROP POLICY "Aprovados atualizam coletas" ON public.coletas;
CREATE POLICY "Aprovados atualizam coletas" ON public.coletas FOR UPDATE TO authenticated USING (private.is_aprovado(auth.uid())) WITH CHECK (private.is_aprovado(auth.uid()));
DROP POLICY "Aprovados criam coletas" ON public.coletas;
CREATE POLICY "Aprovados criam coletas" ON public.coletas FOR INSERT TO authenticated WITH CHECK (private.is_aprovado(auth.uid()));
DROP POLICY "Aprovados veem coletas" ON public.coletas;
CREATE POLICY "Aprovados veem coletas" ON public.coletas FOR SELECT TO authenticated USING (private.is_aprovado(auth.uid()));
DROP FUNCTION IF EXISTS public.has_role(uuid, public.app_role);
DROP FUNCTION IF EXISTS public.is_aprovado(uuid);
CREATE OR REPLACE FUNCTION private.registrar_perfil_para(_uid uuid, _email text)
RETURNS void LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public'
AS $$
DECLARE v_primeiro boolean;
BEGIN
IF EXISTS (SELECT 1 FROM public.perfis WHERE id = _uid) THEN RETURN; END IF;
SELECT NOT EXISTS (SELECT 1 FROM public.perfis) INTO v_primeiro;
INSERT INTO public.perfis (id, email, nome, aprovado)
VALUES (_uid, coalesce(_email, ''), split_part(coalesce(_email, ''), '@', 1), v_primeiro);
IF v_primeiro THEN
INSERT INTO public.papeis_usuario (user_id, role) VALUES (_uid, 'admin') ON CONFLICT DO NOTHING;
END IF;
END;
$$;
CREATE OR REPLACE FUNCTION private.on_auth_user_created()
RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public'
AS $$
BEGIN
PERFORM private.registrar_perfil_para(NEW.id, NEW.email);
RETURN NEW;
END;
$$;
DROP FUNCTION IF EXISTS public.registrar_perfil();