142 lines
5.1 KiB
PL/PgSQL
142 lines
5.1 KiB
PL/PgSQL
-- Create user_role enum
|
|
CREATE TYPE public.user_role AS ENUM ('user', 'admin');
|
|
|
|
-- Create profiles table
|
|
CREATE TABLE IF NOT EXISTS public.profiles (
|
|
id uuid PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
|
|
email text,
|
|
phone text,
|
|
role public.user_role DEFAULT 'user'::public.user_role,
|
|
created_at timestamp with time zone DEFAULT now()
|
|
);
|
|
|
|
-- Sync auth.users to profiles trigger
|
|
CREATE OR REPLACE FUNCTION public.handle_new_user()
|
|
RETURNS trigger
|
|
LANGUAGE plpgsql
|
|
SECURITY DEFINER SET search_path = public
|
|
AS $$
|
|
DECLARE
|
|
user_count int;
|
|
BEGIN
|
|
SELECT COUNT(*) INTO user_count FROM profiles;
|
|
INSERT INTO public.profiles (id, email, phone, role)
|
|
VALUES (
|
|
NEW.id,
|
|
NEW.email,
|
|
NEW.phone,
|
|
CASE WHEN user_count = 0 THEN 'admin'::public.user_role ELSE 'user'::public.user_role END
|
|
);
|
|
RETURN NEW;
|
|
END;
|
|
$$;
|
|
|
|
DROP TRIGGER IF EXISTS on_auth_user_confirmed ON auth.users;
|
|
CREATE TRIGGER on_auth_user_confirmed
|
|
AFTER UPDATE ON auth.users
|
|
FOR EACH ROW
|
|
WHEN (OLD.confirmed_at IS NULL AND NEW.confirmed_at IS NOT NULL)
|
|
EXECUTE FUNCTION handle_new_user();
|
|
|
|
-- Create documents table
|
|
CREATE TABLE IF NOT EXISTS public.documents (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
owner_id uuid REFERENCES public.profiles(id) ON DELETE CASCADE,
|
|
name text NOT NULL,
|
|
type text NOT NULL,
|
|
storage_path text NOT NULL,
|
|
content_text text,
|
|
metadata jsonb DEFAULT '{}'::jsonb,
|
|
status text DEFAULT 'pending',
|
|
created_at timestamp with time zone DEFAULT now()
|
|
);
|
|
|
|
-- Create extracted_entities table
|
|
CREATE TABLE IF NOT EXISTS public.extracted_entities (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
document_id uuid REFERENCES public.documents(id) ON DELETE CASCADE,
|
|
entity_type text NOT NULL,
|
|
entity_value text NOT NULL,
|
|
confidence float DEFAULT 1.0,
|
|
created_at timestamp with time zone DEFAULT now()
|
|
);
|
|
|
|
-- Create templates table
|
|
CREATE TABLE IF NOT EXISTS public.templates (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
owner_id uuid REFERENCES public.profiles(id) ON DELETE CASCADE,
|
|
name text NOT NULL,
|
|
type text NOT NULL,
|
|
storage_path text NOT NULL,
|
|
created_at timestamp with time zone DEFAULT now()
|
|
);
|
|
|
|
-- Create fill_tasks table
|
|
CREATE TABLE IF NOT EXISTS public.fill_tasks (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
owner_id uuid REFERENCES public.profiles(id) ON DELETE CASCADE,
|
|
template_id uuid REFERENCES public.templates(id) ON DELETE CASCADE,
|
|
document_ids uuid[] NOT NULL,
|
|
status text DEFAULT 'pending',
|
|
result_path text,
|
|
error_message text,
|
|
created_at timestamp with time zone DEFAULT now()
|
|
);
|
|
|
|
-- Enable RLS
|
|
ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE public.documents ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE public.extracted_entities ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE public.templates ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE public.fill_tasks ENABLE ROW LEVEL SECURITY;
|
|
|
|
-- Helper function is_admin
|
|
CREATE OR REPLACE FUNCTION is_admin(uid uuid)
|
|
RETURNS boolean LANGUAGE sql SECURITY DEFINER AS $$
|
|
SELECT EXISTS (
|
|
SELECT 1 FROM profiles p
|
|
WHERE p.id = uid AND p.role = 'admin'::user_role
|
|
);
|
|
$$;
|
|
|
|
-- Policies
|
|
-- Profiles: Admin full access, users view/update own (except role)
|
|
CREATE POLICY "Admins have full access to profiles" ON profiles
|
|
FOR ALL TO authenticated USING (is_admin(auth.uid()));
|
|
CREATE POLICY "Users can view their own profile" ON profiles
|
|
FOR SELECT TO authenticated USING (auth.uid() = id);
|
|
CREATE POLICY "Users can update their own profile" ON profiles
|
|
FOR UPDATE TO authenticated USING (auth.uid() = id)
|
|
WITH CHECK (role IS NOT DISTINCT FROM (SELECT role FROM profiles WHERE id = auth.uid()));
|
|
|
|
-- Documents: owner full access, admin view
|
|
CREATE POLICY "Users can manage own documents" ON documents
|
|
FOR ALL TO authenticated USING (auth.uid() = owner_id);
|
|
CREATE POLICY "Admins can view all documents" ON documents
|
|
FOR SELECT TO authenticated USING (is_admin(auth.uid()));
|
|
|
|
-- Extracted Entities: owner (via document) full access
|
|
CREATE POLICY "Users can manage entities of own docs" ON extracted_entities
|
|
FOR ALL TO authenticated USING (
|
|
EXISTS (SELECT 1 FROM documents d WHERE d.id = document_id AND d.owner_id = auth.uid())
|
|
);
|
|
|
|
-- Templates: owner full access
|
|
CREATE POLICY "Users can manage own templates" ON templates
|
|
FOR ALL TO authenticated USING (auth.uid() = owner_id);
|
|
|
|
-- Fill Tasks: owner full access
|
|
CREATE POLICY "Users can manage own tasks" ON fill_tasks
|
|
FOR ALL TO authenticated USING (auth.uid() = owner_id);
|
|
|
|
-- Create storage buckets
|
|
INSERT INTO storage.buckets (id, name, public) VALUES ('document_storage', 'document_storage', false) ON CONFLICT (id) DO NOTHING;
|
|
|
|
-- Storage policies
|
|
CREATE POLICY "Users can upload docs" ON storage.objects
|
|
FOR INSERT TO authenticated WITH CHECK (bucket_id = 'document_storage');
|
|
CREATE POLICY "Users can view own docs" ON storage.objects
|
|
FOR SELECT TO authenticated USING (bucket_id = 'document_storage' AND (auth.uid()::text = (storage.foldername(name))[1]));
|
|
CREATE POLICY "Users can delete own docs" ON storage.objects
|
|
FOR DELETE TO authenticated USING (bucket_id = 'document_storage' AND (auth.uid()::text = (storage.foldername(name))[1]));
|